⚠ El trading implica un riesgo significativo de pérdida. Todo el contenido de este sitio es únicamente educativo y no constituye asesoramiento financiero.
Lección 13 de 13 · Gratis

Un proyecto completo de notebook de investigación, de los datos al informe, con una lista de comprobación de revisión de código frente al sesgo de anticipación y el sobreajuste

Organiza un estudio completo de una estrategia como un proyecto reproducible, ejecútalo desde los datos brutos hasta un resultado por escrito y revisa el código con una lista de comprobación que detecta los errores que embellecen los backtests.

Autor: Moustafa HijaziContenido revisado: 2026-10-308 min de lecturaCurso de autoaprendizaje

Qué aprenderás

  • Una estructura de carpetas y notebooks que hace que un estudio sea reproducible por ti dentro de seis meses o por cualquier otra persona.
  • El orden de trabajo: datos, limpieza, variables, señales, backtest, métricas, comprobación fuera de muestra, informe.
  • Una lista de comprobación de revisión de código, línea a línea, para el sesgo de anticipación, el sesgo de supervivencia y el sobreajuste.
  • Cómo redactar un resultado con honestidad, incluido lo que se probó y no funcionó.

Explicación

Las pruebas fuera de muestra, las mesetas de parámetros y el coste de realizar muchas pruebas se trataron en Trading sistemático y backtesting (lecciones 6 y 7). Esta lección trata de la ingeniería que hace que esas ideas se sostengan: una estructura de proyecto, un flujo de trabajo fijo y una revisión que otra persona podría repetir.

Estructura del proyecto

  • project/
  • data/raw/ # descargas, nunca editadas a mano
  • data/clean/ # archivos parquet generados por 01_data.ipynb
  • notebooks/ # 01_data, 02_features, 03_backtest, 04_report
  • src/ # indicators.py, engine.py, metrics.py (lecciones 6, 8, 9)
  • tests/ # test_metrics.py, test_engine.py
  • config.yaml # tickers, fechas, parámetros, supuestos de costes
  • requirements.txt # versiones fijadas: pandas==2.x, numpy==…

El código reutilizado vive en src/; los notebooks solo llaman a funciones y muestran resultados. Fija las versiones de los paquetes, porque una actualización de pandas puede cambiar un valor por defecto (cadenas de frecuencia, etiquetas de remuestreo) y mover tus números. Fija la semilla de cualquier elemento aleatorio y anota en el informe la fecha de descarga de los datos.

El flujo de trabajo, en orden

  1. Primero la hipótesis. Una frase de razonamiento económico y las reglas exactas antes de tocar los datos (Trading sistemático y backtesting, lección 2). Fija ahora la división entre dentro de muestra y fuera de muestra y no abras el archivo fuera de muestra hasta el paso 7.
  2. Datos. Descarga, guarda en bruto, documenta la fuente y el método de ajuste (lección 4). Una lista de «los miembros actuales del índice» probada diez años hacia atrás es sesgo de supervivencia.
  3. Limpiar y validar. Huecos, duplicados, precios a cero, zonas horarias; guarda en data/clean/.
  4. Variables y señales con las funciones probadas de src/; cada variable en la barra t usa solo datos hasta t.
  5. Backtest con el motor de la lección 8, incluido el modelo de costes (Trading sistemático y backtesting, lección 8).
  6. Métricas y gráficos (lecciones 9 y 10), sobre el periodo dentro de muestra, con una pequeña cuadrícula de parámetros, no con una búsqueda de la mejor celda.
  7. Ejecución fuera de muestra una sola vez, con los parámetros congelados. Registra el resultado, sea cual sea.
  8. Informe: hipótesis, datos, reglas, costes, ambas tablas de resultados, gráficos, número de variantes probadas, decisión.

Lista de comprobación de revisión de código

Revisa tu notebook como si fuera de otra persona; cada línea es una pregunta de sí o no.

Área Comprobación Solución habitual
Temporización ¿Se desplaza cada señal una barra antes de fijar una posición? pos = signal.shift(1)
Temporización ¿Usan las ventanas móviles center=False (el valor por defecto) y ningún bfill()? Eliminar el centrado y los rellenos hacia atrás
Temporización ¿Se ajustan las normalizaciones (z-scores, rangos, escaladores) solo con datos pasados? Ajustes expansivos o móviles
Datos ¿Se define el universo tal como se conocía en cada fecha? Listas de componentes a fecha histórica o una advertencia honesta
Datos ¿Se usan precios ajustados para las rentabilidades y no ajustados para las distancias de stop cuando corresponde? Conservar ambas columnas
Ejecuciones ¿Se ejecutan los gaps en la apertura, no en el precio del stop o de la señal? min(open, stop)
Costes ¿Se aplican comisiones, spread y deslizamiento por operación y por sentido? Modelo de costes en la configuración
Sobreajuste ¿Cuántos conjuntos de parámetros, reglas y universos se probaron? ¿Figura el recuento en el informe? Registrar cada ejecución
Sobreajuste ¿Dan los parámetros vecinos resultados similares (una meseta)? Mapa de calor de la cuadrícula
Sobreajuste ¿Se tocaron los datos fuera de muestra más de una vez? Si es así, ahora son datos dentro de muestra; busca datos nuevos
Reproducibilidad ¿Reproduce «Restart Kernel and Run All» (reiniciar el kernel y ejecutar todo) los números del informe? Corregir el estado oculto y el orden de las celdas

Regla práctica para la significación: el estadístico t de un ratio de Sharpe anualizado es aproximadamente Sharpe × √años. Un Sharpe de 0.8 durante cuatro años da 1.6, por debajo del umbral habitual de alrededor de 2, y eso antes de tener en cuenta el número de variantes probadas; entre veinte pruebas, cabe esperar que la mejor parezca buena por pura casualidad.

Ejemplo práctico

Ejemplo hipotético, todas las cifras inventadas. Un trader prueba «comprar una ruptura de 100 días, salir con un stop dinámico de 3 × ATR» en 30 acciones de gran capitalización, 2014–2025, con unos costes del 0.1 % por sentido.

  1. División. Dentro de muestra 2014–2021 (8 años), fuera de muestra 2022–2025 (4 años), decidida antes de la primera ejecución.
  2. Cuadrícula. Longitudes de ruptura de 50, 100 y 150 × multiplicadores de ATR de 2, 3 y 4 = 9 variantes, registradas. El Sharpe dentro de muestra va de 0.6 a 1.1, con la mejor celda en (100, 3) = 1.1; sus vecinas dan 0.9 y 1.0, una meseta, así que se mantiene la elección.
  3. Revisión. La lista de comprobación detecta que la columna del ATR usaba rolling(14).mean() sobre el rango verdadero de hoy para el stop de hoy. Se cambia el motor para usar el ATR de la barra anterior (lección 8). El Sharpe dentro de muestra baja de 1.1 a 1.0; el drawdown máximo no cambia, −22 %.
  4. Fuera de muestra. Una sola ejecución: Sharpe 0.6, CAGR del 7.2 %, drawdown máximo del −19 %, 118 operaciones. Significación: 0.6 × √4 = 1.2; la cifra dentro de muestra daba 1.0 × √8 = 2.8. El resultado fuera de muestra es positivo, pero estadísticamente débil por sí solo.
  5. Decisión en el informe. «La regla conservó alrededor del 60 % de su Sharpe dentro de muestra fuera de muestra (0.6 / 1.0), sobre una meseta, con 9 variantes probadas y un error de anticipación corregido. La evidencia es modesta; el siguiente paso son seis meses de trading simulado con el script de la lección 12, no capital real.» Comisiones modeladas; impuestos y financiación excluidos.

Error frecuente

Echar un vistazo al periodo fuera de muestra «solo para comprobar»Cada vistazo a los datos reservados y cada ajuste hecho después los convierte en datos dentro de muestra. Si cambiaste el multiplicador de ATR después de ver 2022–2025, las únicas opciones honestas son informar de que la prueba ya no es fuera de muestra o buscar datos nuevos, por ejemplo otros mercados o los meses transcurridos desde entonces. Anota en el informe la fecha de la división y la fecha en que se ejecutó la prueba fuera de muestra.

Actividad práctica

En papel: revisa este fragmento de notebook y el resultado que obtuvo.

  • df['z'] = (df.close - df.close.mean()) / df.close.std()
  • df['signal'] = df.z < -1.5
  • df['pos'] = df.signal.astype(int)
  • df['ret'] = df.close.pct_change() * df.pos

Sharpe dentro de muestra de 2.4 en 6 años; Sharpe fuera de muestra de −0.3 en 2 años. (a) Identifica dos problemas de sesgo de anticipación en el código. (b) Calcula los estadísticos t aproximados de los ratios de Sharpe dentro y fuera de muestra. (c) Reescribe las cuatro líneas para que estén libres de sesgo de anticipación (describe los cambios si prefieres hacerlo en prosa).

Mostrar solución

(a) Primero, df.close.mean() y df.close.std() usan toda la muestra, incluidos los precios futuros, así que el z-score de cualquier fecha «conoce» la media futura. Segundo, pos no está desplazada: la rentabilidad de hoy se obtiene con una señal calculada a partir del cierre de hoy.

(b) Dentro de muestra: 2.4 × √6 = 5.9; fuera de muestra: −0.3 × √2 = −0.4. El elevado valor dentro de muestra lo produjeron los errores, y por eso se desplomó.

(c) Usa una ventana móvil o expansiva y un desplazamiento:

  • m = df.close.rolling(250).mean(); s = df.close.rolling(250).std()
  • df['z'] = (df.close - m) / s
  • df['signal'] = df.z < -1.5
  • df['pos'] = df.signal.shift(1).fillna(False).astype(int)
  • df['ret'] = df.close.pct_change() * df.pos

Limitaciones: eliminar el sesgo de anticipación hace que la prueba sea honesta, no rentable; la regla corregida sigue necesitando costes, un motor con stops y un periodo fuera de muestra nuevo.

Ideas clave

  • Mantén el código reutilizable en src/ con pruebas, fija las versiones y haz que «ejecutar todo» reproduzca el informe.
  • Fija la hipótesis, las reglas y la división dentro / fuera de muestra antes del primer backtest; ejecuta la prueba fuera de muestra una sola vez.
  • Revisa la temporización, los datos, las ejecuciones, los costes y el número de pruebas con una lista de comprobación; registra cada variante que probaste.
  • Comunica la significación de forma aproximada como Sharpe × √años, el número de variantes y la decisión, incluido «no hay evidencia suficiente».

Siguiente paso

Con esto terminas Python para traders e inversores; el siguiente curso, «Prop firms, cuentas fondeadas y el trading como negocio», analiza las reglas de evaluación, los tipos de drawdown y la parte práctica y fiscal alemana del trading como negocio, donde un proceso probado y automatizado mediante scripts como el que has construido aquí es el punto de partida.

Autoevaluación

Responde a las preguntas y luego comprueba tus respuestas. Puedes volver a intentarlo tantas veces como quieras.

1. Una estrategia muestra un ratio de Sharpe anualizado de 0.9 durante 5 años fuera de muestra. Con la regla aproximada t ≈ Sharpe × √años, ¿cuál es el estadístico t y cómo debe interpretarse?

2. Durante un estudio, un investigador ejecuta el periodo fuera de muestra, ve un resultado débil, cambia la regla de salida y lo vuelve a ejecutar con un resultado mejor. ¿Qué validez tiene el segundo resultado?

3. ¿Qué línea de un notebook introduce más claramente un sesgo de anticipación?

Autoevaluación automática para tu propio repaso. Tus respuestas se comprueban en tu navegador y no las vemos ni evaluamos. No es un examen ni una titulación.

Tu progreso

«Completada» solo significa que marcaste la lección como terminada. No es una prueba de tu comprensión. El progreso y las notas se guardan solo en este navegador y en este dispositivo, no en una cuenta.