Quant500 Academy

1. Los cinco sitios por donde falla un sistema de trading

Nivel 11: Cuando falla

Un sistema de trading nunca se rompe por donde estabas mirando. Pasas semanas afinando la señal y el día que salta la alarma resulta que el disco estaba lleno o que el proveedor mandó un precio de cero. Este módulo es el mapa de los cinco sitios por donde falla de verdad un sistema en producción, ordenados por frecuencia real: de cada uno, cómo se detecta y cuál es la respuesta segura por defecto.

El inventario, ordenado por frecuencia real

En un sistema con un año de operativa diaria, el reparto de sustos se parece a esto:

Atravesando las cinco hay una única regla: ante la duda, no operar. Un sistema parado se pierde una oportunidad; uno que opera con información falsa pierde dinero y crea posiciones que luego hay que deshacer. La asimetría manda.

Capa 1: los datos

Fallan de cuatro maneras, y no están ordenadas por gravedad como esperarías:

Las dos primeras se detectan con un reloj; las dos últimas, con un recuento y una banda de cordura.

FRESCURA_MAXIMA_S = 90
BANDA_MAXIMA = 0.20

def barra_utilizable(barra, ultimo_cierre_bueno, ahora):
    if barra is None or barra.get('close') is None:
        return False, 'sin dato'
    edad = ahora - barra['ts']
    if edad > FRESCURA_MAXIMA_S:
        return False, 'dato viejo: %d s' % edad
    precio = barra['close']
    if precio <= 0:
        return False, 'precio no positivo'
    if ultimo_cierre_bueno:
        salto = abs(precio / ultimo_cierre_bueno - 1)
        if salto > BANDA_MAXIMA:
            return False, 'salto del %.1f por ciento' % (salto * 100)
    return True, ''

La función no corrige nada: no interpola, no arrastra el último precio bueno, no rellena el hueco. Solo dice sí o no, y por qué. El umbral tampoco es universal: en una acción grande un 20% intradía es sospechoso, en una cripto no. El número es un parámetro; la comprobación, no.

Capa 2: la red y el bróker

También cuatro formas, de mejor a peor. Caído: rechaza la conexión y lo sabes en un segundo. Rechazando órdenes: contesta que no, con un motivo explícito. Con la sesión caducada: devuelve un 401 y basta renovar. Y lento: la petición no falla, tarda catorce segundos, y cuando llega la respuesta tu decisión ya no vale.

Toda llamada lleva un tiempo máximo de espera explícito: una petición sin límite deja el proceso colgado, la forma más silenciosa de estar caído. Y se vigila la latencia, no solo el porcentaje de errores: un bróker que pasa de 200 milisegundos a seis segundos está roto aunque sus respuestas sean correctas. Cuando cualquiera de las dos se sale de rango, se corta la emisión de órdenes hasta que un ciclo completo salga sano.

Capa 3: tu código, y capa 4: la máquina donde vive

El fallo de código que importa no es el que revienta al arrancar; ese es un regalo, porque no llegas a operar. El que importa es la excepción no capturada a mitad de ejecución: has enviado tres órdenes de las ocho de la rotación y el proceso muere. Eso no es un error, es media cartera.

La infraestructura falla en tres sabores: se llena el disco, a menudo por culpa de los registros que escribes; se agota la memoria y el sistema operativo mata el proceso; o la máquina se reinicia sola. Ninguno avisa con una excepción de Python.

import shutil

MARGEN_DISCO_MB = 500

def preparado_para_operar(ruta_datos):
    libre_mb = shutil.disk_usage(ruta_datos).free / 1e6
    if libre_mb < MARGEN_DISCO_MB:
        return False, 'disco: solo %.0f MB libres' % libre_mb
    return True, ''

def ciclo_protegido(motor, freno):
    try:
        motor.ejecutar_ciclo()
    except Exception as e:
        freno.activar('excepcion no prevista: %r' % e)
        raise

La comprobación de disco se ejecuta antes de cada ciclo, no una sola vez al arrancar. Y el envoltorio hace lo único razonable ante una excepción imprevista: activa el freno, deja constancia y vuelve a lanzarla. Lo que no hace es tragársela y seguir, porque un sistema que continúa tras un error que no entiende opera con un estado que ya no puede justificar.

Capa 5: el humano que toca algo mientras el sistema opera

Alguien cierra una posición desde el móvil porque la veía perder. Alguien edita la configuración a las once y media. Alguien reinicia el proceso para que coja el cambio. Ninguna de las tres es mala en sí; lo malo es que el sistema no se entera y sigue creyendo un estado que ya no existe. La detección es la reconciliación contra el bróker, y la respuesta es una bandera de mantenimiento explícita: antes de tocar nada a mano se para el sistema y se deja escrito que está parado. Así, ante una divergencia, el sistema distingue entre no cuadra por un fallo y no cuadra porque alguien avisó.

Caso real: Knight Capital, 1 de agosto de 2012

Knight Capital Group, uno de los mayores creadores de mercado de Estados Unidos, desplegó código nuevo en sus servidores de enrutamiento. Quedó activo un componente antiguo que llevaba años sin usarse y, en la apertura, empezó a mandar órdenes al mercado en bucle. En menos de una hora, unos 45 minutos, la pérdida rondó los 440 millones de dólares, más que el beneficio de la compañía del año anterior. Knight fue absorbida pocos meses después.

Es el ejemplo canónico porque falló en varias capas a la vez: código viejo que nadie limpió, un despliegue que no llegó a todas las máquinas y avisos automáticos que nadie tenía obligación de escalar. Y el sistema no tenía ningún límite propio capaz de decir esto ya es demasiado. De eso va el módulo siguiente.

El resto de Quant500

Lo que estás aprendiendo, aplicado al índice todos los días:

La cartera del modelo, cada semana en tu correo

Cada sábado, su semana en un minuto: cómo fue, lo mejor y lo peor, y qué directivos compraron con su dinero. Y el día que el modelo se rehace, qué entra y qué sale. Es una simulación, no un consejo. Como mucho un correo a la semana, sin publicidad y sin cuenta; te das de baja con un clic. Lo hace una persona: Pol Gracia.

Cartera modelo con resultados simulados, no una recomendación personalizada. Tu correo se usa solo para esto: privacidad.

Detrás de la Academia y del resto de Quant500 hay una sola persona: Pol Gracia Serrahima.