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.
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:
Los invariantes
Seis reglas que el sistema no negocia. Las tres primeras son operativas; las tres siguientes son las que hacen honesto un backtest.
| # | Invariante | Contra qué protege |
|---|---|---|
| 1 | Ninguna clave de API con permiso de retiro; lista blanca de IP siempre | Que una clave filtrada pueda mover fondos |
| 2 | Los límites de riesgo viven en código, no en la disciplina del operador | El momento en que el operador decide saltarse su propia regla |
| 3 | Todo dato histórico es point-in-time, o se marca como no apto para backtest | Decidir con información que en esa fecha no existía |
| 4 | El universo incluye los símbolos delistados | El sesgo de supervivencia, que infla todo backtest de cripto |
| 5 | El mismo código de estrategia en backtest y en producción | Que lo probado y lo ejecutado sean dos programas distintos |
| 6 | Todo experimento queda registrado antes de mirar el resultado | Elegir 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.
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.
| Clase | Dónde cae | Consecuencia en un backtest | Veredicto |
|---|---|---|---|
| De cabeza | Antes de la primera barra | Ninguna: sin precio no hay entrada posible, así que nadie puede devengar ese carry | revisar |
| Intercalado | En un hueco interior | Puede haber posición abierta, y falta dato en mitad de la vida del símbolo | roto |
| De cola | Después de la última barra | La posición puede estar abierta y se sigue devengando sobre algo que no se puede marcar ni cerrar | roto |
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.
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.
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í.
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.
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í.
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.
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ón | Qué se perdería al reescribir |
|---|---|
| El precio nominal es un hecho | Ese 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ía | Lo 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ón | Un 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.
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.
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.
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.
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í
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.
Arquitectura y verificación
Ocho decisiones de arquitectura tomadas y escritas, y dieciocho subsistemas clasificados entre construir y adoptar.
| Decisión | Elección | Criterio |
|---|---|---|
| Topología | Monolito modular en contenedores | Un operador solo no sostiene microservicios |
| Aislamiento | Total respecto al resto de sistemas: usuario, contenedores y base separados | Un fallo en un lado no puede alcanzar al otro |
| Multi-mercado | Desde el primer día, con abstracción de instrumento y de bróker | Añadirla después obliga a reescribir la ejecución |
| Interfaz web | Al final | Un panel de métricas cubre lo necesario durante meses |
| Agentes de IA | Después de que el pipeline manual funcione | Automatizar 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
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.