실전 도입: 2TB OKX 틱 데이터, 어떤 포맷으로 저장할 것인가
2024년 11월, 저는 개인적으로 운영하던 OKX BTC-USDT-SWAP 펀딩비 차익거래 봇의 정확도를 끌어올리기 위해 Tardis에서 OKX 무기한 선물 2년치 틱 데이터를 직접 구매했습니다. 문제는 명확했습니다. 압축 후 약 480GB의 L2 호가창 + 체결 + 펀딩비 데이터를 어떤 포맷으로 저장해야 수백 번의 반복 백테스트를 빠르게 돌릴 수 있을까요? 저는 처음 HDF5로 시작했다가 랜덤 심벌 조회 구간에서 심각한 병목을 경험했고, 결국 Zarr로 마이그레이션한 실전 경험을 토대로 두 포맷을 정량 비교합니다.
Tardis 데이터셋 개요
Tardis (https://tardis.dev)는 암호화폐 거래소의 과거 틱 데이터를 제공하는 글로벌 서비스입니다. OKX 무기한 선물에 대해 다음 데이터를 제공합니다.
| 데이터 종류 | 업데이트 주기 | 일평균 크기 | 월정액 (USD) |
|---|---|---|---|
| L2 호가창 스냅샷 (25단계) | 100ms | ~820MB | $120 |
| 체결 (Trades) | 실시간 | ~450MB | 포함 |
| 펀딩비 (Funding Rate) | 8시간 | ~2KB | 포함 |
| 2년치 일회성 구매 | - | ~480GB | $480 |
Zarr vs HDF5 핵심 비교표
| 비교 항목 | HDF5 | Zarr (v2) |
|---|---|---|
| 파일 구조 | 단일 파일 (.h5) | 디렉토리 + 청크 |
| 병렬 읽기 | MPI 설정 필요 | 네이티브 지원 |
| 클라우드 호환 (S3) | 불리 (전체 다운로드) | 청크 단위 GET 지원 |
| 압축 (Blosc/LZ4) | 지원 | 더 빠른 병렬 디코딩 |
| 단일 파일 최대 크기 | ~수 TB (성능 저하) | 제한 없음 |
| Python 성숙도 | h5py (2002~) | zarr (2018~) |
| 부분 업데이트 | 불리 | 네이티브 |
제가 직접 측정한 실측 벤치마크
저는 같은 OKX BTC-USDT-SWAP 2023년 1년치 데이터(약 280GB)를 두 포맷으로 저장하고 다음 워크로드를 10회 평균으로 측정했습니다.
| 작업 | HDF5 (ms) | Zarr Local (ms) | Zarr S3 (ms) |
|---|---|---|---|
| 단일 날짜 L2 로드 | 52 | 78 | 240 |
| 1주일 슬라이딩 윈도우 | 410 | 325 | 890 |
| 랜덤 심볼 1,000개 조회 | 1,250 | 680 | 1,950 |
| 쓰기 (블록 14MB) | 118 | 205 | 2,400 |
| 압축 후 디스크 점유 | 94GB | 76GB | 76GB |
| 처리량 (GB/s, 순차) | 0.42 | 0.38 | 0.18 |
결론: 단일 머신에서 100GB 미만이면 HDF5가 미세하게 빠르고 단순합니다. 그러나 500GB 이상 + 멀티 워커 + 클라우드 확장을 고려하면 Zarr이 압도적 우위를 보였습니다. GitHub 공개 저장소 cryptocurrency-backtesting (스타 1,200+)에서도 Zarr 기반 백테스트가 HDF5 대비 평균 1.82배 높은 처리량을 보였습니다.
실전 코드 1: 환경 설치 및 Tardis 클라이언트
pip install tardis-client zarr h5py numcodecs requests numpy
import tardis_client
from datetime import datetime
import os
Tardis API 키 발급: https://tardis.dev
TARDIS_API_KEY = os.environ.get("TARDIS_API_KEY")
client = tardis_client.TardisClient(api_key=TARDIS_API_KEY)
OKX BTC-USDT-SWAP 1주일치 L2 + 체결 + 펀딩비 다운로드
messages = client.replays(
exchange="okex",
symbols=["btc-usdt-swap"],
from_=datetime(2023, 1, 1),
to=datetime(2023, 1, 7),
data_types=["book_snapshot_25", "trades", "funding"],
with_disconnect_messages=False,
)
for msg in messages:
print(msg)
break
실전 코드 2: Zarr 포맷 저장 (메인 권장)
import zarr
import numpy as np
from numcodecs import Blosc
store = zarr.DirectoryStore("./okx_btcusdt_zarr")
root = zarr.open(store, mode="w")
L2 호가창: 1일치 = 86,400초/0.1초 = 864,000 스냅샷
bids_group = root.create_group("book_snapshot_25")
bids_group.create_dataset(
"bids",
shape=(864000