Saltar al contenido
ALX Quant
← Volver a las guías
Etapa 3 de 4ValidarGuía 6 de 8
Avanzado

11 min de lectura

Fuera de muestra: la única prueba que importa

Mil estrategias optimizadas sobre ruido puro sacan un Sharpe medio de 0,61. Fuera de muestra caen a −0,06. Cómo se parte la muestra, qué es el walk-forward y por qué salir bien tampoco demuestra nada.

  • backtesting
  • método
  • validación
  • estadística

Optimizar es buscar, entre muchas versiones de una idea, la que mejor funcionó. El problema es que siempre encuentras una. Aunque no haya nada.

Esta guía demuestra eso con un experimento que puedes reproducir en diez minutos, y después explica las tres pruebas que separan una ventaja de una coincidencia.

El experimento: optimizar sobre datos sin información

Generé 1.000 series de precios de puro ruido. Movimiento aleatorio, 500 días cada una, volatilidad diaria del 1,2 % y deriva cero. Por construcción no hay nada que encontrar: no hay tendencia, no hay memoria, no hay patrón.

Sobre cada serie probé 38 combinaciones de dos medias móviles (rápida entre 5 y 30, lenta entre 20 y 200) con la regla más común del mundo: largo si la rápida está por encima de la lenta, fuera si no. Y me quedé con la combinación que mejor Sharpe daba en cada serie, que es exactamente lo que hace todo el mundo.

Sin optimizar
Sharpe medio de las 38 combinaciones: −0,118
Quedándote con la mejor
Sharpe medio: 0,607
Superan Sharpe 1,0
230 de las 1.000
La mejor de todas
Sharpe 2,36 y +96,2 % en 500 días

Ahí está todo. Los datos no tienen información, la media de las combinaciones es negativa como debe ser, y en cuanto te quedas con la mejor de cada serie aparece un Sharpe de 0,607 de la nada. Elegir la mejor de 38 es lo que fabrica el resultado, no la estrategia.

Sharpe de la mejor combinación de cada una de las 1.000 series de ruido

Sharpe de la mejor combinación de cada una de las 1.000 series de ruido. −1,0/−0,5: 9. −0,5/0,0: 99. 0,0/0,5: 328. 0,5/1,0: 334. 1,0/1,5: 181. 1,5/2,0: 42. 2,0/2,5: 7.−1,0/−0,5−0,5/0,00,0/0,50,5/1,01,0/1,51,5/2,02,0/2,5999328334181427
Cuántas series caen en cada tramo de Sharpe. En cian, las 230 que superan 1,0. Recuerda que debajo de todas ellas no hay más que números aleatorios.

Si alguien te enseña un backtest con Sharpe 1,3 y no te dice cuántas variantes probó antes, este gráfico es la respuesta: el 23 % del puro ruido llega ahí.

Qué pasa cuando esas estrategias ven datos nuevos

Cada serie tenía 500 días más que la optimización nunca vio. Apliqué la combinación ganadora de cada serie a esos 500 días.

Sharpe dentro de muestra
0,607 de media
Sharpe fuera de muestra
−0,061 de media
Acaban en positivo
43,6 %, menos que echándolo a cara o cruz
Superan Sharpe 0
46,3 %

La ventaja desaparece entera. No se reduce: desaparece, y se queda alrededor de cero como corresponde a un sistema que opera sobre ruido pagando comisión.

Esta es la mejor de las mil, la que sacó Sharpe 2,36. La misma regla, los mismos parámetros, en los 500 días que se usaron para elegirla y en los 500 siguientes:

La campeona de 1.000: dentro y fuera de muestra

La campeona de 1.000: dentro y fuera de muestra. los 500 días que la eligieron: empieza en 1,00 y termina en 1,96. los 500 días siguientes: empieza en 1,00 y termina en 1,05.0,501,001,502,00
  • los 500 días que la eligieron
  • los 500 días siguientes
Serie 989, medias de 15 y 75. Dentro de muestra: +96,2 % con solo 2 operaciones. Fuera: +5,0 % con 7 operaciones y un Sharpe de 0,28. La curva de arriba es la que se publicaría.

Fíjate en un detalle que lo explica todo: la curva de arriba son dos operaciones. El +96,2 % no es una estrategia funcionando, son dos aciertos de suerte en una serie aleatoria que subió, elegidos entre 38 intentos y 1.000 series. Cualquier informe con dos operaciones debería ir a la basura sin mirarlo, y sin embargo su curva es preciosa.

# El experimento entero, en lo esencial
import numpy as np, pandas as pd
 
rng = np.random.default_rng(20260922)
mejores = []
for _ in range(1000):
    r = rng.normal(0, 0.012, 1000)               # ruido: deriva cero
    p = pd.Series(100 * np.exp(np.cumsum(r)))
    dentro, fuera = p[:500], p[500:].reset_index(drop=True)
 
    sharpes = {}
    for rap in (5, 10, 15, 20, 25, 30):
        for len_ in (20, 30, 50, 75, 100, 150, 200):
            if rap >= len_:
                continue
            pos = (dentro.rolling(rap).mean() > dentro.rolling(len_).mean()).astype(int)
            ret = pos.shift(1) * dentro.pct_change()
            sharpes[(rap, len_)] = ret.mean() / ret.std() * np.sqrt(252)
 
    mejores.append(max(sharpes.values()))         # nos quedamos con la mejor
 
print(f"Sharpe medio del ganador: {np.mean(mejores):.3f}")   # ~0,61 sobre ruido

Las tres pruebas, por orden de lo que cuestan

Partir la muestra

Lo mínimo. Reservas el último tramo de los datos, construyes y optimizas solo con el primero, y cuando ya no vas a tocar nada más, miras el tramo reservado una vez.

Yo parto en 70/30. En el estudio del Silver Bullet el corte cayó en el 19 de septiembre de 2024: 390 operaciones para construir, 134 para mirar.

Silver Bullet · profit factor dentro y fuera de muestra

Silver Bullet · profit factor dentro y fuera de muestra. dentro (390 ops): 1,08×. fuera (134 ops): 1,55×.dentro (390 ops)fuera (134 ops)por debajo, pierde1,08×1,55×
Corte en 2024-09-19. El agregado de las 524 operaciones es 1,22.

La regla dura: el tramo reservado se mira una sola vez. Si lo miras, no te gusta, cambias algo y vuelves a mirarlo, ya lo has convertido en datos de entrenamiento y has perdido la prueba para siempre.

Walk-forward

Partir una vez usa los últimos años para validar y nunca para construir, y deja la duda de si el resultado depende del corte concreto.

El walk-forward lo resuelve: optimizas en una ventana, aplicas en la siguiente, desplazas y repites. Optimizas en 2020-2021 y operas 2022, luego en 2021-2022 y operas 2023, y así. Al final tienes una curva compuesta solo de tramos que ninguna optimización vio.

Es más caro de calcular y es lo más parecido a operar de verdad, porque reoptimizar cada cierto tiempo es lo que harías.

Monte Carlo sobre tus propias operaciones

Las dos anteriores prueban contra el tiempo. Esta prueba contra el orden.

Coges tus resultados reales, los barajas miles de veces, y miras la distribución de lo que podría haber salido. Sirve para una pregunta muy concreta: cuál es el peor drawdown que esta estrategia puede darte, no el que te dio. Casi siempre es bastante peor que el histórico.

import numpy as np
 
def peor_caida(resultados):
    curva = np.cumsum(resultados)
    return (curva - np.maximum.accumulate(curva)).min()
 
rng = np.random.default_rng(0)
simuladas = [peor_caida(rng.permutation(resultados)) for _ in range(10_000)]
 
print("Histórica:  ", peor_caida(resultados))
print("Mediana:    ", np.median(simuladas))
print("Percentil 5:", np.percentile(simuladas, 5))   # lo que deberias esperar

Lo que fuera de muestra no demuestra

Pasar estas tres pruebas no significa que la estrategia funcione. Significa que todavía no la has descartado, que es una afirmación mucho más pequeña.

Tres motivos concretos.

El tramo reservado se gasta. Es de un solo uso por proyecto. Si pruebas veinte estrategias contra el mismo tramo de 2024-2026, ese tramo ya está optimizado igual que el otro: alguna de las veinte pasará por azar.

Fuera de muestra sigue siendo el pasado. Los últimos dos años son datos que la optimización no vio, pero tú sí los viviste. Sabías que el Nasdaq subió. Esa información se te cuela en las decisiones aunque no quieras.

Una muestra pequeña no concluye nada. Ciento treinta y cuatro operaciones no distinguen un profit factor de 1,55 de uno de 1,0. Los intervalos son anchísimos y casi nadie los publica.

Lo que sí hace fuera de muestra es matar cosas. La mayoría de las ideas mueren ahí, y ese es su trabajo: es un filtro, no un certificado.

Si algo sobrevive, todavía queda la prueba que se lleva a la mitad de lo que queda, que es sumarle lo que cuesta operarlo.

Contenido educativo. Nada de lo que leas aquí es una recomendación de inversión.

Guías nuevas, cuando las haya

Un correo cuando publico algo. Ni resúmenes de mercado ni ofertas. Te puedes borrar con un clic desde cualquier envío.