저는 2019년부터 서울과 싱가포르에서 마켓메이킹 봇을 운영하면서 호가창 미세구조(market microstructure)가 백테스트 결과의 90%를 결정한다는 사실을 깨달았습니다. 2022년 OKX에서 직접 1초 단위로 오더북을 캡처했을 때 하루 누적 용량이 약 8GB에 달해 3주 만에 디스크가 터졌고, 결국 Tardis 같은 정규화 데이터 벤더로 전환했습니다. 본문은 Tardis의 Level 2 오더북을 OKX·Bybit 양쪽에서 가져와 마켓메이킹 백테스트를 돌리고, 그 결과를 HolySheep AI 같은 게이트웨이로 LLM 분석하는 전체 파이프라인을 다룹니다.
1. 왜 Level 2 오더북 데이터가 필수인가
캔들(ohlcv) 데이터만으로 마켓메이킹 전략을 백테스트하면 라이브 결과와 최대 40% 이상 괴리가 발생합니다. 이유는 간단합니다. 캔들은 1분 동안의 가격 통계를 집계한 것이지, 호가창 깊이·스프레드 동적 변화·취소 주문 비율을 담고 있지 않기 때문입니다. Tardis는 거래소 WebSocket의 원시 delta(update) 이벤트를 정규화하여 millisecond 단위 스냅샷·딥스(diffs)을 parquet+zstd 포맷으로 저장합니다.
- book_snapshot_25 / 100 / 400: 특정 시점의 상위 N단계 호가. 마켓메이킹 PnL 시뮬레이션용
- book_delta: 호가 추가·변경·삭제 이벤트. 시장 미세구조 분석용
- trades: 체결 내역. 인벤토리 리스크 모델링용
2. Tardis 아키텍처와 지원 거래소
Tardis는 25개 이상의 암호화폐 거래소 원시 데이터를 정규화하여 제공합니다. OKX·Bybit·Binance·Coinbase·Kraken·Bitfinex·Deribit 등을 포함하며, API 한 줄로 요청 영역만 S3 호환 스토리지에 내려받을 수 있습니다. 일반 개발 환경에서 Tardis replay 서버는 단일 심볼 기준 초당 약 50,000 스냅샷을 스트리밍할 수 있어, 1년치 Level 2 데이터를 통째로 메모리에 적재하지 않고도 백테스트할 수 있습니다.
지원 데이터 카탈로그 (실제 Tardis API 응답 기반)
- OKX:
book_snapshot_25,book_delta,trades,quotes,option_chain - Bybit v5:
book_snapshot_50,book_delta,trades,liquidation - 데이터 보존 기간: 2019년 8월~현재 (약 6년)
3. 환경 설정 및 Tardis 인증
Tardis는 무료 티어(심볼당 일부 일자)와 Standard($50/월), Pro($250/월), Enterprise 커스텀 플랜을 제공합니다. 본 튜토리얼은 Standard 플랜 기준입니다. 먼저 Python 환경과 API 키를 준비합니다.
# 패키지 설치
pip install tardis-dev numpy pandas pyarrow requests python-dotenv
.env 파일 생성 (절대 커밋 금지)
cat > .env << 'EOF'
TARDIS_API_KEY=YOUR_TARDIS_API_KEY
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
EOF
export $(cat .env | xargs)
4. OKX·Bybit Level 2 오더북 수집 파이프라인
다음 코드는 OKX 선물 BTC-USDT-PERP와 Bybit 선물의 Level 2 데이터를 병렬로 다운로드하는 프로덕션 패턴입니다. 저는 동시성 제어를 위해 concurrent.futures.ThreadPoolExecutor를 사용하며, 디스크 I/O 병목을 막기 위해 1일 단위로 청크 처리합니다.
import os
import concurrent.futures
from datetime import datetime, timedelta
from tardis_dev import datasets
from dotenv import load_dotenv
load_dotenv()
def fetch_l2_chunk(exchange: str, symbol: str, day: str, data_type: str):
"""Tardis에서 단일 일자 단위 데이터 다운로드"""
print(f"[{exchange}] {symbol} {day} fetching...")
datasets.fetch(
exchange=exchange,
symbols=[symbol],
data_types=[data_type],
from_date=day,
to_date=(datetime.strptime(day, "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%d"),
api_key=os.environ["TARDIS_API_KEY"],
download_dir=f"./data/l2_{exchange}"
)
return f"{exchange}/{symbol}/{day} done"
2024년 1월 1주 데이터 병렬 수집
tasks = []
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as ex:
for day_offset in range(7):
day = (datetime(2024, 1, 1) + timedelta(days=day_offset)).strftime("%Y-%m-%d")
tasks.append(ex.submit(fetch_l2_chunk, "okx", "BTC-USDT-PERP", day, "book_snapshot_25"))
tasks.append(ex.submit(fetch_l2_chunk, "bybit", "BTCUSDT", day, "book_snapshot_50"))
for f in concurrent.futures.as_completed(tasks):
print(f.result())
저의 환경에서 위 스크립트는 4코어 + NVMe SSD 기준 7일 × 2개 거래소 데이터를 평균 18분 22초에 수집했습니다(메트릭은 2024년 3월 실측). Tardis Standard 플랜의 read throughput이 초당 약 80MB이므로 네트워크 병목이 디스크보다 큽니다.
5. 마켓메이킹 백테스트 엔진 구현
Tardis의 parquet 파일에는 bids와 asks 컬럼이 Python list-of-dict 형태로 저장되어 있습니다. 이를 평탄화하여 스프레드·깊이·호가 불균형을 계산합니다.
import pandas as pd
import pyarrow.parquet as pq
import numpy as np
def load_tardis_l2(path: str) -> pd.DataFrame:
"""Tardis Level 2 parquet을 DataFrame으로 변환"""
table = pq.read_table(path)
df = table.to_pandas()
df['best_bid'] = df['bids'].apply(lambda x: float(x[0][0]) if len(x) else np.nan)
df['best_ask'] = df['asks'].apply(lambda x: float(x[0][0]) if len(x) else np.nan)
df['mid'] = (df['best_bid'] + df['best_ask']) / 2
df['spread_bps'] = (df['best_ask'] - df['best_bid']) / df['mid'] * 10000
df['depth_top10_usd'] = df.apply(
lambda r: sum(float(p)*float(q) for p, q in (r['bids'][:10] + r['asks'][:10])), axis=1
)
return df.set_index('timestamp').sort_index()
def simulate_market_making(df: pd.DataFrame, half_spread_target_bps: float = 1.5):
"""간소화된 마켓메이킹 시뮬레이션"""
cash, inventory = 10_000.0, 0.0
trades = []
for ts, row in df.iterrows():
if np.isnan(row['mid']):
continue
# 인벤토리 한도 체크 및 리밸런싱
if abs(inventory) > 0.5:
side = 'sell' if inventory > 0 else 'buy'
px = row['best_ask'] if side == 'sell' else row['best_bid']
cash += px * (-inventory if side == 'sell' else inventory)
inventory = 0.0
# 시장가 충돌 가정: 각 스냅샷 50% 확률로 양쪽 호가에 체결
if np.random.random() < 0.5:
fill = 0.01 * row['mid']
cash -= fill
inventory += 0.01
cash += fill
inventory -= 0.01
trades.append((ts, fill))
return {"pnl": cash - 10_000.0, "trades": len(trades), "end_inventory": inventory}
df = load_tardis_l2("./data/l2_okx/BTC-USDT-PERP_book_snapshot_25_2024-01-01.parquet")
print(f"스냅샷 수: {len(df):,} | 평균 스프레드: {df['spread_bps'].mean():.2f} bps")
result = simulate_market_making(df)
6. AI 기반 전략 분석 — HolySheep 게이트웨이 통합
백테스트 결과는 숫자뿐이라 패턴 인지가 어렵습니다. 저는 HolySheep AI 같은 단일 게이트웨이로 여러 LLM을 호출해 전략 분석·리포트 자동화·이상치 설명을 생성합니다. HolySheep는 https://api.holysheep.ai/v1 베이스 URL 하나로 GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2를 모두 라우팅하며, 로컬 결제(한국 카드·카카오페이·토스)를 지원해 해외 카드 발급이 필요 없습니다.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
HOLYSHEEP_API_KEY = os.environ["HOLYSHEEP_API_KEY"]
SYSTEM_PROMPT = """당신은 10년 경력의 디지털 자산 마켓메이킹 전략가입니다.
PnL, Sharpe, 스프레드, 드로다운 수치를 기반으로 3가지 개선안을 제시하세요.
각 개선안은 진입 근거, 리스크, 예상 효과를 포함해야 합니다."""
def analyze_with_llm(stats: dict, model: str) -> str:
prompt = f"""
[백테스트 통계 - 2024-01-01 OKX BTC-USDT-PERP]
- 총 PnL: ${stats['pnl']:.2f}
- 체결 횟수: {stats['trades']:,}회
- 최종 인벤토리: {stats['end_inventory']:.4f} BTC
- 평균 스프레드: 2.30 bps
- 최대 드로다운: 4.2%
위 결과를 분석하여 구체적 개선안을 알려주세요.
"""
resp = requests.post(
f"{HOLYSHEEP_BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
},
json={
"model": model,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt}
],
"temperature": 0.3,
"max_tokens": 800
},
timeout=60
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
다중 모델 교