MarketTrace
M1포지셔닝M2풋프린트M3청산M4FundingM5볼륨 프로파일
방법론 · v2.0.0 · 업데이트 2026-07-04

Order flow 방법론, OBI × CVD 분면

Order flow 분면이 어떻게 소싱·크기 조정·해석되는가: 깊이에서 도출한 호가 불균형, taker 흐름 CVD, 롤링 P95 정규화, 존 분류, HTF 레짐, 신선도 규칙.

실시간 위젯 보기: /perpetuals/positioning.

데이터 출처

분면은 대기 깊이와 taker 흐름을 네 개의 무기한 거래소, Binance, Bybit, OKX, Hyperliquid, 에서 두 축 모두에 대해 집계합니다. 런타임은 전부 Rust이며, hot path에 Python 데몬은 없습니다.

백엔드 /ws/market WebSocket은 ~10초마다 스냅샷을 푸시하고, 구독과 윈도우 변경 시 trail/normaliser 프레임을 푸시합니다.

호가 불균형 (OBI)

OBI는 중간가 주변 좁은 대역 내 대기 유동성 편향을 측정합니다. 먼저 거래소별로, 각 거래소 자신의 책 위에서 계산되며, 크로스 거래소 혼합은 다음 섹션에서 설명합니다. 대역 밖 레벨은 제외, 스프레드를 가져가는 거래에 영향을 주지 않기 때문.

mid       = (best_bid + best_ask) / 2          // 거래소별
band      = [mid * 0.998, mid * 1.002]         // ±0.2 %
bid_qty   = 대역 내 매수 qty 합
ask_qty   = 대역 내 매도 qty 합
OBI       = (bid_qty - ask_qty) / (bid_qty + ask_qty)

범위:    [-1, +1]
+1 →      매도 비어 있음, 모든 가중치 매수에
-1 →      매수 비어 있음, 모든 가중치 매도에
 0 →      대역 내 균형

계산은 대역 내 측면당 상위 200 레벨을 가져옵니다(TOP_LEVELS_PER_SIDE = 200). 플롯되는 X축은 아래의 크로스 거래소 혼합이며 이 단계에서는 평활화 없음. 위젯은 OBI 라벨을 소수 3자리로 표시하여 0.01 이동이 보이게 합니다.

크로스 거래소 OBI 집계 (flow-weighted)

각 거래소의 거래소별 OBI(위)는 그 거래소의 최근 taker 흐름으로 가중된 단일 크로스 거래소 OBI로 결합됩니다, flow-weighted (FW), 역분산이 아님. 거래소의 가중치는 실제로 체결이 찍히는 곳을 따라가므로, 조용한 책은 거의 기여하지 않고 활발한 책이 컨센서스를 주도합니다.

2026-05-26에 집계는 역분산 가중(IVW)에서 FW로 전환되었습니다. IVW에서는 Hyperliquid가 혼합을 지배했습니다: nSigFigs로 양자화된(거친) 책이 인위적으로 낮은 분산의 OBI를 만들고, IVW는 그 뒤에 얼마나 적은 크기가 있든 상관없이 높은 가중치로 보상합니다. Flow-weighting은 가중치를 실현된 공격자 볼륨에 묶으므로, 거친 양자화 거래소가 더 이상 병합된 판독을 지배할 수 없습니다.

누적 거래량 델타 (CVD)

CVD는 롤링 윈도우 내 부호 있는 taker 명목가를 네 거래소 전체에 걸쳐 합산합니다. 부호 규약은 업계 테이프 리딩 관행을 따릅니다, taker 매수는 위로, taker 매도는 아래로. 각 거래소의 공격자 측은 인제스트에서 정규화되어(Bybit, OKX, Hyperliquid는 Binance와 다르게 인코딩) 모든 출력이 같은 부호를 기여합니다.

각 taker 출력(모든 거래소)에 대해:
  notional = qty * price                       // USD
  공격자 == 매수이면: delta = +notional          // taker 매수
  그렇지 않으면:       delta = -notional          // taker 매도
  (ts, delta)를 2시간 deque에 추가

cvd_30m_usd = Σ delta, ts ∈ [now - 30m, now]
cvd_2h_usd  = Σ delta, ts ∈ [now - 2h, now]

2시간 deque가 가장 긴 호라이즌. 30분 윈도우는 매 쓰기마다 같은 버퍼에서 슬라이스. bars 파일은 분당 한 행과 두 윈도우를 모두 보존해 재시작 후에도 잔상이 히스토리를 리플레이할 수 있게 합니다. 네 거래소 합산 처리량은 활성에 따라 ~40–200 prints/s.

Y축 정규화기

원시 30분 CVD 크기는 BTC($1M급 이동)와 ETH·SOL·소형 무기한 사이에서 두 자릿수 차이가 납니다. 원시 USD 플롯은 작은 자산을 평평한 선으로 누릅니다. 따라서 Y축은 자산별로 롤링 7일 |cvd_30m_usd| P95로 나눠 결과를 [-1, +1]로 클램프합니다.

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

p95_30m_usd: 롤링 7일 백분위, 스냅샷 엔드포인트에서 갱신
fallback:    롤링 백분위가 아직 사용 불가일 때(콜드 스타트 또는 얇은 표본)
             2_000_000 USD 사용

엔드포인트 /api/market/cvd-normalizer?asset=…가 자산별 P95 값을 제공하며 WebSocket의 normalizer 프레임으로 미러링되어, FE는 그 값이 잔상 데이터 이후에 도착할 때 잔상을 일관되게 재스케일할 수 있습니다.

존 분류 (v2)

원시 분면 규칙(OBI 부호, CVD 부호)은 sub-bp 노이즈로 판정이 깜빡이지 않도록 3계층 파이프라인으로 감쌌습니다. 각 계층이 순차로 발화하며, 세 계층 모두를 통과하는 신호만 표시되는 존이 됩니다.

계층 1, OBI에 EMA (CVD는 이미 슬라이딩 윈도우 집계)
  obi_ema(t) = α · obi_raw(t) + (1 - α) · obi_ema(t-1)
  α = 1 - exp(-dt / span_seconds)
  span_seconds 30m 자산별: BTC/ETH/SOL/DOGE 120s, BNB 60s, XRP 5s

계층 2, 크기 데드밴드 (Schmitt-trigger 하단)
  두 축 모두 데드밴드 안  → 후보 = "balanced" (평평한 시장:
    |obi_ema| < OBI_DB  그리고  |cvd| < CVD_DB_PCT × p95_30m_usd, 어느 쪽도 편향 없음).
  정확히 한 축만 안       → 현재 존 유지 (모호).
  두 축 모두 밖           → 후보 = (obi_ema, cvd)의 분면.
  OBI_DB      자산별: BTC 0.12, ETH 0.20, SOL 0.12, BNB 0.12, XRP 0.05, DOGE 0.20
  CVD_DB_PCT  자산별, 롤링 |cvd_30m| p95의 분수:
              BTC 0.15, ETH 0.05, SOL 0.20, BNB 0.10, XRP 0.20, DOGE 0.20

계층 3, 최소 체류 (Schmitt-trigger 상단)
  새 후보는 현재가 되려면 ≥ MIN_TENURE_S 동안 유지되어야 합니다. 데드밴드로의
  한 번의 딥은 더 이상 성숙 중인 후보를 지우지 않습니다; 타이머는 연속 홀드의
  최대 3s 유예 동안 유지됩니다 (CVD는 평활화되지 않음).
  MIN_TENURE_S  30m 잔상 300s (DOGE 30s); 4h 300s; 24h 1800s.

네 개의 방향성 분면, 그리고 어느 축도 편향이 없을 때의 balanced 상태:

x >= 0 그리고 y >= 0   → "매수 우위"
x <  0 그리고 y <  0   → "매도 우위"
x <  0 그리고 y >= 0   → "수요 흡수 중"
x >= 0 그리고 y <  0   → "오더북 지지"
두 축 모두 데드밴드 내  → "Balanced" (뚜렷한 우위 없음)

후보가 아직 체류를 채우는 중일 때 FE는 판정 아래에 명시적 candidate_pending서브라인을 렌더링합니다 (“매도 · 4m 22s 동안 유지 · 매수 후보 18s / 5m”). 분류기는 블랙박스가 아니라 가시적 상태 기계.

파라미터는 자산별 7일 그리드 스윕(scripts/replay_classifier.py)에서 튜닝. 선택된 값은 backend/classifier/config.py에 위치하며 재배포 없이 핫패치를 위해 env 오버라이드(예: MICRO_DEADBAND_DOGE=0.20) 가능. 상태(OBI EMA, 후보 타이머, entered-at)는 애그리게이터 사이클 사이에 Redis에 지속되므로 데몬 재시작은 콜드 스타트가 아니라 같은 존에서 재개됩니다.

잔상 히스토리

잔상은 최근 N개 분면 위치를 나이별로 페이드되는 폴리라인으로 그립니다. 선택 가능한 윈도우는 30m, 4h, 24h. 각 윈도우는 bars 파일에서 다운샘플링된 시퀀스를 가져와 24시간 리플레이가 1440개 원시 분을 스트리밍할 필요가 없게 합니다:

FE는 렌더링된 잔상을 60점으로 제한해 24시간 윈도우가 오버플로팅 없이 들어가게 합니다. 실시간 스냅샷은 같은 버퍼에 추가되어 버킷 시퀀스 뒤로 흔적을 남깁니다. 자산 전환 시 로컬 버퍼가 재설정되어 새 분면이 이전 자산의 잔상으로 오염되지 않습니다.

HTF 레짐 태그

위젯 헤더의 Trend BULL/BEAR/NEUTRAL 태그는 scripts/btc_monitor.py(유지되는 cron writer)의 htf_regime()가 스냅샷 cron 5분 케이던스로 계산:

300 1분 캔들 가져오기 (>= 289 유효 바 필요)
pct_4h  = (close - close_4h_ago)  / close_4h_ago  * 100
pct_24h = (close - close_24h_ago) / close_24h_ago * 100

BULL    if pct_4h >  +0.5  or pct_24h >  +1.5
BEAR    if pct_4h <  -0.5  or pct_24h <  -1.5
NEUTRAL 그렇지 않으면 (표본 부족 시에도)

태그는 HTF 컨텍스트일 뿐 흐름 리딩이 아닙니다. 분면의 단기 윈도우 판정(30분 CVD)이 다중 시간 방향에 대해 sanity check 받을 수 있도록 존재합니다.

Long/Short 비율

L/S 통계는 Binance의 globalLongShortAccountRatio(15분 주기)를 스냅샷 cron이 ~5분마다 폴링. 1.0 위는 리테일 계정 net long, 아래는 net short. 이는 계정의 포지셔닝이지 흐름이 아닙니다, 분면 평면이 아니라 OBI·CVD와 함께 사이드 패널에 위치합니다.

신선도

세 독립 신호가 위젯을 비실시간으로 표시할 수 있습니다:

한계

버전

방법론 버전 v2.0.0 · 업데이트 2026-07-04. 중요 변경(추가 거래소, 공식 조정, 임계값 변경)은 버전을 올리고 위의 구조화 데이터의 dateModified를 갱신.