암호화폐 거래소에서 오더북(Order Book) 리플레이는 시장 미세구조(Market Microstructure)를 정밀하게 분석하고, 거래 알고리즘을 백테스팅하는 핵심 기술입니다. 저는 최근 6개월간 Tardis.me의 역사적 시장 데이터를 활용하여 고빈도 거래 시스템의 백테스팅 파이프라인을 구축했고, 이 과정에서 얻은 실무 노하우를 공유하려 합니다.
오더북 리플레이란 무엇인가?
오더북 리플레이는 과거 특정 시간대의 호가창 데이터를 시간 순서대로 재생하여, 그 시점의 시장 상황을 완벽하게 재현하는 기술입니다. 단순한 가격 이력 시뮬레이션과 달리:
- 호가창 상태: 매수/매도 호가의 양적 관계
- 유동성 변화: 특정 가격대의 주문 밀도
- 스프레드 역학: Bid-Ask 스프레드의 시系列 변화
- 호가 충격: 대규모 주문이 시장에 미치는 영향
를 실시간으로 재현할 수 있습니다. Tardis.me는 주요 암호화폐 거래소(Binance, Bybit, OKX 등)의 레벨2 호가창 데이터를 저지연으로 제공하며, 이를 통해 실제 거래 환경과 유사한 백테스팅이 가능합니다.
Tardis API 구조 이해
Tardis.me는 WebSocket과 REST 두 가지 인터페이스를 제공합니다. 오더북 리플레이에는 WebSocket이 필수적입니다.
# Tardis.ws — 오더북 리플레이용 WebSocket 클라이언트
https://docs.tardis.dev/docs/tardis-api#websocket-api
import asyncio
import json
from tardis Tardis import TardisTCP, Tardis replaybook
class OrderBookReplayer:
def __init__(self, exchange: str, symbol: str, start: int, end: int):
self.exchange = exchange
self.symbol = symbol
self.start = start # Unix timestamp (ms)
self.end = end
self.order_book = {}
self.trades = []
async def connect_and_replay(self):
"""Tardis WebSocket 연결 및 리플레이"""
# Binance USDT-M 선물 2024-01-15 오더북 데이터
exchange_id = f"{self.exchange}-futures" if "futures" in self.exchange else self.exchange
async with TardisTCP(
exchange=exchange_id,
symbols=[self.symbol],
from_timestamp=self.start,
to_timestamp=self.end
) as client:
async for message in client:
await self.process_message(message)
async def process_message(self, message: dict):
"""메시지 타입별 처리"""
msg_type = message.get("type")
if msg_type == "book_change":
# 오더북 업데이트 처리
await self.update_order_book(message["data"])
elif msg_type == "trade":
self.trades.append(message["data"])
elif msg_type == "book_snapshot":
# 초기 스냅샷 적용
self.order_book = self._parse_snapshot(message["data"])
def _parse_snapshot(self, data: dict) -> dict:
"""스냅샷 데이터 파싱"""
return {
"bids": {float(p): float(q) for p, q in data.get("bids", [])},
"asks": {float(p): float(q) for p, q in data.get("asks", [])},
"timestamp": data.get("timestamp")
}
async def update_order_book(self, changes: dict):
"""오더북增量 업데이트 적용"""
# bids 처리
for price, qty in changes.get("bids", []):
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["bids"].pop(price, None)
else:
self.order_book["bids"][price] = qty
# asks 처리
for price, qty in changes.get("asks", []):
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["asks"].pop(price, None)
else:
self.order_book["asks"][price] = qty
# 정렬 유지
self.order_book["bids"] = dict(
sorted(self.order_book["bids"].items(), reverse=True)[:20]
)
self.order_book["asks"] = dict(
sorted(self.order_book["asks"].items())[:20]
)
사용 예시: 2024년 1월 15일 BTC/USDT Binance Futures 오더북
async def main():
replayer = OrderBookReplayer(
exchange="binance",
symbol="BTCUSDT",
start=1705276800000, # 2024-01-15 00:00:00 UTC
end=1705363200000 # 2024-01-16 00:00:00 UTC
)
await replayer.connect_and_replay()
if __name__ == "__main__":
asyncio.run(main())
실시간 스프레드 및 유동성 분석
오더북 데이터를 재현하면 시장 미시구조의 다양한 지표를 계산할 수 있습니다.
import numpy as np
from dataclasses import dataclass
from typing import Dict, List
from collections import deque
@dataclass
class MarketMetrics:
timestamp: int
best_bid: float
best_ask: float
spread_bps: float # Basis points 단위 스프레드
mid_price: float
book_imbalance: float # 오더북 불균형 (-1 ~ +1)
micro_price: float # 유동성 가중均价
class MarketMicrostructureAnalyzer:
"""시장 미세구조 분석기"""
def __init__(self, window_size: int = 10):
self.window_size = window_size
self.order_book_history = deque(maxlen=1000)
self.metrics_history = []
def calculate_metrics(self, order_book: dict, timestamp: int) -> MarketMetrics:
"""오더북에서 시장 지표 계산"""
bids = order_book.get("bids", {})
asks = order_book.get("asks", {})
if not bids or not asks:
return None
best_bid = max(bids.keys())
best_ask = min(asks.keys())
mid_price = (best_bid + best_ask) / 2
# 스프레드 (basis points)
spread_bps = (best_ask - best_bid) / mid_price * 10000
# 오더북 불균형:bid总量 대비 bid总量的 차이
total_bid_qty = sum(bids.values