MarketTrace
M1PosicionamientoM2Libro de órdenesM3LiquidacionesM4Financiación
Metodología · v2.0.0 · actualizado 2026-05-12

Posicionamiento del mercado (OBI × CVD)

Cómo se obtiene, dimensiona y lee el cuadrante OBI × CVD: desequilibrio derivado de la profundidad, CVD por flujo agresor, normalización P95 rotatoria, clasificación de zonas, régimen HTF y reglas de frescura.

Consulta el widget en vivo en /perpetuals/positioning.

Fuentes de datos

Dos streams de Binance USDT-M futuros alimentan un único daemon de microestructura por activo. El cuadrante es single-source en v1.0.0; la expansión cross-exchange (OBI y CVD por venue más un indicador de divergencia) está prevista para una próxima versión.

El daemon scripts/microstructure_daemon.py escribe un heartbeat cada 30 s en data/<asset>/microstructure_snapshot.json y una barra por minuto en data/<asset>/microstructure_bars.jsonl (la superficie del trail). El WebSocket de backend /ws/market empuja una snapshot cada ~10 s y una trama trail/normalizador al suscribirse y al cambiar de ventana.

Desequilibrio del libro (OBI)

OBI mide el skew de liquidez en reposo dentro de una banda ajustada alrededor del precio medio. Los niveles fuera de la banda quedan excluidos porque no afectan a un trade que se come el spread.

mid       = (best_bid + best_ask) / 2
banda     = [mid * 0.998, mid * 1.002]   // ±0.2 %
bid_qty   = Σ qty de bids dentro de la banda
ask_qty   = Σ qty de asks dentro de la banda
OBI       = (bid_qty - ask_qty) / (bid_qty + ask_qty)

Rango:    [-1, +1]
+1 →      asks vacíos, todo el peso en bids
-1 →      bids vacíos, todo el peso en asks
 0 →      equilibrado dentro de la banda

Calculado sobre los 50 niveles superiores por lado que caen dentro de la banda. Se traza como eje X del cuadrante, sin suavizado. El widget muestra OBI como tag con tres decimales para que un movimiento de 0,01 sea visible.

Delta de volumen acumulado (CVD)

CVD suma el notional agresor con signo sobre una ventana rotatoria. La convención de signo sigue la lectura clásica de cinta: las compras agresoras suben, las ventas agresoras presionan.

para cada evento aggTrade:
  notional = qty * price                       // USD
  si is_buyer_maker == false: delta = +notional   // agresor compró
  si no:                      delta = -notional   // agresor vendió
  añadir (ts, delta) a un deque de 2 horas

cvd_30m_usd = Σ delta para ts en [ahora - 30m, ahora]
cvd_2h_usd  = Σ delta para ts en [ahora - 2h, ahora]

El deque de 2 h es el horizonte más largo; la ventana de 30 m se toma del mismo buffer en cada escritura. El archivo de barras persiste una fila por minuto con ambas ventanas, de modo que el trail pueda replayear historia tras un reinicio. El throughput por evento queda en ~10–50 prints/s según la actividad del venue.

Normalizador del eje Y

Las magnitudes crudas de CVD a 30 m varían en dos órdenes de magnitud entre BTC (movimientos de orden $1 M), ETH, SOL y los perpetuos más pequeños. Plotear USD crudo aplastaría los activos menores. El eje Y por eso divide por un P95 rotatorio de 7 días de |cvd_30m_usd| por activo y recorta a [-1, +1].

y_norm = clamp(cvd_30m_usd / p95_30m_usd, -1, +1)

p95_30m_usd: percentil rotatorio 7 días, refrescado por el endpoint de snapshot
fallback:    2 000 000 USD cuando el percentil aún no está disponible
             (arranque en frío o muestra escasa)

El endpoint /api/market/cvd-normalizer?asset=… sirve el P95 por activo, replicado por WebSocket como trama normalizer para que el FE reescale el trail con coherencia cuando el valor llega después de los datos del trail.

Clasificación de zonas (v2)

La regla de cuadrante crudo (signo de OBI, signo de CVD) se envuelve en un pipeline de tres capas para que el veredicto no parpadee con ruido sub-bp. Cada capa actúa en secuencia; sólo las señales que sobreviven a las tres se convierten en la zona mostrada.

Capa 1 — EMA sobre OBI (CVD ya es un agregado de ventana deslizante)
  obi_ema(t) = α · obi_raw(t) + (1 - α) · obi_ema(t-1)
  α = 1 - exp(-dt / span_segundos)
  span_segundos: 30 s en el trail 30m (por defecto; ajustable por activo)

Capa 2 — Banda muerta de magnitud (Schmitt-trigger, brazo inferior)
  Dentro de la banda muerta, candidato = zona actual:
    candidato = zona_actual  si |obi_ema| < OBI_DB
                              o   |cvd|     < CVD_DB_PCT × p95_30m_usd
  De lo contrario, candidato = cuadrante de (obi_ema, cvd).
  OBI_DB        por activo: BTC/ETH 0.05, SOL 0.07, XRP 0.08,
                            BNB 0.10, DOGE 0.12
  CVD_DB_PCT    10 % del p95 |cvd_30m| rolling 7d del activo

Capa 3 — Tenencia mínima (Schmitt-trigger, brazo superior)
  Un nuevo candidato debe mantenerse ≥ MIN_TENURE_S antes de
  convertirse en actual.
  MIN_TENURE_S  60 s en trail 30m; 300 s en 4h; 1800 s en 24h.

El nombre de los cuadrantes no cambia respecto a v1.0:

x >= 0 Y y >= 0  → "Compradores al mando"
x <  0 Y y <  0  → "Vendedores dominando"
x <  0 Y y >= 0  → "Demanda absorbiendo"
x >= 0 Y y <  0  → "Libro sostiene"

Mientras un candidato espera su tenencia mínima, el FE renderiza una sub-línea explícita bajo el veredicto (“Vendedores · sostenido 4m 22s · Compradores pendientes 18s de 60s”). El clasificador es una máquina de estados visible, no una caja negra.

Los parámetros se ajustan por activo desde un sweep de grid de 7 días (scripts/replay_classifier.py); los valores seleccionados viven en backend/classifier/config.pyy pueden ser sobrescritos por env (p. ej.MICRO_DEADBAND_DOGE=0.20) para hot-patch sin redeploy. El estado (OBI EMA, timers del candidato, entered-at) se persiste en Redis entre ciclos del agregador, de modo que un reinicio del daemon resume en la misma zona en lugar de arrancar en frío.

Historial del trail

El trail dibuja una polilínea con fade por edad de las últimas N posiciones del cuadrante. Ventanas seleccionables: 30m, 4h y 24h. Cada ventana extrae una serie downsampleada del archivo de barras para que un replay de 24 h no transmita 1440 minutos crudos:

El FE limita el trail renderizado a 60 puntos para que la ventana de 24 h siga encajando sin sobreploteo. Las snapshots en vivo se anexan al mismo buffer, dejando una estela detrás de la serie bucketeada. Al cambiar de activo el buffer local se reinicia para que un cuadrante fresco no quede contaminado por el trail del activo anterior.

Etiqueta de régimen HTF

La etiqueta Trend BULL/BEAR/NEUTRAL en la cabecera del widget la calcula htf_regime() en scripts/btc_monitor.py en la cadencia de 5 m del daemon de snapshot:

toma 300 velas de 1 minuto (requiere >= 289 barras válidas)
pct_4h  = (close - close_4h_atras)  / close_4h_atras  * 100
pct_24h = (close - close_24h_atras) / close_24h_atras * 100

BULL    si pct_4h >  +0.5  O pct_24h >  +1.5
BEAR    si pct_4h <  -0.5  O pct_24h <  -1.5
NEUTRAL en caso contrario (también cuando la muestra es insuficiente)

La etiqueta es solo contexto HTF, no una lectura de flujo. Existe para que los veredictos de ventana corta del cuadrante (CVD 30 m) se contrasten con la dirección multi-hora.

Long/short ratio

El stat L/S lee el globalLongShortAccountRatio de Binance en período de 15 m, encuestado cada ~5 m por el daemon de snapshot. Valores por encima de 1.0 significan cuentas retail netas largas; por debajo de 1.0 netas cortas. Es posicionamiento de cuentas, no flujo — aparece junto a OBI y CVD en el panel lateral, no en el plano del cuadrante.

Frescura

Tres señales independientes pueden marcar el widget como no-vivo:

Limitaciones

Versionado

Metodología versión v2.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.