Una prueba de backtest útil no tiene nada que ver con encontrar un parámetro mejor: detén el proceso a mitad de camino, restáuralo y termina la misma reproducción. Con entradas idénticas y un simulador de ejecución controlado, sus decisiones, órdenes y patrimonio deberían coincidir con los de una ejecución ininterrumpida.
Si no coinciden, has encontrado un problema de gestión del estado. La estrategia depende de algo que no guardaste o que no pudiste reconstruir. Esa dependencia importa cuando se reanudan ejecuciones de investigación, se sustituyen workers o un servicio de paper trading despliega código nuevo.
Me gusta esta prueba porque la respuesta esperada es inusualmente clara. No hay discusión sobre si el mercado cambió. Ambas ejecuciones reciben el mismo mercado.
Aquí tienes 3 formas incorrectas de reiniciar. Las cifras son ilustrativas; cada fallo puede ocurrir en un sistema por lo demás determinista.
1. Cargar unas pocas barras y dar por listos los indicadores
Supongamos que una estrategia usa una media móvil exponencial de 100 periodos. Su actualización es:
alpha = 2 / 101
ema_next = alpha * close + (1 - alpha) * ema_previous
El proceso ininterrumpido arrastra el valor acumulado de la EMA. El proceso reiniciado obtiene 100 barras, inicializa la EMA con el primer cierre y supone que un indicador de 100 periodos necesita 100 observaciones.
Esa suposición confunde el parámetro de suavizado del indicador con una ventana de memoria finita. Una EMA conserva una contribución decreciente de su estado inicial. Si 2 versiones empiezan con una diferencia de 10 unidades de precio en la EMA, los mismos precios posteriores reducen esa diferencia así:
| Actualizaciones desde la inicialización | Diferencia restante | Fracción del error inicial |
|---|---|---|
| 100 | 1.353 | 13.53% |
| 250 | 0.0674 | 0.674% |
| 500 | 0.000454 | 0.00454% |
El cálculo es 10 * (99 / 101)^k. Obtener 100 barras te da solo 99 actualizaciones si la primera observación se usa como semilla.
El problema suele aparecer cerca de un umbral de decisión. Una ejecución ve el precio por encima de la EMA; la otra, por debajo. Una pequeña diferencia numérica genera una operación adicional. A partir de ahí, también pueden divergir los periodos de espera, el efectivo disponible y las decisiones posteriores.
Guarda el estado recursivo del indicador, su estado de inicialización y el último evento procesado. Como alternativa, vuelve a reproducir desde un estado inicial conocido. Un calentamiento más largo puede dar una aproximación aceptable, pero elige su duración con una tolerancia de error explícita y comprueba si esa tolerancia puede cambiar las decisiones. «5 veces el periodo» es una convención, no una demostración.
Y los indicadores no son todo el historial. Un percentil móvil necesita su ventana. Un modelo online puede necesitar el estado de su optimizador. Una regla que espera 3 barras después de una pérdida tiene que recordar la pérdida y el contador.
2. Guardar posiciones y olvidarse de las órdenes en curso
Tu posición objetivo es de 10 unidades. Se ha ejecutado parcialmente una orden de compra de 10: se han llenado 4 y quedan 6 pendientes. Guardas la posición como 4, reinicias y envías otra orden de compra por las 6 que faltan.
Si se ejecutan tanto el resto de la orden original como la orden de reemplazo, acabarás con 16 unidades.
La versión de este error en backtesting suele pasar desapercibida porque al reiniciar se borran silenciosamente las órdenes activas del motor de ejecución. En paper trading, el simulador o el servicio externo puede conservarlas. Entonces, el mismo código de recuperación produce una exposición distinta según qué componente haya sobrevivido.
| Al reiniciar | Estado real | Lo que detecta la recuperación basada solo en posiciones |
|---|---|---|
| Posición objetivo | 10 | 10 |
| Posición ejecutada | 4 | 4 |
| Cantidad de compra pendiente | 6 | 0 |
| Cantidad adicional necesaria | 0 | 6 |
El problema se manifiesta como una ráfaga inexplicable de órdenes justo después de la recuperación. A veces duplica la exposición. Otras veces cierra una posición cuya orden de protección sigue activa, lo que permite que esa orden abra una posición nueva más adelante.
Un checkpoint necesita la identidad y el estado del ciclo de vida de las órdenes, además de las posiciones. Antes de generar nuevas acciones, la recuperación debe conciliar esos registros con el sistema de ejecución. Si se desconoce el resultado de una orden, hay que investigarlo; dar por hecho que «no se guardó ninguna confirmación» significa «nunca se envió» es como nacen las órdenes duplicadas.
Los identificadores de orden de cliente estables te ayudan a averiguar qué ocurrió. Solo evitan duplicados si el sistema receptor aplica de verdad las reglas de unicidad o idempotencia necesarias. Guarda también los identificadores de las ejecuciones procesadas, para que volver a reproducir un fill no incremente la posición 2 veces.
Le tengo cariño a la aburrida pantalla del estado de las órdenes. El día del reinicio, sus pequeñas filas se convierten de pronto en la interfaz más interesante del edificio.
3. Restaurar la posición y empezar un libro de pérdidas y ganancias nuevo
Considera un ejemplo de spot sin apalancamiento ni comisiones. Empieza con $10,000 en efectivo, compra 10 unidades a $100 y guarda un checkpoint cuando el mark alcanza $110.
El estado correcto es $9,000 en efectivo más una posición valorada en $1,100: un patrimonio de $10,100. Si la recuperación restaura las 10 unidades, pero reinicia el efectivo a los $10,000 iniciales, informa $11,100. Has creado $1,000 al reiniciar un proceso.
Otras variantes son menos espectaculares. La recuperación conserva el patrimonio, pero reinicia el precio de entrada a $110. El patrimonio total puede seguir siendo correcto mientras cambia la atribución entre pérdidas y ganancias realizadas y no realizadas. Si un stop o una condición de salida hace referencia al precio de entrada, ese atajo contable cambia ahora el comportamiento de trading.
O quizá el sistema olvida el máximo anterior del patrimonio. Supongamos que alcanzó un pico de $10,600 antes de caer a $10,100. La caída desde el máximo es de aproximadamente 4.72%. Si al recuperarse se reinicia el máximo histórico, la estrategia cree de pronto que la caída es del 0%. Cualquier control de riesgo basado en la caída acaba de recibir un reinicio no autorizado.
Así que el problema puede ser una discontinuidad en el patrimonio, una caída desde máximos sospechosamente mejor o una regla de riesgo que deja de activarse después de los despliegues. Conserva el libro contable y el estado de la estrategia que dependa de la contabilidad: movimientos de efectivo, posiciones, el coste base aplicable, cargos acumulados y la memoria de los controles de riesgo. Concilia el patrimonio restaurado con el libro contable usando la misma marca temporal de valoración.
Un checkpoint necesita un límite coherente. Si guardas el efectivo después de un fill y la cantidad de la posición antes de ese fill, obtienes un estado que nunca existió. Confirma el estado relacionado en conjunto o registra una secuencia de eventos duradera a partir de la cual pueda reconstruirse. Guarda el cursor de eventos junto con ese estado para que la recuperación no omita el fill ni lo aplique 2 veces.
En el entorno de investigación, mantendría una prueba que ejecuta primero una reproducción de referencia ininterrumpida y luego reinicia una segunda ejecución en puntos deliberadamente complicados: durante la inicialización de los indicadores, después de un fill parcial y mientras está activo un límite de riesgo. Usa el mismo orden de eventos y conserva cualquier estado aleatorio del simulador. Compara la primera decisión tras la recuperación, los registros de órdenes y fills y la trayectoria del patrimonio. Un saldo final coincidente puede ocultar errores que se compensan entre sí.
Si ocurre un fallo después del envío de una orden, pero antes de recibir la confirmación, el entorno de pruebas también tiene que conservar por separado el estado del servicio de ejecución y el del proceso de la estrategia. De lo contrario, elimina precisamente la incertidumbre que intentas probar.
La especificación de una estrategia incluye lo que recuerda. Haz que esa memoria sea lo bastante explícita como para poder detener el proceso a mitad de una reproducción y mostrar exactamente cómo vuelve a funcionar.
← Todos los artículos


