ProCat Solutions
Bots de trading y automatización cripto: lo que construimos después de Web3
APIs REST y WebSocket de exchanges, ciclo de vida de órdenes, rate limits, gestión de riesgo y monitorización: lecciones de ingeniería al construir bots cripto.
Este artículo no es asesoramiento de inversión. Nosotros construimos software: la infraestructura de bots de trading y automatización, no estrategias de trading con el dinero de los clientes. La estrategia siempre es del cliente; nuestro trabajo es que el sistema haga exactamente, de forma predecible y segura, lo que la estrategia dicta. A continuación resumimos qué cuestiones de ingeniería surgieron cuando, después de los proyectos de smart contracts y preventas de tokens, nos orientamos hacia la automatización en exchanges.
APIs de exchanges: REST y WebSocket juntos
Casi todos los exchanges centralizados ofrecen dos canales. A través de la API REST enviamos y cancelamos órdenes, consultamos el saldo y el historial. Por el canal WebSocket llegan las cotizaciones, las actualizaciones del order book y los cambios de estado de nuestras propias órdenes. Los dos canales no están sincronizados: ocurre que en el WebSocket ya vemos la ejecución mientras REST todavía muestra el estado “abierta”, o al revés.
Nuestra solución práctica: el WebSocket es la fuente principal del estado, pero por detrás corre una conciliación periódica contra REST, y en caso de discrepancia consideramos como verdad la respuesta REST del exchange. Un heartbeat vigila la conexión WebSocket, y al reconectar pedimos un snapshot completo, no solo la continuación, porque la recuperación de mensajes perdidos funciona de forma distinta en cada exchange y rara vez es fiable.
Ciclo de vida de las órdenes, idempotencia y rate limits
La máquina de estados de una orden parece sencilla (creada, abierta, parcialmente ejecutada, ejecutada, cancelada, rechazada); la dificultad está en las transiciones. ¿Qué pasa si la llamada REST agota el tiempo de espera? No sabemos si la orden entró o no. Por eso asignamos a cada orden un identificador propio del lado del cliente, que la mayoría de los exchanges aceptan, de modo que un reintento no crea un duplicado, sino que devuelve la orden ya existente.
Escribimos todos los cambios de estado en un registro de eventos append-only en PostgreSQL, y el estado actual se deriva de él. Si algo se discute, a partir de la secuencia de eventos se puede reconstruir qué pasó y cuándo.
La otra limitación con la que choca todo bot es el rate limit. Los exchanges aplican rate limits ponderados: una consulta del order book cuesta más que un ping. Superar el límite trae un bloqueo breve, que duele justo cuando habría que cancelar una orden rápidamente. Por eso:
- un token bucket del lado del cliente contabiliza el consumo, leyéndolo de las propias cabeceras del exchange;
- para las operaciones críticas (cancelación, kill switch) reservamos un margen que las consultas no pueden agotar;
- ante un error aplicamos backoff exponencial con jitter, y tras una respuesta 429 respetamos la espera que pide el exchange, no la nuestra.
Gestión de riesgo a nivel de software
Para nosotros la gestión de riesgo no forma parte de la estrategia, sino que es una capa independiente que corre por debajo y que la estrategia no puede eludir:
- límites de posición por activo y en total; por encima del límite el sistema no envía órdenes de apertura;
- pérdida diaria máxima, al alcanzarla el bot solo permite órdenes de cierre;
- kill switch: un único comando que cancela todas las órdenes abiertas y pone el bot en modo pasivo, accesible también por un canal separado (no por la interfaz web del propio bot);
- modo paper trading, donde todo corre igual, solo que las órdenes van a un ejecutor simulado.
El modo paper trading no es una comodidad opcional, sino la base de las pruebas: todo cambio de código corre primero allí durante días, sobre un flujo de datos real.
Monitorización y secretos
Un bot que se detiene en silencio es peor que uno que falla haciendo ruido. Por eso el estado del bot lo señalan métricas de heartbeat (antigüedad de la última actualización de precio, número de órdenes abiertas, estado de la conexión), y se dispara una alerta si alguna sale del rango esperado o si el flujo de datos se atasca, aunque no haya llegado ningún mensaje de error. La alerta sale por varios canales, porque el propio bot no puede saber cuál está vivo en ese momento.
Las claves de API nunca entran en el repositorio ni en la imagen Docker. Llegan desde un almacén de secretos, al arrancar, como variables de entorno; los permisos de las claves son mínimos (trading sí, retiradas no) y están restringidas por IP. La revocabilidad importa más que el cifrado: una clave comprometida hay que poder sustituirla en minutos.
Determinismo y las trampas del backtesting
El backtesting es tentador porque muestra curvas bonitas, pero el resultado solo es tan bueno como las suposiciones. Los errores típicos que podemos tratar desde el lado del software:
- look-ahead: la estrategia usa datos que en ese instante todavía no estaban disponibles, por lo que el motor de backtesting solo sirve datos que según su marca de tiempo “ya han llegado”;
- slippage y comisiones: el ejecutor simulado no ejecuta al precio medio, sino según el order book y con comisiones;
- huecos en los datos: las velas que faltan no se interpolan, sino que se marcan, y son visibles también para la estrategia;
- ejecución no determinista: la misma entrada da siempre el mismo resultado, porque la aleatoriedad se siembra con una semilla y el tiempo se inyecta, no se lee del reloj del sistema.
Esta capa garantiza que el backtest sea reproducible y honesto. No garantiza que la estrategia sea buena. Es importante separar las dos cosas, y durante el desarrollo nosotros asumimos la responsabilidad de la primera.