Liquidaciones cross-exchange
Cómo se obtiene, normaliza, dimensiona y lee la cinta de liquidaciones: feeds WS, esquema, semántica del lado, bucketing adaptativo, detección de clusters, reglas de frescura.
Consulta el widget en vivo en /perpetuals/liquidations.
Fuentes de datos
Tres feeds WebSocket de exchanges, un único stream Redis normalizado por activo. Cada print de liquidación ejecutada cae en la cinta — no estimamos presión de liquidaciones a partir del open interest, no inferimos zonas de stops, no sintetizamos prints.
- Binance.
wss://fstream.binance.com/market/ws/<symbol>@forceOrder— stream público por símbolo. La ruta antigua/ws/es throttled silenciosamente por Binance;/market/ws/es la ruta activa. - Bybit. Topic
liquidationenwss://stream.bybit.com/v5/public/linear. - OKX. Canal
liquidation-ordersenwss://ws.okx.com:8443/ws/v5/public, instTypeSWAP.
Cada daemon escribe los eventos en un Redis Stream por activo prod:liq:<asset>:events con tope MAXLEN ≈ 10 000 (≈ 4 horas de cinta con cascadas, a menudo las 24 h completas en días tranquilos). El backend lee de un stream por activo tanto para REST como para WebSocket; no hay canal pubsub aparte — el stream es la superficie de broadcast.
Esquema del evento
Los campos por evento son idénticos en las tres fuentes. Los dos timestamps se mantienen distintos a propósito:
ts_ms— timestamp del trade marcado por el exchange. Úsalo para comparar tiempos de print entre venues.producer_ts_ms— wall-clock del daemon en el momento de la escritura.producer_ts_ms − ts_mses la latencia visible del feed más cualquier skew de reloj entre nosotros y el venue. Se muestrea en un ring buffer rotatorio de 200 eventos para reportar p50/p99.exchange∈{binance, bybit, okx}.side∈{long, short}— ver semántica abajo.qty— unidades en el activo base (BTC, ETH, …).price— precio de ejecución en moneda quote.usd=qty × price. Las comparaciones cross-asset van en USD; las comparaciones crudas en qty entre BTC y DOGE no significan nada.
Semántica del lado
Los streams de liquidación codifican el lado desde el punto de vista de la orden de cierre del exchange, que es el opuesto a la posición del trader. Normalizamos a la posición que perdió:
side: "long"— una posición long fue cerrada a la fuerza (el precio cayó atravesando su nivel de liquidación). En Binance es unSELLforceOrder; en Bybit, una posición conside: "Buy"liquidándose; en OKX,posSide: "long".side: "short"— una posición short fue cerrada a la fuerza (el precio subió). En Binance es unBUYforceOrder; en Bybit,side: "Sell"; en OKX,posSide: "short".
El color de la cinta sigue esto: punto rojo = long liquidado (precio bajó); punto verde = short liquidado (precio subió).
Escalado del tamaño
El radio en viewBox-px de cada punto es clamp(√(usd / $10 000) × 2, 4, 22). La raíz cuadrada atenúa el escalado lineal-por-área para que un evento de $1 M se lea como ~3.16× uno de $100 K — no 10× — y un solo print de cascada no pueda tapar todo lo demás. El clamp inferior mantiene una zona de click legible en eventos sub-$10 K; el superior evita que la cascada más grande se salga del panel.
r = clamp(sqrt(usd / 10_000) * 2, 4, 22) // viewBox-px # Valores de referencia (vs la leyenda de tamaño) $10 K → r ≈ 4 $100 K → r ≈ 6.3 $1 M → r = 20 $10 M+ → r = 22 (clamped)
Render adaptativo
El scatter crudo solo escala bien hasta ~15 minutos. Más allá, cientos de prints pequeños en un solo segundo se difuminan en un brillo uniforme y la estructura de cascada desaparece. Cambiamos el modo de render según la ventana:
- ≤ 15 m. Scatter crudo — un círculo por evento. Los puntos más grandes se dibujan al final para que las cascadas queden legibles encima del polvo.
- 1 h–4 h. Buckets de 1 minuto por (tiempo, lado). Un círculo por bucket no vacío, dimensionado por
Σ usd, plotado en el precio ponderado por volumen dentro del bucket. Una insignia pequeñaN×dentro del círculo muestra cuántos eventos subyacentes fusionó el bucket. - 24 h. La misma lógica con buckets de 5 minutos.
El hit-test, las anotaciones top-N y los tooltips operan sobre los eventos renderizados (con bucket); la detección de clusters, el panel acumulado y el sub-panel de ritmo escanean los eventos crudos, porque esas señales tratan de densidad por evento y flujo de lado, no de hover.
Detección de clusters
Las bandas de clusters de precio se detectan sobre los eventos crudos de la ventana activa:
reference_price = mid actual (precio del último evento)
bin_width = reference_price * 0.001 // 0.1 %
bin los eventos por round((price - reference) / bin_width)
para cada bin:
si Σ usd ≥ 15 % del total de la ventana Y eventos distintos ≥ 3:
márcalo como cluster
cluster_price = media ponderada por volumen de los precios del bin
mantén los 3 clusters más grandes por Σ usdCada cluster recibe una línea horizontal punteada + insignia $X cluster · N× @ price. La media ponderada por volumen (en lugar del centro nominal del bin) mantiene la línea donde los dólares realmente se concentraron.
Anotaciones top-N en línea
Hasta tres de los puntos más grandes de la ventana reciben una etiqueta inline $cantidad · hora · venue|N× para que el ojo aterrice de inmediato en la cascada más grande. El piso de $50 K evita etiquetar polvo. Las anclas de las etiquetas se invierten según la mitad del plot en la que esté el punto, así el texto nunca se sale por el eje derecho ni por el borde izquierdo.
Sub-panel de ritmo de liquidaciones
Bajo el panel acumulado, un gráfico de barras espejado muestra Σ usd por bucket de tiempo separado por lado: los shorts se extienden HACIA ARRIBA desde la línea base (verde); los longs HACIA ABAJO (rojo). El tamaño del bucket sigue los mismos umbrales adaptativos del scatter (30 s en ≤ 15 m, 1 min en 1–4 h, 5 min en 24 h) para que las columnas se alineen visualmente con los puntos de arriba.
Estado del feed
Un hash de estado por (activo, exchange) prod:liq:status:<asset>:<exchange> se refresca cada 5 s con TTL de 60 s. El backend lo convierte en tres estados:
- ok — daemon vivo (hash refrescado en los últimos 30 s) Y un evento aterrizó en los últimos 2 minutos.
- stale — daemon vivo, pero el venue lleva ≥ 2 minutos en silencio. Puede ser un venue genuinamente tranquilo o un feed mudo; el FE lo pinta ámbar.
- missing — daemon caído (hash expirado o nunca escrito). Se pinta rojo.
Los p50 / p99 de latencia que aparecen en el tooltip del estado provienen del ring buffer rotatorio de 200 eventos del daemon con producer_ts_ms − ts_ms.
Bandas de tiempo vacío
Las franjas continuas de la ventana sin ninguna liquidación reciben un tinte de fondo sutil. Sin eso, una vista de 24 h con 6 h de datos locales se lee como un renderer roto en lugar de como “no hubo cascadas en ese tramo”. El umbral es gap ≥ 5 % de la ventana — suficiente para marcar 30 m tranquilos en 24 h, sin rayar el chart con micro-bandas en segundos silenciosos.
Cacheo
- WebSocket. En vivo, sin caché. La cola la conduce
XREAD BLOCKsobre Redis Streams, así que la tarea WS espera nuevas entradas sin polling. - /api/liquidations/recent. Caché público de 5 s — suficiente para absorber una avalancha de refresh sin volver la cinta obsoleta.
- /api/liquidations/health. Caché público de 3 s. Bastante corto para parecer en vivo, bastante largo para que una avalancha de refresh no abanique 18
HGETALLs por request.
Limitaciones
- Auto-reportado por el venue. Cada exchange decide qué cuenta como print de liquidación. Prints spoofeados raros (parpadeos cancel-replace, fills parciales contados dos veces) pueden colarse. No intentamos deduplicación cross-venue: la cascada de un solo trader extendida en tres venues son tres eventos, por diseño.
- Profundidad del stream.
MAXLEN ≈ 10 000entradas por stream de activo. En un día agitado un solo activo llena el stream en ~4 horas; en un día tranquilo el mismo buffer cubre las 24 h completas. La ventana de 24 h del widget es el tope duro — no hay una superficie de replay a más largo plazo. - El precio mark es la última liquidación, no un mid continuo.La línea horizontal mark y el price trace derivan de los prints de liquidación. Con datos escasos el trace queda escalonado; con un feed de mid continuo se suavizaría. No leas el trace como historia precisa del mid — léelo como “¿dónde ocurrieron los prints?”.
- El skew temporal cross-exchange es real. Binance marca con granularidad de 1 segundo, Bybit y OKX van más finos; los relojes pueden derivar.
ts_mses lo que hay que usar para alineación inter-venue;producer_ts_mssolo sirve como medida de latencia, no como clave de ordenación. - Cobertura free-tier de OKX. El canal
liquidation-ordersestá disponible en el WS público (sin auth), pero al nivel VIP más bajo el firehose de liquidaciones de OKX puede retrasarse en cascadas sincronizadas. La pastilla de estado pasa a stale en esas ventanas.
Versionado
Metodología versión v1.0.0 · actualizado 2026-05-12. Los cambios materiales (nuevos venues, ajustes de fórmula, cambios de umbral) suben la versión y actualizan dateModified en los datos estructurados de arriba.