DEV Community

shakti tiwari
shakti tiwari

Posted on • Originally published at optiontradingwithai.in

KI-gestützter Optionenhandel auf den DAX 40 — Architektur, Greeks und Backtest (Python)

KI Optionenhandel DAX 40

Kurzantwort: Ein KI-gestützter Optionshandels-Bot für den DAX 40 kombiniert drei Schichten — (1) eine Feature-Pipeline aus Optionsschein- und Volatilitätsdaten, (2) einen Gradient-Boosting-Klassifikator für die Richtungswahrscheinlichkeit und (3) ein Backtest-Framework mit Greeks-basierten Risikogrenzen. Das Modell setzt kein Geld, es liefert eine Wahrscheinlichkeit, die ein Regelwerk überlagert.

Dieser Leitfaden ist für Retail-Quants geschrieben, die Python beherrschen und den deutschen Markt (Frankfurter Börse, EU-Regulierung) adressieren.

Bildungzwecke. Keine Anlageberatung. Optionen bergen das Risiko des totalen Verlusts. Konsultieren Sie BaFin und einen lizenzierten Berater.

Warum DAX 40 Optionen ein guter KI-Zielmarkt sind

Der DAX 40 ist der führende deutsche Leitindex. Seine Optionen (an Eurex gehandelt) bieten:

  • Hohe Liquidität in den Hauptläufen → enge Spreads, saubere Labels.
  • EU-Regulierung (MiFID II, BaFin) → transparente Kostenoffenlegung.
  • Volatilitätsindex VDAX → eigenes Regime-Signal (Analog zu VIX).

Architektur (drei Schichten)

1. Daten/Feature-Pipeline → Optionskette, IV, PCR, VDAX
2. Modell (Gradient Boosting) → P(Richtung | Features)
3. Regeln + Greeks-Engine → Positionsgröße, Stopp, DTE-Limit
Enter fullscreen mode Exit fullscreen mode

Schicht 1 — Features (Python)

# Mac / Linux / Termux
python3 features.py
# Windows CMD
py features.py
Enter fullscreen mode Exit fullscreen mode
import pandas as pd, numpy as np

def build_features(chain: pd.DataFrame, vdax: float, pcr: float) -> pd.DataFrame:
    df = chain.copy()
    df["mid"] = (df["bid"] + df["ask"]) / 2.0
    df["spread_pct"] = (df["ask"] - df["bid"]) / df["mid"].clip(lower=1e-9)
    df["moneyness"] = df["strike"] / df["spot"] - 1.0
    atm_iv = df.loc[(df["moneyness"].abs()).idxmin(), "iv"]
    df["iv_skew"] = df["iv"] - atm_iv
    df["vdax"] = vdax
    df["pcr"] = pcr
    df["theta_per_delta"] = df["theta"] / df["delta"].clip(lower=1e-9)
    return df

if __name__ == "__main__":
    demo = pd.DataFrame([{"strike": 18500, "bid": 9.0, "ask": 9.4, "iv": 0.17,
        "delta": 0.50, "gamma": 0.002, "theta": -1.0, "vega": 3.8,
        "oi": 70000, "volume": 4000, "spot": 18450, "dte": 8}])
    f = build_features(demo, vdax=18.2, pcr=0.88)
    print(f[["mid","spread_pct","moneyness","iv_skew","theta_per_delta"]].to_string())
Enter fullscreen mode Exit fullscreen mode

Schicht 2 — Modell (HistGradientBoosting)

# Mac / Linux / Termux
python3 train.py
# Windows CMD
py train.py
Enter fullscreen mode Exit fullscreen mode
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import TimeSeriesSplit, roc_auc_score
import pandas as pd

FEATURES = ["spread_pct","moneyness","iv_skew","vdax","pcr",
            "theta_per_delta","gamma","vega","dte","oi","volume"]

def train(X: pd.DataFrame, y: pd.Series):
    tscv = TimeSeriesSplit(n_splits=5)
    model = HistGradientBoostingClassifier(max_depth=4, learning_rate=0.05, max_iter=300)
    for tr, te in tscv.split(X):
        model.fit(X.iloc[tr], y.iloc[tr])
        pred = model.predict_proba(X.iloc[te])[:, 1]
        print("fold AUC:", round(roc_auc_score(y.iloc[te], pred), 3))
    model.fit(X, y)
    return model
Enter fullscreen mode Exit fullscreen mode

Zeitreihen-Split, nie zufällig mischen — sonst inflierte AUC.

Schicht 3 — Greeks-Regeln

def decide(prob_up, greeks, max_capital, risk_per_trade=0.01):
    if not (0.58 <= prob_up <= 0.80):
        return {}
    if greeks["dte"] <= 1:
        return {}
    if abs(greeks["vega"]) > 8.0:
        return {}
    size = (max_capital * risk_per_trade) / max(greeks["theta"], 1e-6)
    return {"action": "paper_entry", "size": round(size, 2),
            "stop_theta": greeks["theta"] * 2.5}
Enter fullscreen mode Exit fullscreen mode

Backtest (pandas)

def backtest(signals: pd.DataFrame, fees_bps=2.0) -> float:
    s = signals.copy()
    s["position"] = ((s["prob_up"] >= 0.60) & (s["dte"] > 1)).astype(int)
    s["pnl"] = s["position"] * (s["delta"] * s["spot_ret"] * 100
                                - s["theta"] + s["prob_up"] - 0.5)
    s["pnl"] -= (s["position"] * fees_bps / 10000.0)
    return s["pnl"].sum()
Enter fullscreen mode Exit fullscreen mode

VDAX-Regime-Filter

  • VDAX < 18: länger laufende Strukturen bevorzugen.
  • VDAX 18–28: Basis-Modus.
  • VDAX > 28: Größe halbieren, Wahrscheinlichkeitsband verbreitern.

Datenquellen in Deutschland (Eurex, BaFin-Meldungen)

Für ein glaubwürdiges Modell brauchen Sie stabile Datenquellen. Der DAX-Optionsmarkt ist an Eurex notiert; viele Broker (TradeRepublic, Scalable, ING) leiten Eurex-Preise weiter. Wichtige Quellen:

  • Eurex Market Data: Ketten-Snapshots, IV-Oberfläche, Open Interest.
  • VDAX-New: der volatilitätsgewichtete Index der DAX-Optionen — unser Regime-Signal.
  • BaFin-Publikationen: Meldepflichten und Produktinnovationen, relevant für Compliance.
  • Terminstruktur des VDAX: nicht nur der Spot-VDAX, sondern die 1M/3M/6M-Termstruktur gibt den "Volatilitäts-Roll" voraus.

Ein robustes Feature ist die VDAX-Termstruktur-Steigung: slope = VDAX_1M - VDAX_3M. Negativ (Contango) = ruhiges Regime; positiv (Backwardation) = Stress. Dieses einzelne Feature verbessert die AUC oft mehr als drei Preis-Features.

# Mac / Linux / Termux
python3 termstructure.py
# Windows CMD
py termstructure.py
Enter fullscreen mode Exit fullscreen mode
def vdax_slope(vdax_1m: float, vdax_3m: float) -> float:
    """Negativ = Contango (ruhig), positiv = Backwardation (Stress)."""
    return round(vdax_1m - vdax_3m, 3)

if __name__ == "__main__":
    print("Contango :", vdax_slope(16.5, 18.2))   # -1.7 ruhig
    print("Stress   :", vdax_slope(28.0, 22.0))   # +6.0 Stress
Enter fullscreen mode Exit fullscreen mode

Volatilitätsmodellierung mit VDAX-Termstruktur

Die meisten Einsteiger modellieren nur die Spot-IV. Das greift zu kurz: eine steile Backwardation warnt vor einem Volatilitäts-Schock, der Ihre Theta-Akkumulation zunichte macht. Unser Regelwerk erweitert sich um eine Slope-Bedingung:

def decide(prob_up, greeks, vdax_slope_val, max_capital, risk_per_trade=0.01):
    if not (0.58 <= prob_up <= 0.80):
        return {}
    if greeks["dte"] <= 1:
        return {}
    if abs(greeks["vega"]) > 8.0:
        return {}
    if vdax_slope_val > 4.0:          # Backwardation-Stress
        risk_per_trade *= 0.5         # Größe halbieren
    size = (max_capital * risk_per_trade) / max(greeks["theta"], 1e-6)
    return {"action": "paper_entry", "size": round(size, 2),
            "stop_theta": greeks["theta"] * 2.5}
Enter fullscreen mode Exit fullscreen mode

Durchgerechnetes Beispiel (DAX 18500, DTE 8)

Angenommen das Modell liefert prob_up = 0.66, Greeks delta=0.50, theta=-1.0, vega=3.8, VDAX-Slope -1.7 (Contango). Kapital 10.000 €, Risiko 1 %:

  1. risk_per_trade = 0.01 (kein Stress-Abschlag).
  2. size = (10000 * 0.01) / max(1.0, 1e-6) = 100.
  3. Stop bei theta * 2.5 = -2.5 (Maximalverlust-Budget pro Kontrakt).
  4. Position nur, wenn dte > 1 und vega ≤ 8 — beides erfüllt.
  5. Ergebnis: paper_entry, Größe 100 € Budget, Stop bei −2,5 θ.

Dieses Beispiel zeigt: das Modell sagt nur die Richtung, das Regelwerk übersetzt sie in eine messbare, begrenzte Risikoposition.

Walk-Forward Evaluation (not just train/test)

A single TimeSeriesSplit is honest, but a production system needs walk-forward: retrain on a rolling window, test on the next, slide forward. This catches the "model decayed" failure that static splits hide.

# Mac / Linux / Termux
python3 walkforward.py
# Windows CMD
py walkforward.py
Enter fullscreen mode Exit fullscreen mode
from sklearn.model_selection import TimeSeriesSplit
import pandas as pd, numpy as np

def walk_forward(X, y, n_splits=10, train_size=300, test_size=60):
    aucs = []
    for start in range(0, len(X) - train_size - test_size, test_size):
        tr = slice(start, start + train_size)
        te = slice(start + train_size, start + train_size + test_size)
        # train + eval placeholder; plug your model here
        aucs.append(0.0)  # replace with real roc_auc_score
    return np.mean(aucs)

# Real use: fit HistGradientBoostingClassifier on X.iloc[tr], score on X.iloc[te]
Enter fullscreen mode Exit fullscreen mode

The point is the loop shape: never let the test window touch training data, and slide by exactly the test size so windows are contiguous and non-overlapping.

Feature Importance (what actually drives the signal)

After training, inspect which features the model leans on. On options data the ranking is usually:

  1. theta_per_delta -- decay cost vs directional exposure.
  2. iv_skew -- cheapness of the strike relative to ATM.
  3. moneyness -- direction of the strike vs spot.
  4. vix/vdax/jvx -- regime context.
  5. pcr -- sentiment extreme.

If your model ranks oi or volume first, suspect leakage: those are post-hoc liquidity, not predictive of next-window mid move. Drop them from features and re-check.

Deployment Checklist

Before any paper trade:

  • [ ] TimeSeriesSplit AUC printed, not random-split.
  • [ ] Walk-forward mean AUC stable across windows.
  • [ ] Feature importance sane (no leakage features ranked top).
  • [ ] Rules engine hard limits active (dte, vega, prob band).
  • [ ] Backtest includes fees and theta accrual.
  • [ ] Position size calculator wired to the rules layer.
  • [ ] Canonical URL and disclaimers present in published version.

    Glossary (terms the model relies on)

  • Delta: directional exposure of the option per 1 unit of underlying move.

  • Gamma: rate of change of delta; high gamma = convex PnL, fast risk shift.

  • Theta: daily time decay; the cost you pay for holding.

  • Vega: sensitivity to implied-volatility moves; the dominant risk in stress.

  • IV skew: difference between a strike's IV and ATM IV; a cheapness signal.

  • PCR: put-call ratio; a sentiment extreme indicator when far from 1.0.

  • DTE: days to expiry; the hard stop before assignment/gamma risk.

  • Moneyness: strike divided by spot minus one; negative = ITM, positive = OTM.

Understanding these is what separates a backtest that looks good from one that survives live. The rules engine exists precisely because no single Greek is safe alone.

Häufige Fehler

  1. Zufälliger Train/Test-Split bei Zeitreihen.
  2. Bid/Ask-Spread ignorieren.
  3. Nackte Short-Optionen aus "hoher Wahrscheinlichkeit".
  4. Overfitting auf IV-Skew in einem Regime.
  5. Keine Positionsgröße.

Wöchentlicher Rhythmus

  • Mo: Features neu aufbauen, Retrain bei AUC-Drift > 3 %.
  • Di–Do: Paper-Trade, Fills vs. Vorhersage loggen.
  • Fr: False Positives reviewen, Regeln festigen.

FAQ

F1. Brauche ich ein neuronales Netz für DAX-Optionen?
Nein. Gradient-Boosting auf guten Features schlägt Netze auf Tabellendaten meist und ist auditierbarer.

F2. Ist das unter BaFin/MiFID II erlaubt?
Eigenes Modell und Paper-Trading sind erlaubt. Live-Automatisierung unterliegt Broker-Prüfung. Konsultieren Sie einen Compliance-Experten.

F3. Wie viel Kapital pro Trade?
≤ 1 % des Kapitals, über Greeks skaliert. Nie mehr riskieren als verlustfähig.

F4. Kann ich das auf dem Handy (Termux) laufen lassen?
Ja. Reine Python/pandas-Pipeline läuft auf Termux oder Raspberry Pi.

F5. Was ist der größte Hebel — Modell oder Risiko?
Die Risiko-Schicht. Ein mittelmäßiges Modell mit strengen Greek-Limits überlebt; ein gutes ohne Limits nicht.

Footer

Shakti Tiwari — Options Trader, XGBoost Expert.
Books: Option Trading with AI (B0H9ZNTBPK) · The AI Opportunity (B0HBBFKDQF)
Site: optiontradingwithai.in · Free help: shaktitiwari715@gmail.com
Dev.to: @shaktitiwari · X: @shaktitiwari

Top comments (0)