Quantsys — Documento técnico de plataforma

Integridad del dato en un sistema de inversión cuantitativo

Un backtest no falla con una excepción: devuelve un número plausible y falso. Este documento trata de las comprobaciones que separan un histórico en el que se puede confiar de uno que produce señales espectaculares e inexistentes.

Alcance · integridad y coherencia del dato Almacenamiento · TimescaleDB + Parquet Puerta · lint · formato · tipos estrictos · pruebas Pruebas · 50 módulos
01

Qué cubre este documento

Quantsys es un sistema de inversión cuantitativo: ingesta, research, riesgo, ejecución y contabilidad. Este documento trata solo de la capa de datos — cómo se verifica que el histórico sobre el que se decide es real.

Alcance, dicho de frente

No se describen estrategias, señales ni parámetros de research: eso no es materia de un documento público. Lo que sí se describe es la parte que no da ventaja competitiva pero decide si el resto sirve de algo, y que suele estar ausente de los sistemas que fallan.

El principio de construcción del proyecto explica la elección: no se construyen los subsistemas en capas horizontales, sino una rebanada vertical delgada que los atraviesa todos y funciona de punta a punta. La razón está escrita en el propio repositorio y es la tesis de este documento:

El supuesto de partida
Los sistemas automáticos no mueren por mala estrategia: mueren por fallas operativas.
Y el modo de fallo más caro no es la caída ruidosa, sino el dato que pasa todas las validaciones y produce un resultado convincente. Todo lo que sigue son defensas contra eso.
02

Los invariantes

Seis reglas que el sistema no negocia. Las tres primeras son operativas; las tres siguientes son las que hacen honesto un backtest.

#InvarianteContra qué protege
1Ninguna clave de API con permiso de retiro; lista blanca de IP siempreQue una clave filtrada pueda mover fondos
2Los límites de riesgo viven en código, no en la disciplina del operadorEl momento en que el operador decide saltarse su propia regla
3Todo dato histórico es point-in-time, o se marca como no apto para backtestDecidir con información que en esa fecha no existía
4El universo incluye los símbolos delistadosEl sesgo de supervivencia, que infla todo backtest de cripto
5El mismo código de estrategia en backtest y en producciónQue lo probado y lo ejecutado sean dos programas distintos
6Todo experimento queda registrado antes de mirar el resultadoElegir la hipótesis después de ver cuál ganó

El cuarto es el que más trabajo cuesta sostener, y el resto de este documento es en buena parte su consecuencia: incluir los delistados obliga a lidiar con datos que terminan mal, y ahí es donde aparecen todos los casos difíciles.

03

Coherencia del funding

En futuros perpetuos, el funding es una prima que se cobra o se paga periódicamente. El invariante 3 obliga a cruzarlo con las barras de precio: un evento de funding sin una barra que lo respalde es carry sin precio.

La primera versión trataba eso como un solo fenómeno, y hacía las dos cosas mal a la vez: dejaba pasar una lectura alarmante y rechazaba datos buenos. La corrección fue distinguir dónde cae el evento.

ClaseDónde caeConsecuencia en un backtestVeredicto
De cabezaAntes de la primera barraNinguna: sin precio no hay entrada posible, así que nadie puede devengar ese carryrevisar
IntercaladoEn un hueco interiorPuede haber posición abierta, y falta dato en mitad de la vida del símboloroto
De colaDespués de la última barraLa posición puede estar abierta y se sigue devengando sobre algo que no se puede marcar ni cerrarroto
La asimetría no es una concesión: son dos hechos medidos

Un símbolo delistado en 2024 acumula 156 eventos de funding y el 26 % de su carry después de su última barra. Una estrategia de carry cobraría cincuenta y dos días de prima sobre un instrumento sin precio con el que marcar la posición ni con el que cerrarla. El backtest no falla, no avisa, y sale inflado justo sobre los delistados — que son la población de la que depende su honestidad.

En el otro extremo, hay símbolos cuyo primer funding llega minutos u once días antes que su primera barra. No falta nada: el fichero de funding del archivo empieza antes que el de precios. Nadie puede cobrar ese carry porque nadie puede haber entrado todavía.

Dos detalles que conviene no re-descubrir

La clasificación la manda el precio, no el universo. Un evento anterior a la primera barra es de cabeza aunque además caiga fuera del tramo en que el universo dice que el símbolo cotizaba: las dos cosas afirman lo mismo. Pero un evento que sí tiene barra y cae fuera del universo es intercalado, porque hay precio y hay funding en una fecha en la que el universo point-in-time sostiene que el símbolo no existía, y esa contradicción no la explica la asimetría del archivo.

Un símbolo sin una sola barra no tiene nada «de cabeza». Sin línea de precios no hay primera barra de la que estar antes: el símbolo entero es un hueco. Es el caso de un funding descargado cuyas barras nunca llegaron, y llamar inocua a esa ausencia sería exactamente el fallo silencioso que el invariante persigue.

04

Rechazar un dato bueno cuesta

Con un único criterio severo, la puerta de carga rechazó 7 de 12 símbolos por el caso inocuo. Ese resultado es lo que motivó la distinción anterior, y la lección generaliza.

Una puerta que se salta por costumbre deja de ser una puerta

El criterio del proyecto no cambió: un fallo silencioso que produce datos plausibles pero incorrectos sigue siendo peor que una excepción, y lo cobrable sigue en rojo y sigue sin entrar en la base de datos. Lo que se corrigió fue la puntería.

Porque rechazar un dato bueno también tiene coste, y no es el que parece: empuja al operador a usar la opción de forzado. Y una comprobación que se salta por rutina ya no protege de nada — solo da la sensación de que sí.

05

La discontinuidad de dentro

El caso más instructivo del sistema no es un dato corrupto: son dos archivos perfectamente válidos que juntos mienten.

Medido sobre el lago real
última barra 2023-01-31 23:00 cierre 85,59 siguiente barra 2023-02-22 14:00 apertura 1,411 ───────────────────────────────────────────────────── salto 60,66× retorno −98,35 % en una sola barra
Y no es un dato roto: cada barra pasa la validación por separado, la lectura las devuelve limpias con su mes y su zona correctos, ninguna consulta se queja y ningún verificador se enciende.

Lo que ocurrió entre esas dos fechas fue una redenominación: el instrumento cambió de unidades. El precio no cayó un 98 %, pero cualquier cálculo de retorno que no lo sepa dirá que sí.

Por qué este es el peor caso posible

Para una estrategia de momentum o de reversión, ese −98 % no es ruido: es la señal más fuerte de todo el histórico de ese símbolo, y es falsa. Es la variante extrema del criterio del proyecto, porque aquí el dato malo ni siquiera es plausible — es espectacular, y por tanto es exactamente el que una búsqueda de señales va a encontrar primero.

06

Por qué no se reescriben los precios

Es literalmente el problema de los splits de acciones, y la industria lo resolvió hace décadas: no se tocan los precios históricos; se mantiene una tabla de factores y se aplica al calcular retornos. Aquí igual, por tres razones.

RazónQué se perdería al reescribir
El precio nominal es un hechoEse día el instrumento cotizó a ese valor en las unidades de entonces. Reescribirlo sería guardar un precio al que nadie operó nunca
Reescribir destruye la auditoríaLo que permite demostrar que un fichero venía recortado de origen es que coincide byte a byte con su checksum publicado. Un archivo reescrito ya no se puede contrastar con la fuente
El factor puede cambiar de opiniónUn ajuste mal inferido se corrige editando una fila. Un precio reescrito se corrige volviendo a descargar decenas de miles de barras — o no se corrige, porque nadie se entera

Los precios se quedan como están. Lo que se ajusta es lo derivado.

07

Cinco tipos de salto

La detección recorre la serie comparando el cierre de cada barra con la apertura de la siguiente que existe, y clasifica lo que encuentra. No basta con dos categorías.

La inferencia se apoya en dos hechos medibles en la propia serie —el volumen de la barra anterior y si hay barras ausentes en medio— y en un tercero que no está en la serie: si en esa misma hora paró alguien más.

Un umbral compartido, a propósito

La detección usa el mismo umbral que la costura del rellenado de huecos. Compartirlo no es economía de código: si divergieran, el rellenado podría fusionar un salto que la tabla de ajustes considera una redenominación, y las dos comprobaciones se contradirían sobre el mismo fichero.

08

Sincronía, no magnitud

Esta es la decisión de diseño del apartado, y conviene dejar escrito por qué la alternativa obvia no sirve.

Por qué la magnitud no sirve

Sobre los datos reales, el racimo de una parada del exchange llega a 1,037× y la acción corporativa más pequeña está en 4,007×. Cualquier corte intermedio separa las dos poblaciones, y por eso la tentación es fuerte.

Pero eso no separa dos fenómenos

Separa los números que esos dos fenómenos tuvieron esta vez. Una parada del exchange que caiga en una hora de liquidaciones —un hueco del 10 %, perfectamente posible en cripto— se clasificaría como acción corporativa y le borraría a la estrategia un retorno que sí ocurrió, en decenas de símbolos a la vez y sin que nada se queje. Y al revés: una redenominación pequeña se leería como parada.

El umbral no tiene forma de saber cuál de los dos está mirando, porque la magnitud no es la diferencia entre ellos.

Por qué la sincronía sí

La diferencia es de alcance, y el alcance es observable
Acción corporativa → un hecho sobre UN instrumento le cambia las unidades, no toca a los demás Parada del exchange → un hecho sobre LA SEDE para a todos a la vez, no cambia unidades
Medir el alcance responde exactamente a la pregunta que hay que responder —«¿existió este retorno?»— y lo hace igual de bien con un hueco del 2 % que con uno del 40 %, porque no mira el tamaño del salto sino su causa.

Y se mide sobre las paradas, no sobre los saltos

Un símbolo que se detuvo y reabrió plano prueba la caída del exchange exactamente igual que uno que reabrió un 3 % arriba. Contarlo solo si se movió haría que el diagnóstico dependiera de la volatilidad de esa hora, que es volver a meter la magnitud por la puerta de atrás.

Por eso lo que se agrupa por instante es el censo de horas sin operar de cada símbolo, no sus saltos.

09

Arquitectura y verificación

Ocho decisiones de arquitectura tomadas y escritas, y dieciocho subsistemas clasificados entre construir y adoptar.

DecisiónElecciónCriterio
TopologíaMonolito modular en contenedoresUn operador solo no sostiene microservicios
AislamientoTotal respecto al resto de sistemas: usuario, contenedores y base separadosUn fallo en un lado no puede alcanzar al otro
Multi-mercadoDesde el primer día, con abstracción de instrumento y de brókerAñadirla después obliga a reescribir la ejecución
Interfaz webAl finalUn panel de métricas cubre lo necesario durante meses
Agentes de IADespués de que el pipeline manual funcioneAutomatizar un proceso que aún no es correcto multiplica el error

De los dieciocho subsistemas, la mayoría se adoptan. Se construyen solo cinco, y no por gusto: integridad point-in-time, framework de validación, motor de riesgo, contabilidad y gobernanza. Son justamente los que ninguna librería puede resolver, porque dependen de decisiones que solo tienen sentido dentro de este sistema.

Una sola puerta, y la misma en los dos sitios

La verificación
lint · formato · tipos en modo estricto · pruebas
Los cuatro verificadores viven en el mismo archivo que usa el desarrollador y en el de integración continua, y son literalmente los mismos: si divergieran, el servidor rechazaría lo que en local pasa, y el equipo aprendería a desconfiar de la puerta en vez de a usarla.

Se ejecutan también antes de cada commit, de modo que un cambio que rompe los tipos no llega a existir como historia. Los 50 módulos de prueba se concentran en la capa de datos, que es donde un error no se nota.