Walk-Forward-Validierung für ML-Trading.
Wer ein ML-Modell im Trading mit klassischem k-fold-Cross-Validation evaluiert, betrügt sich selbst. Zeitreihen brauchen eine eigene Methodik: Walk-Forward. Richtig gemacht ist sie der ehrlichste Stresstest, den ein Modell durchlaufen kann. Falsch gemacht reproduziert sie genau dieselben Bias-Probleme, die sie verhindern soll.
Warum k-fold im Trading falsch ist.
Bei k-fold-CV werden Beobachtungen zufällig in Folds aufgeteilt. Trainings- und Validierungs-Beobachtungen liegen zeitlich vermischt vor. Damit lernt das Modell implizit Zustände der Zukunft, die es nutzt, um Vergangenheit vorherzusagen — und die Performance sieht großartig aus, bis es live geht.
Selbst TimeSeriesSplit von scikit-learn ist nur eine erste Annäherung. Es schützt vor Zukunft im Training, lässt aber andere Probleme offen: feature-engineering mit gloablen Statistiken, scaler-fit auf der gesamten Reihe, hyperparameter-tuning ohne harten Out-of-Sample-Teil.
Die drei Walk-Forward-Varianten.
- Expanding-Window: Trainingsfenster wächst, Validierung gleitet. Mehr Daten über die Zeit — gut bei langsam wechselnden Mustern.
- Rolling-Window: Trainingsfenster bleibt konstant gross, beide gleiten. Besser bei Regime-Wechseln.
- Anchored-Walk-Forward: fester Startpunkt, regelmäßig neu trainiert. Pragmatischer Standard in der Praxis.
Welche Variante richtig ist, hängt vom Markt ab. Für trendstabile Assets mit langen Regimen ist Expanding oft besser. Für regimewechselanfällige Märkte — Rolling. Wer unsicher ist, testet beide und vergleicht die Streuung der Out-of- Sample-Performance.
Implementierung in Python.
import numpy as np
import pandas as pd
def walk_forward_splits(n, train_size, val_size, step, mode="rolling"):
splits = []
start = 0
while True:
train_end = start + train_size
val_end = train_end + val_size
if val_end > n:
break
train_idx = np.arange(0 if mode == "expanding" else start, train_end)
val_idx = np.arange(train_end, val_end)
splits.append((train_idx, val_idx))
start += step
return splits
def walk_forward_eval(X, y, model_factory, train_size, val_size, step):
splits = walk_forward_splits(len(X), train_size, val_size, step)
metrics = []
for tr, va in splits:
model = model_factory()
# WICHTIG: Scaler/Feature-Pipelines nur auf tr fitten
model.fit(X.iloc[tr], y.iloc[tr])
pred = model.predict(X.iloc[va])
metrics.append({
"start": X.index[va[0]],
"end": X.index[va[-1]],
"ic": np.corrcoef(pred, y.iloc[va])[0, 1],
})
return pd.DataFrame(metrics)
Embargo und Purging.
Wenn die Zielvariable aus mehreren Tagen aggregiert ist — etwa 5-Tages-Forward- Return — überlappen Trainings- und Validierungs-Labels. Ein Beispiel: ein Sample am letzten Trainings-Tag enthält implizit das Label der nächsten fünf Tage, also der ersten Tage der Validierung. Das ist subtiler Leakage.
Die Lösung: Purging und Embargo. Marcos López de Prado hat das in "Advances in Financial Machine Learning" formal beschrieben.
- Purging: Entferne Trainings-Samples, deren Label-Fenster in die Validierung hineinreicht.
- Embargo: Lasse zusätzlich eine kleine Lücke zwischen Training und Validierung, um Auto-Korrelation in den Features zu neutralisieren.
def purged_split(train_idx, val_idx, label_horizon, embargo):
val_start = val_idx[0]
val_end = val_idx[-1]
# Purging: Trainings-Samples raus, deren Label in Validierung reichen
keep = train_idx[(train_idx + label_horizon) < val_start]
# Embargo: zusaetzliche Pufferzone
keep = keep[keep < val_start - embargo]
return keep, val_idx
Hyperparameter-Tuning ohne Selbstbetrug.
Wer 200 Hyperparameter-Kombinationen über Walk-Forward laufen lässt und die beste auswählt, hat aus dem Walk-Forward einen verkleideten In-Sample-Test gemacht. Die korrekte Architektur:
- Walk-Forward innerhalb des Trainings-Universums für Hyperparameter-Wahl.
- Einen harten, separaten Out-of-Sample-Block am Ende, der nie für Tuning angefasst wird.
- Endgültige Performance wird ausschließlich auf diesem letzten Block berichtet.
Wenn der Out-of-Sample-Block deutlich schlechter aussieht als der Walk-Forward- Mittelwert, haben Sie überoptimiert. Lieber jetzt feststellen als im Live-Betrieb.
Was Sie aus den Walk-Forward-Ergebnissen lesen.
Nicht nur der Mittelwert der Out-of-Sample-Metriken zählt. Mindestens genauso wichtig:
- Streuung über Folds: hohe Varianz heißt instabiles Modell — selbst bei gutem Mittel.
- Stationarität: wird die Performance über die Zeit schlechter? Hinweis auf Regime-Wechsel oder Konzept-Drift.
- Bedingte Performance: wie schlägt sich das Modell in Hochvola-Phasen vs. Niedrigvola? Wenn es nur in einem Regime funktioniert, brauchen Sie einen Regime-Filter.
- Decay des Edge: in vielen ML-Trading-Setups schmilzt der Vorteil über die Zeit. Periodisches Retraining ist Pflicht.
Retraining-Frequenz: wie oft ist sinnvoll?
Zu seltenes Retraining heißt veraltete Modelle. Zu häufiges Retraining heißt Overfitting auf jüngste Daten und höheren Operationsaufwand. Praktische Faustregel: Tagesbar-Modelle alle 4–12 Wochen, Intraday-Modelle alle 1–4 Wochen, hochfrequente Modelle täglich oder kontinuierlich. Die genaue Frequenz lässt sich empirisch ermitteln, indem man im Walk-Forward verschiedene Retraining-Intervalle vergleicht.
Sie wollen Ihr ML-Trading-Setup an einer realistischen Walk-Forward-Pipeline validieren? Erstgespräch buchen — wir bauen die Validierung so auf, dass das Modell hält, was der Backtest verspricht.