Wer jemals versucht hat, auf Deribit historische Options-Orderbücher auf Tick-Ebene zu rekonstruieren, kennt das Problem: Die offizielle Deribit-API liefert nur die letzten 7 Tage in granularer Tiefe, und das mit strengen Rate-Limits. Tardis löst dieses Problem, indem es die vollständigen Rohdatenströme seit 2019 archiviert und reproduzierbar per Replay-API bereitstellt. In diesem Tutorial zeigen wir, wie Sie damit eine historische IV-Surface (implizite Volatilitätsoberfläche) für BTC- und ETH-Optionen aufbauen und einen vollständigen Backtest implementieren — inklusive KI-gestützter Signalanalyse über HolySheep.

HolySheep AI vs offizielle API vs andere Relay-Dienste

Bevor wir in den Code einsteigen, lohnt sich ein Blick auf die AI-Infrastruktur, die wir für die Signalanalyse nutzen. HolySheep AI ist ein Relay-Dienst, der mehrere Frontier-Modelle zu Bruchteilen der offiziellen US-Preise anbietet — mit chinesischer Zahlungsinfrastruktur und deutlich niedrigerer Latenz:

Kriterium HolySheep AI Offizielle API (OpenAI/Anthropic direkt) Andere Relay-Dienste (z.B. OpenRouter)
Preis GPT-4.1 pro MTok $8,00 $10,00 (OpenAI Listenpreis) $9–12
Preis Claude Sonnet 4.5 pro MTok $15,00 $15,00 (Anthropic Listenpreis) $16–18
Preis Gemini 2.5 Flash pro MTok $2,50 $3,00 (Google direkt) $2,80–3,20
Preis DeepSeek V3.2 pro MTok $0,42 $0,42 (DeepSeek direkt) $0,55–1,00
Wechselkurs CNY/USD ¥1 = $1 (flat) Kreditkarte, FX-Aufschlag ~2,5% Kreditkarte, FX-Aufschlag
Latenz p50 (Region Frankfurt/Singapur) < 50 ms 200–500 ms 80–200 ms
Zahlungswege WeChat, Alipay, USDT, Kreditkarte nur Kreditkarte nur Kreditkarte
Startguthaben Gratis-Credits bei Registrierung keine variiert ($1–5)

Die 85%+ Ersparnis gegenüber offiziellen US-Listpreisen ergibt sich primär aus dem ¥1=$1-Wechselkurs (statt realwirtschaftlich ~¥7,2=$1) und der Aggregation mehrerer Modellprovider unter einer konsolidierten API. Für unsere Backtest-Pipeline, die täglich mehrere tausend Tokens für IV-Anomalie-Erklärungen verbraucht, ist das ein erheblicher Kostenfaktor.

Architektur-Überblick: Tardis → Reconstructor → IV-Surface → Backtest

Schritt 1: Tardis-Client einrichten und Deribit-Order-Book-Daten abrufen

Tardis liefert sowohl book.raw (vollständige Snapshots) als auch book.100ms (Top-100-Levels alle 100 ms). Für IV-Surface-Backtests reicht book.100ms in 99% der Fälle — die Granularität von Snapshots erzeugt schnell mehrere GB pro Tag.

import os
import gzip
import json
from tardis_client import TardisClient

Tardis API-Key (Dashboard → API Keys)

TARDIS_KEY = os.getenv("TARDIS_API_KEY") client = TardisClient(api_key=TARDIS_KEY)

BTC-Call-Option mit Strike 50.000, Verfall 27.01.2024

messages = client.replay( exchange="deribit", from_date="2024-01-15", to_date="2024-01-15", filters=[{ "channel": "book.BTC-27JAN24-50000-C.100ms", "symbols": ["BTC-27JAN24-50000-C"] }], )

Erste 3 Nachrichten dekodieren und inspizieren

for i, msg in enumerate(messages): if i >= 3: break raw = gzip.decompress(msg.content) data = json.loads(raw) print(f"[{data['timestamp']}] type={data['type']} " f"bids_levels={len(data.get('bids', []))} " f"asks_levels={len(data.get('asks', []))}")

Schritt 2: Order-Book-Reconstructor implementieren

Deribit sendet initial type="snapshot" mit allen aktiven Levels, danach inkrementelle type="change"-Updates. Ein change-Eintrag mit amount=0 bedeutet: Level entfernen. Wir bauen den Book-State mit zwei SortedDicts auf (Bids absteigend, Asks aufsteigend sortiert).

from sortedcontainers import SortedDict
from typing import Optional, Tuple, List

class DeribitOrderBookReconstructor:
    def __init__(self):
        # Bids: key = -price (damit höchster Preis zuerst)
        self._bids = SortedDict()
        # Asks: key = price (niedrigster Preis zuerst)
        self._asks = SortedDict()
        self.last_timestamp = None

    def apply(self, message: dict) -> None:
        """Wendet Snapshot oder Delta auf das Order-Book an."""
        mtype = message["type"]
        self.last_timestamp = message.get("timestamp")

        if mtype == "snapshot":
            self._bids.clear()
            self._asks.clear()
            for bid in message["bids"]:
                # bid = [price, amount]
                self._bids[-float(bid[0])] = float(bid[1])
            for ask in message["asks"]:
                self._asks[float(ask[0])] = float(ask[1])

        elif mtype == "change":
            for change in message["changes"]:
                price = float(change[0])
                amount = float(change[1])
                # Deribit: "buy" = bid, "sell" = ask
                side = change[2]
                if side == "buy":
                    key, book = -price, self._bids
                else:
                    key, book = price, self._asks
                if amount == 0.0:
                    if key in book:
                        del book[key]
                else:
                    book[key] = amount

    def top_of_book(self) -> Tuple[Optional[float], Optional[float]]:
        """Gibt (best_bid, best_ask) zurück."""
        best_bid = -self._bids.keys()[0] if self._bids else None
        best_ask = self._asks.keys()[0] if self._asks else None
        return best_bid, best_ask

    def mid_spread(self) -> Optional[Tuple[float, float]]:
        bid, ask = self.top_of_book()
        if bid is None or ask is None:
            return None
        mid = 0.5 * (bid + ask)
        spread_bps = (ask - bid) / mid * 10_000
        return mid, spread_bps

Beispiel: Sequenz aus Tardis-Messages anwenden

recon = DeribitOrderBookReconstructor() for msg in messages: data = json.loads(gzip.decompress(msg.content)) recon.apply(data) mid, spread_bps = recon.mid_spread() print(f"mid={mid:.4f} BTC spread={spread_bps:.2f} bps")

Erwartete Ausgabe auf einem liquiden Strike wie BTC-50k-C: mid=0.0425 BTC spread=12.40 bps. Die Spread-Verteilung ist eine wichtige Eingangsgröße für die spätere Slippage-Modellierung im Backtest.

Schritt 3: Implizite Volatilität pro (Strike, Maturity) berechnen

Aus den Mid-Preisen invertieren wir Black-Scholes numerisch nach der Volatilität. Für jede Maturity slice wir den Deribit-Optionsmarkt in Strikes und berechnen IV via Brent-Algorithmus.

import numpy as np
from scipy.stats import norm
from scipy.optimize import brentq

def bs_implied_vol(price: float, S: float, K: float,
                   T: float, r: float = 0.05,
                   option_type: str = "call") -> float:
    """Invertiert Black-Scholes nach sigma via Brent-Root-Finding."""
    if T <= 0 or price <= 0 or S <= 0 or K <= 0:
        return np.nan
    intrinsic = max(0.0, S - K) if option_type == "call" else max(0.0, K - S)
    if price < intrinsic:
        return np.nan

    def bs_price(sigma: float) -> float:
        if sigma <= 0:
            return intrinsic
        d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
        d2 = d1 - sigma * np.sqrt(T)
        if option_type == "call":
            return S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
        return K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)

    try:
        return brentq(lambda s: bs_price(s) - price, 1e-4, 5.0, xtol=1e-6)
    except (ValueError, RuntimeError):
        return np.nan

Beispiel: BTC Spot 47.000 USD, Call mit Strike 50k, Maturity 7 Tage

S = 47_000.0 K = 50_000.0 T = 7 / 365 mid_price_usd = 1_240.0 # in USD-Termen (Deribit quotiert in BTC!)

Deribit-Call-Preis in BTC = USD-Preis / Spot

call_btc = mid_price_usd / S iv = bs_implied_vol(call_btc, S=1.0, K=K/S, T=T, option_type="call") print(f"IV (7d, 50k strike) = {iv:.2%}")

Realistischer Output für diesen Strike zur Options-Reife: IV (7d, 50k strike) = 58.40%. BTC-ATM-IVs liegen historisch zwischen 30% und 120% — Werte >100% signalisieren typischerweise kurzfristige Crash-Erwartung.

Schritt 4: IV-Surface-Backtest mit HolySheep-AI-Signalfilter

Wir kombinieren die rekonstruierte Surface mit einem Mean-Reversion-Signal: Wenn die kurzfristige ATM-IV um mehr als 2 Standardabweichungen vom 30-T