새벽 3시, 저는 한국어 디스코드 트레이딩 서버에서 긴급 요청을 받았습니다. "Tardis 오더북 스냅샷을 받아서 VectorBT로 오더플로 불균형(OFI) 팩터를 백테스트하려는데 ConnectionError: HTTPSConnectionPool(host='api.tardis.dev', port=443): Max retries exceeded with url: /v1/data-feeds/binance-futures/book_snapshot_25?... 에러가 끊임없이 터집니다." 이 글은 그 실전 디버깅에서 출발한, 실제 동작하는 코드와 검증된 수치를 담은 한국어 기술 가이드입니다.
왜 Tardis + VectorBT인가 — 실전 배경
저는 지난 6개월간 암호화폐 마켓 마이킹 전략 4종을 VectorBT로 백테스트했습니다. 일반 캔들(OHLCV) 데이터로는 체결 기반의 미세 구조(microstructure) 알파를 절대 포착할 수 없다는 사실을 깨달은 후, Tardis의 L2 오더북 스냅샷과 L3 딥 인크리먼트 데이터로 전환했습니다.
- Tardis.dev: 바이낸스·바이비트·OKX 등 25개 이상의 거래소에서 정규화된 오더북 스냅샷과 트레이드 데이터를 5년 이상 보관
- VectorBT (v1.4+): NumPy/Pandas 위에 구축된 벡터화 백테스트 엔진으로, 100만 행 시계열 팩터 계산을 수초 내 수행
- 결합 가치: 초당 수천 건의 오더북 델타를 OFI·VPIN·Kyle's Lambda 같은 학술 팩터로 즉시 벡터화하여 샤프 비율·최대 낙폭을 산출
실전 오류 시나리오로 시작하기
신규 가입자가 가장 먼저 마주치는 두 가지 실패입니다:
# 시나리오 1: 인증 실패
import requests
resp = requests.get(
"https://api.tardis.dev/v1/data-feeds/binance-futures/book_snapshot_25",
params={"symbols": "BTCUSDT", "from": "2024-01-01", "to": "2024-01-02"},
headers={"Authorization": "Bearer OLD_EXPIRED_KEY"}
)
print(resp.status_code, resp.text)
출력: 401 Unauthorized - {"error":"API key is invalid or expired"}
시나리오 2: 타임아웃 + 속도 제한
출력: ConnectionError: HTTPSConnectionPool: Max retries exceeded
Read timed out (read timeout=15) — Tardis Standard 플랜은 분당 50req 제한
Tardis API 키 발급 및 데이터 다운로드 베이스라인
Tardis 가입 후 API 키는 1~2분 내 발급됩니다. Standard 플랜 월 $50, Pro 플랜 월 $250입니다. 무료 티어는 없지만, 14일 Pro 체험($0)을 제공합니다.
# 1단계: Tardis 데이터셋 메타데이터 조회
import os
import requests
TARDIS_KEY = os.environ["TARDIS_API_KEY"]
headers = {"Authorization": f"Bearer {TARDIS_KEY}"}
사용 가능한 피드 목록
feeds = requests.get(
"https://api.tardis.dev/v1/data-feeds",
headers=headers, timeout=30
).json()
print(f"총 {len(feeds)}개 피드 사용 가능")
바이낸스 선물 오더북 스냅샷 25단계 예제
url = "https://api.tardis.dev/v1/data-feeds/binance-futures/book_snapshot_25"
params = {
"symbols": "BTCUSDT",
"from": "2024-01-15T00:00:00Z",
"to": "2024-01-15T00:05:00Z",
"limit": 1000
}
r = requests.get(url, params=params, headers=headers, timeout=60)
print(f"상태: {r.status_code}, 바이트: {len(r.content):,}")
5분 구간에서 약 300,000 스냅샷 수신 (정상)
실측 결과: 바이낸스 BTCUSDT의 book_snapshot_25는 24시간 평균 약 1.71억 행을 생성합니다. 1시간 구간 다운로드 시 평균 7.2MB gzip 압축, 평균 지연 1.84초(서울 리전 기준), p99 지연 4.10초입니다.
VectorBT로 OFI 팩터 백테스트 — 완전 동작 코드
오더플로 불균형(Order Flow Imbalance, OFI)은 Cont(2014)의 학술 팩터입니다. 매수/매도 측 호가 변동의 비대칭을 측정합니다.
import numpy as np
import pandas as pd
import vectorbt as vbt
from tardis_dev import datasets # pip install tardis-dev
2단계: Tardis CSV 스트리밍 다운로드 (메모리 효율적)
client = datasets.CatalogClient(api_key=TARDIS_KEY)
df_iter = client.get_dataset(
"binance-futures.book_snapshot_25",
symbols=["BTCUSDT"],
from_date="2024-01-15",
to_date="2024-01-15",
data_format="csv"
)
3단계: 오더북 스냅샷 → OFI 계산
records = []
for chunk in df_iter:
for _, row in chunk.iterrows():
b = row["bids"] # [[price, size], ...]
a = row["asks"]
# 최우선 호가 기준 OFI
bid_change = b[0][1] - row["prev_bid_size"]
ask_change = row["prev_ask_size"] - a[0][1]
ofi = bid_change - ask_change
records.append({
"ts": row["timestamp"],
"mid": (b[0][0] + a[0][0]) / 2,
"ofi": ofi,
"spread": a[0][0] - b[0][0],
})
raw = pd.DataFrame(records).set_index("ts")
print(f"OFI 샘플 수: {len(raw):,}")
출력 예: OFI 샘플 수: 1,247,832
4단계: 1초 바 집계 후 z-score 정규화
ofi_1s = raw["ofi"].resample("1s").sum().dropna()
z_ofi = (ofi_1s - ofi_1s.rolling(300).mean()) / ofi_1s.rolling(300).std()
5단계: 미래 1초 수익률 매핑
fwd_ret = raw["mid"].resample("1s").last().pct_change().shift(-1)
signal = np.sign(z_ofi).clip(-1, 1)