Volvimos a ejecutar un backtest guardado y obtuvimos una curva de capital distinta. El mismo código de estrategia, el mismo intervalo de fechas y el mismo símbolo. El saldo final difería un 1.8%, y 3 operaciones se habían desplazado 1 barra.
Eso basta para que cueste confiar en un resultado de investigación. Si un compañero, una versión futura de ti o un servicio de paper trading no puede reproducir la ejecución, no puedes saber si un cambio mejoró la estrategia o simplemente modificó el experimento. Esta es la secuencia que seguimos para encontrar el origen de las diferencias.
Día 1: Dejamos por escrito qué significaba «la misma ejecución»
Nuestro primer error fue tratar el archivo de la estrategia como si fuera el experimento. No lo era. La ejecución también dependía de los datos de entrada, la versión del motor, el calendario, los metadatos del instrumento y los ajustes de ejecución. El código solo describía una parte del cálculo.
Antes de cambiar nada, creamos un manifiesto de ejecución. Registramos el commit de la estrategia, los identificadores de las instantáneas de datos, el intervalo de fechas, el mercado, el esquema de comisiones, la fuente de funding, el modelo de ejecución y las versiones del software. También guardamos las órdenes y ejecuciones resultantes, porque una curva de capital por sí sola no muestra dónde empezaron a divergir las dos ejecuciones.
| Artefacto | Qué registrar | Por qué importa |
|---|---|---|
| Datos de mercado | ID de la instantánea, versión del esquema, ajustes | Los proveedores corrigen el historial y revisan las acciones corporativas |
| Ejecución | Nivel de comisiones, serie de funding, ajustes de ejecución e impacto | Los valores predeterminados y los supuestos sobre la cuenta cambian los resultados |
| Entorno de ejecución | Commit del código, versiones del motor y las dependencias | Las bibliotecas pueden cambiar el orden, el redondeo o los indicadores |
| Resultados | Órdenes, ejecuciones, posiciones y métricas | Muestra dónde empiezan a discrepar las ejecuciones |
Día 2: Comparamos las operaciones, no el Sharpe
Las métricas resumidas nos distraían. Ambas ejecuciones tenían un Sharpe casi idéntico, pero los registros de ejecuciones mostraron que la primera discrepancia se produjo en una liquidación de funding. Una ejecución aplicó la tasa a la posición abierta en la marca de tiempo de liquidación; la otra, a la posición posterior al rebalanceo de esa misma marca de tiempo.
El código de la estrategia no había cambiado. Sí había cambiado el orden de los eventos del motor. Una pequeña actualización de versión hizo explícita una secuencia que antes dependía del orden casual de clasificación de dos eventos.
Actualizamos el contrato de ejecución para especificar el orden: aplicar el funding a la posición mantenida hasta la liquidación y, después, procesar las decisiones de la estrategia para esa marca de tiempo. La convención exacta puede variar según el mercado y el motor. El error es dejarla implícita.
Día 3: Un archivo de datos «igual» resultó ser distinto
Tras fijar el orden de los eventos, las discrepancias restantes se concentraban en unas pocas operaciones de renta variable. El proveedor había corregido un ajuste histórico por desdoblamiento de acciones. Nuestro archivo tenía el mismo nombre y el mismo número de filas que antes, por lo que parecía no haber cambiado.
Ahora calculamos la huella digital de cada instantánea de datos inmutable y guardamos junto a ella la política de ajustes. Un hash nos dice si cambiaron los bytes, pero no explica por qué. Por eso, el manifiesto también incluye la fuente, la hora de obtención y la versión de la transformación. En los datos sujetos a revisiones, esos detalles forman parte del resultado.
Un backtest reproducible debe poder responder a la pregunta: «¿Qué versión del pasado vio?»
Día 4: Encontramos un valor predeterminado discreto
La última diferencia se debía a una comisión maker configurada en cero porque faltaba ese campo en la configuración de la estrategia. Un motor más reciente aplicó la comisión predeterminada de la cuenta. Ese único valor predeterminado cambió lo suficiente las operaciones marginales como para explicar la mayor parte de la diferencia en el saldo final.
Hicimos explícitos los ajustes con relevancia económica y configuramos el motor para que imprimiera la configuración resuelta en el registro de la ejecución. Los valores predeterminados son prácticos mientras exploras. Sirven de poco como evidencia al comparar resultados a lo largo del tiempo.
Qué evitaríamos la próxima vez
Pasamos medio día comparando métricas agregadas antes de revisar la primera ejecución discrepante. No empieces por ahí. Ordena ambos registros de eventos por marca de tiempo y compara la primera divergencia; muchas diferencias posteriores se derivan de esa única causa.
También dejaríamos de creer que una imagen de contenedor basta por sí sola para hacer reproducible una ejecución. Fija buena parte del entorno de software, pero no un archivo de datos externo, una tabla de comisiones obtenida en tiempo de ejecución ni el historial revisado de un proveedor.
Cuando cambie un backtest, conserva los manifiestos y registros de ambas ejecuciones y corrige una fuente de diferencias cada vez. El resultado útil no es solo una curva que puedas volver a calcular. Es un registro que explica qué datos y supuestos la produjeron y por qué la siguiente ejecución podría ser distinta.
← Todos los artículos


