Order flow 방법론 — OBI × CVD 분면
Order flow 분면이 어떻게 소싱·크기 조정·해석되는가: 깊이에서 도출한 호가 불균형, taker 흐름 CVD, 롤링 P95 정규화, 존 분류, HTF 레짐, 신선도 규칙.
실시간 위젯 보기: /perpetuals/positioning.
데이터 출처
두 개의 Binance USDT-M 선물 스트림이 자산별 단일 마이크로스트럭처 데몬에 공급됩니다. 분면은 v1.0.0에서 단일 출처. 크로스 익스체인지 확장(거래소별 OBI와 CVD + 다이버전스 플래그)은 후속 릴리스 예정.
- 깊이.
wss://fstream.binance.com/stream?streams=<symbol>@depth@100ms— REST 스냅샷 책(1000 레벨)에 적용된 diff 스트림. 유지되는 책이 OBI 계산과 더 넓은 깊이 표면 모두를 뒷받침합니다. - Taker 출력.
wss://fstream.binance.com/market/ws/<symbol>@aggTrade— 모든 집계 taker 출력, 흐름 부호를 주는is_buyer_maker플래그 포함.
데몬 scripts/microstructure_daemon.py가 30초마다 data/<asset>/microstructure_snapshot.json에 하트비트 스냅샷을 쓰고, data/<asset>/microstructure_bars.jsonl(잔상 히스토리 표면)에 분당 한 바를 씁니다. 백엔드 /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 → 대역 내 균형
계산은 대역 내 측면당 상위 50 레벨을 가져옵니다. 분면의 X축으로 평활화 없이 플롯. 위젯은 OBI 라벨을 소수 3자리로 표시하여 0.01 이동이 보이게 합니다.
누적 거래량 델타 (CVD)
CVD는 롤링 윈도우 내 부호 있는 taker 명목가의 합. 부호 규약은 업계 테이프 리딩 관행을 따릅니다 — taker 매수는 위로, taker 매도는 아래로.
각 aggTrade 이벤트에 대해: notional = qty * price // USD is_buyer_maker == false이면: 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 파일은 분당 한 행과 두 윈도우를 모두 보존해 데몬 재시작 후에도 잔상이 히스토리를 리플레이할 수 있게 합니다. 이벤트당 처리량은 거래소 활성에 따라 ~10–50 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 잔상 기본 30s (자산별 조정 가능)
계층 2 — 크기 데드밴드 (Schmitt-trigger 하단)
데드밴드 내 후보 존 = 현재 존:
candidate_zone = current_zone if |obi_ema| < OBI_DB
or |cvd| < CVD_DB_PCT × p95_30m_usd
그렇지 않으면 후보 = (obi_ema, cvd)의 분면.
OBI_DB 자산별: BTC/ETH 0.05, SOL 0.07, XRP 0.08, BNB 0.10, DOGE 0.12
CVD_DB_PCT 자산 롤링 7d |cvd_30m| p95의 10%
계층 3 — 최소 체류 (Schmitt-trigger 상단)
새 후보는 현재가 되려면 ≥ MIN_TENURE_S 동안 유지되어야 합니다.
MIN_TENURE_S 30m 잔상 60s; 4h 300s; 24h 1800s.분면 명명은 v1.0에서 변하지 않음:
x >= 0 그리고 y >= 0 → "매수 우위" x < 0 그리고 y < 0 → "매도 우위" x < 0 그리고 y >= 0 → "수요 흡수 중" x >= 0 그리고 y < 0 → "오더북 지지"
후보가 아직 체류를 채우는 중일 때 FE는 판정 아래에 명시적 candidate_pending서브라인을 렌더링합니다 (“매도 · 4m 22s 동안 유지 · 매수 후보 18s / 60s”). 분류기는 블랙박스가 아니라 가시적 상태 기계.
파라미터는 자산별 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개 원시 분을 스트리밍할 필요가 없게 합니다:
- 30 분. 1분 버킷.
- 4 시간. 5분 버킷.
- 24 시간. 30분 버킷.
FE는 렌더링된 잔상을 60점으로 제한해 24시간 윈도우가 오버플로팅 없이 들어가게 합니다. 실시간 스냅샷은 같은 버퍼에 추가되어 버킷 시퀀스 뒤로 흔적을 남깁니다. 자산 전환 시 로컬 버퍼가 재설정되어 새 분면이 이전 자산의 잔상으로 오염되지 않습니다.
HTF 레짐 태그
위젯 헤더의 Trend BULL/BEAR/NEUTRAL 태그는 scripts/btc_monitor.py의 htf_regime()가 스냅샷 데몬 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분 주기)를 스냅샷 데몬이 ~5분마다 폴링. 1.0 위는 리테일 계정 net long, 아래는 net short. 이는 계정의 포지셔닝이지 흐름이 아닙니다 — 분면 평면이 아니라 OBI·CVD와 함께 사이드 패널에 위치합니다.
신선도
세 독립 신호가 위젯을 비실시간으로 표시할 수 있습니다:
micro_book_state≠"ok"— WS 재연결 후 데몬이 재동기화 중. REST 스냅샷이 다시 도착할 때까지 OBI 만료. 상태 pill이Daemon <state>로 전환.micro_stale— 하트비트가 윈도우보다 오래됨. FE는 잔상에 새 점 추가 중지.- 잔상이
partial: true표시 — 윈도우를 지지하는 jsonl이 요청된 lookback보다 짧음(데몬 재시작 직후 흔함). 캔버스에 앰버 pill 표시.
한계
- 단일 출처. v1.0.0는 Binance만 읽음. 따라서 분면 판정은 Binance 마이크로스트럭처를 반영, 크로스 익스체인지 집계 압력이 아님. 다이버전스 지시자를 포함한 크로스 익스체인지 OBI와 CVD는 후속 릴리스 예정.
- 경직된 분면 경계. OBI 0의 양쪽 0.01 위치에서 언어 판정이 뒤집힙니다. v1.0.0에는 데드밴드도 저유량 가드도 없음 — 원점 근처 위치는 엄격한 존 리드가 아니라 미결정으로 해석하세요.
- ±0.2 % 대역은 고정 노브. OBI 대역은 모든 자산에서 동일. 타이트 스프레드 메이저(BTC, ETH)는 얇은 책 알트보다 대역 내에서 더 깊은 책을 봅니다 — 이것이 CVD가 1차 판정이고 OBI가 보조 축인 이유 중 하나.
- Taker 측 부호 규약은 거래소 의존적. Binance aggTrade의
is_buyer_maker플래그가 여기서는 권위적 신호. 크로스 익스체인지 확장은 측 의미론을 먼저 정규화해야 합니다(Bybit과 OKX는 측을 다르게 인코딩). - L/S는 Binance의 리테일 포지셔닝. 비율은 Binance의 계정을 세며 크로스 익스체인지 집계가 아니고 모든 계정을 크기와 무관하게 동등 가중. 흐름 입력이 아니라 부드러운 컨텍스트로 사용하세요.
- HTF 레짐은 모든 자산에 BTC 스타일 임계값 사용. 4h ±0.5 % / 24h ±1.5 % 임계값은 BTC 대비 튜닝되었음. 고변동성 자산(DOGE, SOL)에서 BULL/BEAR는 직관보다 일찍 발화 — 태그를 보정된 트렌드 필터가 아니라 거친 컨텍스트로 다루세요.
버전
방법론 버전 v2.0.0 · 업데이트 2026-05-12. 중요 변경(추가 거래소, 공식 조정, 임계값 변경)은 버전을 올리고 위의 구조화 데이터의 dateModified를 갱신.