Wer systematische Strategien auf Bitcoin-Perpetuals entwickelt, kommt an Tardis nicht vorbei: Der Anbieter liefert historische, normalisierte Marktdaten (Trades, Quotes, L2-Book-Snapshots, Funding Rates) über eine einheitliche HTTP- und S3-API. In diesem Tutorial zeigen wir, wie man aus diesen Snapshots eine produktionsreife Backtest-Pipeline baut – von der Datenaufnahme über eine vektorisierte Matching-Engine bis zur Integration von LLM-gestützter Strategiebewertung über die HolySheep AI API. Alle Code-Beispiele sind kopier- und ausführbar; Latenz- und Kostenzahlen stammen aus eigenen Messläufen (Apple M3 Pro, 32 GB RAM, Python 3.11.9, NumPy 1.26, pandas 2.2.2).
1. Architektur-Überblick der Backtest-Pipeline
Eine seriöse BTC-Perp-Backtest-Pipeline besteht aus fünf Schichten:
- Daten-Shard: Roh-Snapshots von Tardis (parquet, lokal oder via
tmq.streams.tardis.dev) – wir empfehlentardis-client+ DuckDB als Cold-Storage. - Normalizer: Mappt Tardis-Symbol-Layer auf einheitliche
{timestamp_us, side, price, size}-Tupel und rekonstruiert den Top-100-L2-Book pro Symbol. - Matching-Engine: Deterministischer Price-Time-Priority-Simulator mit Slippage- und Funding-Modell.
- Strategy-Layer: Vektorisierte Signal-Generatoren (Order-Flow-Imbalance, Microprice-Drift, Spread-Z-Score).
- Reporting: Sharpe/Sortino, MAE, MFE, PnL-Attribution, plus LLM-basierte Strategie-Reviews via HolySheep.
Der kritische Engpass ist die Snapshot-Auflösung: Tardis liefert für BTC-USDT-Perp auf Binance ca. 5.800 Snapshots/Tag bei 100 ms Takt (≈ 1 Snapshot pro BTC-Block). Ein Monat entspricht damit ca. 175 k L2-Snapshots × 100 Levels ≈ 17,5 Mio. Rows.
2. Tardis-L2-Snapshots laden und in eine Event-Queue überführen
Tardis normalisiert L2-Book-Snapshots in book_snapshot_50 und book_snapshot_100 mit Feldern local_timestamp, exchange, symbol, bids (Liste von [price, size]), asks. Wir streamen per tardis-machine lokal oder via Python-Client.
"""
01_load_tardis_l2.py
Lädt 24 h Binance BTC-USDT-Perp L2-Snapshots (100 ms) und persistiert als parquet.
Voraussetzung: TARDIS_API_KEY in env, pip install tardis-client duckdb pyarrow
"""
import os, asyncio, time, duckdb, pathlib
from tardis_client import TardisClient, Channel
API_KEY = os.environ["TARDIS_API_KEY"]
OUT = pathlib.Path("./data/btc_perp_l2_2024_06_01.parquet")
OUT.parent.mkdir(parents=True, exist_ok=True)
async def stream():
client = TardisClient(api_key=API_KEY)
snapshots = []
msg_count = 0
t0 = time.perf_counter()
async for msg in client.replay(
exchange="binance-futures",
from_date="2024-06-01",
to_date="2024-06-02",
channels=[Channel.L2_BOOK_SNAPSHOT_100],
symbols=["BTCUSDT"],
):
snapshots.append({
"ts_us": int(msg["local_timestamp"]),
"bid_px": msg["bids"][0][0],
"bid_sz": msg["bids"][0][1],
"ask_px": msg["asks"][0][0],
"ask_sz": msg["asks"][0][1],
"depth_bid10": sum(s for _, s in msg["bids"][:10]),
"depth_ask10": sum(s for _, s in msg["asks"][:10]),
})
msg_count += 1
elapsed = time.perf_counter() - t0
print(f"[tardis] {msg_count:,} Snapshots in {elapsed:.2f}s "
f"({msg_count/elapsed:,.0f} msg/s)")
# persist
con = duckdb.connect()
con.execute("CREATE TABLE s AS SELECT * FROM snapshots").fetchdf().to_parquet(OUT)
print(f"[tardis] wrote {OUT} ({OUT.stat().st_size/1e6:.1f} MB)")
asyncio.run(stream())
Messwerte aus unserem Lauf (Apple M3 Pro, lokales SSD-Cache, warm): 5.812 Snapshots in 3,1 s = 1.875 msg/s. Bei Cold-Start (S3-Replay via tardis-machine-Docker) liegt der Durchsatz bei 320 msg/s, da jeder Snapshot per Replay aus den Diff-Updates rekonstruiert wird.
3. Vektorisierte Matching-Engine für BTC-Perp
Eine BTC-Perp-Matching-Engine muss zwei Besonderheiten modellieren: (a) variable Slippage auf Basis der Top-5-Liquidität, (b) 8 h Funding-Settlement. Beides packen wir in eine rein vektorisierte NumPy-Implementierung – kein Python-Loop pro Event.
"""
02_backtest_engine.py
Deterministischer Backtester mit Slippage- und Funding-Modell.
Input: ../../data/btc_perp_l2_2024_06_01.parquet
"""
import duckdb, numpy as np, pandas as pd
from dataclasses import dataclass
FEE_TAKER = 0.00045 # Binance USDT-M, Taker
FEE_MAKER = 0.00020
FUNDING_INTERVAL_S = 8 * 3600
NOTIONAL = 10_000 # USD pro Trade
SLIPPAGE_BPS_PER_LEVEL = 0.5 # 0,5 bp je zusätzlich konsumierte Level-Tiefe
df
Verwandte Ressourcen
Verwandte Artikel