Vereinheitlichte Validierungspipeline gegen Backtest-Overfitting
Einführung
Jeder algorithmische Trader stößt irgendwann auf einen Backtest, der zu gut aussieht, um wahr zu sein. Die Equity-Kurve gleicht einer nahezu perfekten Treppe, die zur oberen rechten Ecke des Diagramms ansteigt. Die Sharpe-Ratio ist außergewöhnlich. Drawdowns sind flach und kurz.
Die Strategie scheitert dann sofort, sobald sie live eingesetzt wird.
Dieses Ergebnis ist so verbreitet, dass sich in der quantitativen Forschungsgemeinschaft dafür sogar ein eigener Ausdruck eingebürgert hat. Der Übeltäter ist fast immer eine Form von Overfitting: Der Algorithmus hat das historische Rauschen eines spezifischen Datensatzes gelernt, anstatt eine dauerhafte, in die Zukunft übertragbare Marktstruktur. Weniger bekannt ist, dass Overfitting kein einzelnes Phänomen ist. Es tritt über verschiedene Kanäle auf, von denen jeder eine andere Gegenmaßnahme erfordert. Ein Anwender, der nur eine Schutzmaßnahme einsetzt – die gebräuchlichste ist eine einfache Train/Test-Aufteilung –, bleibt gegenüber den anderen weiterhin anfällig.
Dieser Artikel untersucht drei der rigorosesten Werkzeuge, die zur Bekämpfung von Overfitting bei der Entwicklung algorithmischer Strategien verfügbar sind: Validation-within-Validation (V-in-V), wie von Timothy Masters formuliert; Combinatorially Purged Cross-Validation (CPCV), entwickelt von Marcos Lopez de Prado; und Combinatorially Symmetric Cross-Validation (CSCV), eingeführt von Bailey und Lopez de Prado. Jedes adressiert eine eigene Fehlerquelle. Zusammen bilden sie eine umfassende Verteidigung gegen die folgenreichsten Formen der statistischen Selbsttäuschung in der quantitativen Forschung.

Abb. 1. Die vereinheitlichte Forschungspipeline. CPCV bereinigt zeitliche Leckagen innerhalb individueller Auswertungen; V-in-V steuert den Entwicklungsprozess; CSCV bietet eine abschließende quantitative Prüfung des Auswahlprozesses
Teil I: Das Spaghetti-an-die-Wand-Problem
Die Versuchung der Brute-Force-Methode
Der sogenannte Spaghetti-an-die-Wand-Ansatz bezieht sich auf die Praxis, eine enorme Anzahl von Indikatoren, Regeln und Parameterkombinationen anhand eines historischen Datensatzes zu testen, in der Hoffnung, dass etwas hängen bleibt. Moderne Rechenleistung macht diesen Ansatz trivial einfach in der Ausführung und katastrophal einfach, ihn falsch zu interpretieren.
Das Problem ist auch mathematischer Natur. Wenn Sie genügend Tests an einem beliebigen endlichen Datensatz durchführen, wird allein der Zufall Ergebnisse hervorbringen, die statistisch signifikant erscheinen. Dies ist kein Versagen von Intelligenz oder Absicht – es ist eine strukturelle Konsequenz wiederholter Hypothesentests. Jeder zusätzliche Test, der mit denselben Daten durchgeführt wird, erhöht die Wahrscheinlichkeit, dass der letztendliche Gewinner ein statistisches Artefakt und kein echter Marktvorteil ist.
Die primären Gefahren
Data Snooping Bias. Wenn dieselben historischen Daten wiederholt verwendet werden, um verschiedene Hypothesen zu testen, steigt mit jedem Test die Wahrscheinlichkeit, dass der letztlich bestabschneidende Kandidat lediglich ein Glückstreffer aus dem Zufall ist. Eine Strategie, die aus zehntausend Kandidaten auf demselben Datensatz ausgewählt wurde, hat ganz andere probabilistische Implikationen als eine, die aus nur fünf ausgewählt wurde.
Überanpassung an historische Daten. Ein Algorithmus, dem erlaubt wurde, frei über historische Daten zu optimieren, wird sich bis zu einem gewissen Grad an das spezifische konkrete Rauschmuster dieses bestimmten Preispfads anpassen. Er wird bei dem, was bereits geschehen ist, gut abschneiden, gerade weil er gelernt hat, bei dem, was bereits geschehen ist, gut abzuschneiden. Das Rauschen der Vergangenheit ist nicht das Signal der Zukunft.
Freiheitsgrade des Forschers. Selbst ein Forscher, der in gutem Glauben handelt, akkumuliert durch den gewöhnlichen Entwicklungsprozess Voreingenommenheit. Jede Modellierungsentscheidung, die auch nur indirekt durch die beobachtete Leistung auf Testdaten beeinflusst wird – Merkmalsauswahl, Parametergrenzen, Regelstruktur –, stellt einen Freiheitsgrad dar, der die scheinbare Signifikanz des Endergebnisses aufbläht. Diese Form der Kontamination ist in jeder einzelnen Entscheidung unsichtbar, wird aber in der Gesamtheit schwerwiegend.
Der „Spaghetti-an-die-Wand“-Ansatz ist nicht von Grund auf unzulässig. Eine breite explorative Suche kann echte Vorteile zutage fördern. Die entscheidende Frage ist, ob der Forscher Schutzmaßnahmen angewendet hat, die streng genug sind, um echte Vorteile von den statistischen Artefakten zu unterscheiden, die eine solche Suche zwangsläufig hervorbringt. Ohne alle drei hier beschriebenen Verteidigungsschichten kann er das nicht.
Teil II: Validation-within-Validation
Der verborgene Fehler bei Standard-Walkforward-Tests
Das Standard-Walkforward-Framework erscheint oberflächlich betrachtet als eine vollständige Lösung. Sie trainieren Ihre Strategie mit historischen Daten, testen sie in einem nachfolgenden Out-of-Sample-Zeitraum, erklären sie bei Erfolg für gültig und gehen zum Live-Einsatz über. Das Framework ist im Prinzip solide. Das Problem liegt in der Umsetzung.
Jedes Mal, wenn ein Forscher die Validierungsleistung beobachtet und diese Beobachtung nutzt, um eine Modellierungsentscheidung zu treffen – das Anpassen eines Parameters, das Reduzieren eines Merkmalsatzes, das Auswählen zwischen konkurrierenden Regelvarianten –, wird der Out-of-Sample-Zeitraum faktisch Teil des Trainingsdatensatzes. Der Entscheidungsprozess des Forschers hat auf diese Daten zugegriffen.
Dies ist das, was Masters als die Anhäufung von Freiheitsgraden des Forschers bezeichnet, und es ist wohl die gefährlichste Form des Overfittings, da sie unsichtbar ist. Die Datenpartitionen sehen auf dem Papier sauber aus. Der Code ist korrekt strukturiert. Doch die iterativen Reaktionen des Forschers auf die beobachtete Out-of-Sample-Leistung haben eine Verzerrung eingeführt, die durch Standardpartitionierung weder erkannt noch korrigiert werden kann.
Die Drei-Schichten-Architektur
Die Lösung von Masters besteht darin, drei streng partitionierte Datenpools zu erstellen, von denen jeder einem bestimmten Zweck dient, wobei jede Phase die Informationsintegrität des Datensatzes nur an der dafür vorgesehenen Stelle nutzt.
Gesamte historische Daten │ ├── ÄUSSERER TRAININGSSATZ (~60 %) ← Hier findet eine umfassende explorative Suche statt │ ├── INNERER VALIDIERUNGSSATZ (~20 %) ← Hier erfolgt die Vorauswahl der Kandidaten │ (Absichtlich nur einer begrenzten Anzahl von Kandidaten zugänglich gemacht) │ └── ENDGÜLTIGER TESTSATZ (~20 %) ← Wird genau einmal geöffnet, nachdem die Entscheidung endgültig getroffen wurde
Das äußere Trainingsset ist der Ort, an dem die vollständige Brute-Force-Suche durchgeführt wird. Tausende Regeln, Indikatoren und Parameterkombinationen werden hier getestet. Jeder Permutationstest, jeder Regularisierungsdurchlauf und jede explorative Analyse werden innerhalb dieser Grenze durchgeführt. Der Forscher kann die Leistung hier frei beobachten, da diese Daten explizit für die Exploration vorgesehen sind.
Das innere Validierungsset ist der Ort, an dem die Kandidatenauswahl stattfindet. Die in die engere Wahl gezogenen Überlebenden aus Phase 1 werden diesen bisher ungesehenen Daten ausgesetzt. Diejenigen, die stark abfallen, werden als wahrscheinlich überangepasst eliminiert. Diese Stufe wird sparsam eingesetzt – der Forscher legt sich auf eine kleine Anzahl von Finalistenkonfigurationen fest, bevor er sie hier einsetzt.
Das Final Test Set ist unantastbar. Es wird genau einmal geöffnet, nachdem sich der Forscher schriftlich vollständig auf eine einzige, endgültige Strategiekonfiguration festgelegt hat. Das Ergebnis dieser einzigen Auswertung ist die veröffentlichte Leistungskennzahl. Jeder Versuch, dieses Ergebnis für weitere Anpassungen zu nutzen, macht den Test ungültig.

Abb. 2. Die dreischichtige Datenpartitionierung. Jede Zone dient einem festgelegten Zweck. Das Final Test Set ist unantastbar, bis eine vollständige schriftliche Festlegung auf eine einzige Strategie erfolgt ist
Verankerte Walkforward-Erweiterung
Das Kernproblem, das es löst. Eine Drei-Wege-Datenpartitionierung (60/20/20) ist im Prinzip statistisch fundiert, aber in der Praxis sehr anspruchsvoll. Bei fünf Jahren täglicher Daten könnten Sie 1.250 Beobachtungen haben. Ein Final Test Set von 20% liefert Ihnen 250 Bars – kaum genug, um eine Sharpe-Ratio mit aussagekräftigen Konfidenzintervallen zu schätzen, und bei weitem zu wenig, um etwas Belastbares über Robustheit über verschiedene Marktregime hinweg zu sagen. Die Verkleinerung der Partition, um mehr Testdaten zu erhalten, verlagert das Problem lediglich auf den Trainingsdatensatz.
Wie es sich vom standardmäßigen rollierenden Walkforward unterscheidet. Der Standard-Walkforward legt die Größe des Trainingsfensters fest und verschiebt es nach vorn. Der Trainingsdatensatz rollt – alte Daten fallen am linken Rand weg, während neue Daten am rechten Rand hinzugefügt werden. Dies bedeutet, dass die Strategie jedes Mal von Grund auf neu auf einer anderen, gleich großen Stichprobe geschätzt wird. Frühe Marktregime werden schließlich verworfen.
Der verankerte Walkforward – eine weitverbreitete Technik, die von Masters und anderen klar beschrieben wird – legt den Start des Trainingsdatensatzes fest und erweitert ihn im Zeitverlauf. Der Trainingsdatensatz beginnt immer am gleichen historischen Ursprung. Jedes aufeinanderfolgende Fenster fügt weitere Daten hinzu. Die Fenster für den Final Test und die innere Validierung rücken zeitlich vor, behalten aber ihre proportionalen Größen im Verhältnis zu den bisher insgesamt verwendeten Daten bei.
Die einzelnen Ergebnisse des Final Tests werden nicht selektiv herausgegriffen. Sie werden als einheitliche Beweisgrundlage bewertet. Eine Strategie, die ihren Vorteil über die Mehrheit der Testfenster hinweg beibehält, hat ihre Robustheit über verschiedene Marktregime und Zeiträume hinweg bewiesen – eine weitaus stärkere Aussage, als sie ein einzelnes Testergebnis liefern kann.
Warum es funktioniert – fünf ineinandergreifende Gründe
- Jedes Fenster des Final Tests ist tatsächlich out-of-sample. Die Strategiekonfiguration, die auf jedes Testfenster angewendet wird, wurde festgelegt, bevor dieses Fenster geöffnet wurde. Die zeitliche Begrenzung ist strikt.
- Die Testergebnisse sind weitgehend unabhängig. Da jedes Fenster des Final Tests einen anderen, sich nicht überschneidenden Kalenderzeitraum abdeckt, sind die einzelnen Testergebnisse weitgehend unabhängige Beobachtungen der Marktleistung – obwohl die sich überschneidenden Trainingsdaten eine gewisse serielle Korrelation bei den Parameterschätzungen erzeugen, die bei der Aggregation der Ergebnisse berücksichtigt werden sollte.
- Verankerung akkumuliert Informationen, anstatt sie zu verwerfen. Rolling Walkforward verwirft frühe Daten, wenn das Fenster vorrückt. Verankerung behält alle historischen Daten im Trainingsdatensatz bei, während das Fenster wächst.
- Mehrere Testergebnisse offenbaren Regime-Sensitivität. Eine Strategie, die ihren Vorteil über alle Testfenster hinweg beibehält, hat sich unter allen Marktbedingungen bewährt, die über den gesamten historischen Zeitraum aufgetreten sind. Eine Strategie, die in einigen Fenstern gut abschneidet und in anderen versagt, offenbart eine echte Regime-Sensitivität – eine Erkenntnis, die ein einzelnes Testfenster vollständig verschleiern würde.
- Die dreischichtige Integrität bleibt in jedem Fenster erhalten. Jedes Fenster behält seine eigene innere Validierungsgrenze bei. Die Entwicklungsprozess-Disziplin, die V-in-V sinnvoll macht, wird nicht gelockert – sie wird wiederholt angewendet.
Der Nachteil – und warum er weniger wichtig ist, als es scheint
Die einzelnen Testfenster sind nicht vollständig unabhängig. Spätere Fenster teilen sich den Großteil ihrer Trainingsdaten mit früheren, was zu einer seriellen Korrelation bei den Parameterschätzungen führt, die zur Erzeugung des Ergebnisses jedes Fensters verwendet werden. Die Testergebnisse sollten daher qualitativ bewertet werden – als Gesamtevidenz, die untersucht, ob der Vorteil bestehen bleibt und ob sein Ausmaß ungefähr konsistent ist –, anstatt sie zu einer einzigen gepoolten Statistik mit einer fälschlicherweise aufgeblähten Stichprobengröße zusammenzufassen.

Abb. 3. Standard-Walkforward-Erweiterung

Abb. 4. Verankerte Walkforward-Erweiterung – drei Fenster zeigen, wie der Trainingsdatensatz wächst, während sich die innere Validierung und der Final Test nach vorn verschieben
Umsetzung
Der folgende Code zeigt, wie die vollständige dreischichtige V-in-V-Pipeline unter Verwendung von PurgedWalkForwardCV strukturiert wird. Die äußeren Trainingsdaten sind das, was Sie an PurgedWalkForwardCV übergeben; die innere Validierung und die Datensätze des Final Test Sets werden zuvor manuell zurückgehalten.
import numpy as np import pandas as pd from sklearn.base import clone from cross_validation import PurgedWalkForwardCV def vin_v_anchored_walkforward( X: pd.DataFrame, y: pd.Series, t1: pd.Series, estimator, n_splits: int = 5, inner_val_pct: float = 0.20, final_test_pct: float = 0.20, pct_embargo: float = 0.01, scorer=None, ) -> dict: """ Three-layer V-in-V pipeline with anchored expanding-window walkforward. Data is partitioned once, strictly in temporal order: [─── Outer Training (60%) ───][─ Inner Val (20%) ─][─ Final Test (20%) ─] The outer training set is further split by PurgedWalkForwardCV into n_splits expanding windows, each producing its own purged train/test pair. The inner validation set is touched only for shortlisting; the final test set is opened exactly once after a written commitment to a single config. Parameters ---------- X, y, t1 : aligned DataFrame, Series, Series estimator : sklearn-compatible estimator n_splits : number of anchored walkforward windows within outer training inner_val_pct, final_test_pct : fractions of total data reserved pct_embargo : embargo fraction passed to PurgedWalkForwardCV scorer : callable(y_true, y_pred) -> float, or None (returns raw preds) Returns ------- dict with keys: outer_scores – per-window score within outer training inner_val_score – score on the inner validation set (shortlisting only) final_test_score– score on the final test set (opened once, at the end) outer_cv – the fitted PurgedWalkForwardCV object """ n = len(X) # ── 1. Partition the data into three zones ─────────────────────────────── outer_end = int(n * (1.0 - inner_val_pct - final_test_pct)) val_end = int(n * (1.0 - final_test_pct)) X_outer, y_outer, t1_outer = X.iloc[:outer_end], y.iloc[:outer_end], t1.iloc[:outer_end] X_inner_val, y_inner_val, t1_inner_val = X.iloc[outer_end:val_end], y.iloc[outer_end:val_end], t1.iloc[outer_end:val_end] X_final, y_final = X.iloc[val_end:], y.iloc[val_end:] # ── 2. Phase 1 — exhaustive search within outer training ───────────────── # PurgedWalkForwardCV(expanding_window=True) anchors the start at index 0 # and grows the training window forward — this IS anchored walkforward. outer_cv = PurgedWalkForwardCV( n_splits=n_splits, t1=t1_outer, pct_embargo=pct_embargo, expanding_window=True, # ← anchored, not rolling ) outer_scores = [] outer_models = [] for train_idx, test_idx in outer_cv.split(X_outer, y_outer): model = clone(estimator) model.fit(X_outer.iloc[train_idx], y_outer.iloc[train_idx]) preds = model.predict(X_outer.iloc[test_idx]) score = scorer(y_outer.iloc[test_idx], preds) if scorer else None outer_scores.append(score) outer_models.append(model) # At this point the researcher reviews outer_scores, shortlists candidates, # and selects a small number of finalist configurations. The inner # validation set is NOT touched yet. # ── 3. Phase 2 — shortlist on inner validation (sparingly) ─────────────── # Only finalist models are exposed here. This set must not be used # to refine parameters — observation terminates candidate selection. finalist_model = outer_models[-1] # placeholder: researcher selects this val_preds = finalist_model.predict(X_inner_val) inner_val_score = scorer(y_inner_val, val_preds) if scorer else None # ── 4. Phase 3 — final test (opened exactly once) ──────────────────────── # The researcher commits in writing to finalist_model before this line. # Any adjustment after seeing this result invalidates the test. final_preds = finalist_model.predict(X_final) final_test_score = scorer(y_final, final_preds) if scorer else None return { "outer_scores": outer_scores, "inner_val_score": inner_val_score, "final_test_score": final_test_score, "outer_cv": outer_cv, } # ── Example usage ───────────────────────────────────────────────────────── if __name__ == "__main__": from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score dates = pd.date_range("2018-01-01", periods=1000, freq="B") X_demo = pd.DataFrame(np.random.randn(1000, 5), index=dates) y_demo = pd.Series(np.random.randint(0, 2, 1000), index=dates) t1_demo = pd.Series(dates + pd.Timedelta(days=5), index=dates) results = vin_v_anchored_walkforward( X=X_demo, y=y_demo, t1=t1_demo, estimator=RandomForestClassifier(n_estimators=50, random_state=42), n_splits=5, scorer=accuracy_score, ) print("Outer window scores:", results["outer_scores"]) print("Inner validation: ", results["inner_val_score"]) print("Final test score: ", results["final_test_score"])
Teil III: Kombinatorisch bereinigte Kreuzvalidierung
Das Problem, das CPCV lösen sollte
Validation-within-Validation setzt voraus, dass die einzelnen Bewertungen, die innerhalb des äußeren Trainingssets durchgeführt werden, selbst sauber sind. Es steuert den Entwicklungsprozess des Forschungsprozesses, überprüft jedoch nicht die interne Validität jeder Trainings-/Testbewertung.
Dies ist das Problem, das CPCV, entwickelt von Marcos Lopez de Prado in Advances in Financial Machine Learning, lösen sollte. Die Kontamination, die es adressiert, ist nicht verhaltensbedingt, sondern strukturell und in den statistischen Eigenschaften der Finanzzeitreihen selbst eingebettet.
Warum Finanzdaten anders sind
Die standardmäßige k-fache Kreuzvalidierung – das Arbeitspferd des maschinellen Lernens – geht davon aus, dass Beobachtungen unabhängig und identisch verteilt sind. Finanzzeitreihen verletzen diese Annahme grundlegend und auf vielfältige, sich überschneidende Weise.
Betrachten Sie einen Featuresatz, der einen gleitenden 20-Tage-Durchschnitt enthält. Die am Tag 21 berechnete Beobachtung teilt 19 ihrer 20 zugrunde liegenden Datenpunkte mit der an Tag 20 berechneten Beobachtung. Wenn diese beiden Beobachtungen verschiedenen Folds zugewiesen werden – eine zum Training, eine zum Testen –, hat der Test-Fold effektiv fast alle Trainingsdaten gesehen, die die entsprechende Trainingsbeobachtung erzeugt hat. Die Grenze zwischen Training und Test ist eine Illusion.
Dieses Problem wird durch die Labelbildung noch verschärft. Im Bereich Financial Machine Learning werden Beobachtungen typischerweise basierend auf dem Ergebnis einer zukünftigen Haltedauer mit Labels versehen. Ein Label, das durch die Rendite über die nächsten 10 Tage gebildet wird, erzeugt eine Abhängigkeit zwischen der mit Labels versehenen Beobachtung und den 10 nachfolgenden Kursbars. Diese nachfolgenden Bars werden mit ziemlicher Sicherheit an anderer Stelle im Datensatz als Feature-Inputs erscheinen und einen Leckagekanal schaffen, den ein Standard-Purging nicht erfasst.
Abb. 5 veranschaulicht die k Trainings-/Testsplits, die durch einen k-fachen CV durchgeführt werden, wobei k = 5 ist. In diesem Schema:
- Der Datensatz wird in k Teilmengen aufgeteilt.
- Für i = 1,...,k:
- (a) Der ML-Algorithmus wird auf allen Teilmengen außer i trainiert.
- (b) Der angepasste ML-Algorithmus wird an i getestet.

Abb. 5. Train/Test-Splits in einem fünffachen CV-Schema
Die zwei Purging-Mechanismen
Purging: Nachdem ein Test-Fold zugewiesen wurde, entfernt CPCV aus dem Trainingsset alle Beobachtungen, deren Label-Bildungsfenster zeitlich mit dem Testzeitraum überlappt. Dies ist nicht einfach eine Frage der Trennung von Zeiträumen – das Purging muss den spezifischen Horizont berücksichtigen, über den das Label jeder Beobachtung berechnet wurde.
Embargoing: Ein Puffer von Beobachtungen, der unmittelbar auf das Test-Fold folgt, wird ebenfalls aus dem Training entfernt. Features, die auf diesen Post-Test-Beobachtungen berechnet wurden, könnten in den Testzeitraum zurückblicken und einen Reverse-Leakage-Kanal erzeugen. Das Embargo-Fenster wird auf den längsten Feature-Lookback im Feature-Set bemessen.

Abb. 6. Purging von Trainingsbeobachtungen vor dem Test und Embargo für Trainingsbeobachtungen nach dem Test
Die kombinatorischen Pfade
Über das Purging und Embargoing hinaus führt CPCV eine strukturelle Verbesserung gegenüber der Standard-k-Fold-Validierung ein. Anstatt einer einzigen Train/Test-Aufteilung generiert es alle möglichen Kombinationen von Fold-Zuweisungen. Diese Splits werden dann zu vollständigen Backtest-Pfaden rekombiniert.
Betrachten Sie T Beobachtungen, die ohne Mischen in N Gruppen unterteilt sind, wobei die Gruppen n = 1, ... , N − 1 die Größe ⌊T∕N⌋ haben, die N-te Gruppe die Größe T − ⌊T∕N⌋ (N − 1) hat und ⌊.⌋ die Floor- oder Integer-Funktion ist. Für ein Testset der Größe k Gruppen ist die Anzahl der möglichen Trainings-/Test-Splits

Da jede Kombination k getestete Gruppen umfasst, ist die Gesamtzahl der getesteten Gruppen
. Und da wir alle möglichen Kombinationen berechnet haben, sind diese getesteten Gruppen gleichmäßig über alle N verteilt (jede Gruppe gehört zur gleichen Anzahl von Trainings- und Testsets). Die Implikation ist, dass wir aus k-großen Testsets auf N Gruppen eine Gesamtzahl von Pfaden 𝜑 [N, k] backtesten können,

... Es gibt C(6, 4) = 15 Splits, indexiert als S1, … ,S15. Für jeden Split lässt die Abbildung ... die Gruppen, die das Trainingsset bilden, unmarkiert. Jede Gruppe ist Teil von 𝜑 [6, 2] = 5 Testmengen, daher ermöglicht uns dieses Trainings-/Test-Aufteilungsschema die Berechnung von 5 Backtest-Pfaden.
Abb. 7 zeigt die Zuweisung jeder getesteten Gruppe zu einem Backtest-Pfad. Zum Beispiel ist Pfad 1 das Ergebnis der Kombination der Prognosen aus (G1, S1), (G2, S1), (G3, S2), (G4, S3), (G5, S4) und (G6, S5). Pfad 2 ist das Ergebnis der Kombination der Prognosen aus (G1, S2), (G2, S6), (G3, S6), (G4, S7), (G5, S8) und (G6, S9) und so weiter.
Diese Pfade werden generiert, indem der Klassifikator für jede Kombination auf einem Teil 𝜃 = 1 − k∕N der Daten trainiert wird. Obwohl es theoretisch möglich ist, auf einem Teil 𝜃 < 1∕2 zu trainieren, gehen wir in der Praxis davon aus, dass k ≤ N∕2 ist. Der Datenanteil im Trainingsset 𝜃 nimmt mit N → T zu, nimmt aber mit k → N∕2 ab. Die Anzahl der Pfade 𝜑 [N, k] nimmt mit N → T und mit k → N∕2 zu. Im Grenzwert wird die größte Anzahl an Pfaden erreicht, indem N = T und k = N∕2 = T∕2 gesetzt werden, auf Kosten des Trainings des Klassifikators mit nur der Hälfte der Daten für jede Kombination (𝜃 = 1∕2).

Abb. 7. Zuweisung von Testgruppen zu jedem der 5 Pfade
López de Prado, 2018, S. 164-165
Bei N=6 Gruppen und k=2 Testgruppen fügt jeder Pfad die Out-of-Sample-Segmente aus einer Teilmenge der Aufteilungen zusammen, um die gesamte Zeitachse genau einmal abzudecken, was C(6,2) × k / N = 5 vollständige Pfade ergibt. Es sind diese Pfade – nicht die einzelnen Aufteilungen –, die die korrekte Grundlage für die Berechnung von Sharpe-Quotienten und die Bewertung der Robustheit der Verteilung bilden.
Diese Verteilung ist an sich informativ. Eine Strategie mit einer schmalen, positiven Verteilung der Pfadergebnisse über alle kombinatorischen Fold-Zuweisungen hinweg hat ihre Robustheit über viele verschiedene zeitliche Konfigurationen hinweg bewiesen. Eine Strategie, deren Performance über Pfade hinweg stark variiert, ist fragil, unabhängig von ihrer durchschnittlichen Performance.

Abb. 8: Bar-Chart von 5 CPCV-Pfad-Sharpe-Quotienten im direkten Vergleich für eine robuste und eine fragile Strategie
Umsetzung
import numpy as np import pandas as pd from sklearn.ensemble import RandomForestClassifier from combinatorial import CombinatorialPurgedCV, CPCVAnalyzer, optimal_folds_number # ── 1. Configure the CV generator ──────────────────────────────────────── # Use optimal_folds_number to find N and k that meet your targets. # Here: aim for ~600 obs in each training set and 5 backtest paths. n_folds, n_test_folds = optimal_folds_number( n_observations=1000, target_train_size=600, target_n_test_paths=5, ) print(f"n_folds={n_folds}, n_test_folds={n_test_folds}") cv = CombinatorialPurgedCV( n_folds=n_folds, n_test_folds=n_test_folds, t1=t1_outer, # pd.Series: index=event start, values=event end pct_embargo=0.01, # removes 1% of obs after each test block ) print(cv.summary(X_outer)) # Number of Observations 1000 # Total Number of Folds 6 # Number of Test Folds 2 # Number of Test Paths 5 # Number of Training Combinations 15 # ── 2. Fit and predict across all combinatorial splits ─────────────────── # CPCVAnalyzer handles the parallel execution and path recombination. analyzer = CPCVAnalyzer( estimator=RandomForestClassifier(n_estimators=100, random_state=42), cv_gen=cv, close_prices=close_prices, # pd.Series of price for MtM Sharpe calculation ) recombined_preds = analyzer.fit_predict(X_outer, y_outer) # ── 3. Inspect the distribution of path Sharpe ratios ─────────────────── # Each path is an independent backtest covering the full timeline once. # A tight, positive distribution signals robustness. metrics = analyzer.get_distribution_metrics(primary_sides=sides) path_sharpes = metrics.xs("binary", level="method")["mtm_sharpe"] print("Path Sharpe ratios:") print(path_sharpes.to_string()) print(f"Mean: {path_sharpes.mean():.3f} Std: {path_sharpes.std():.3f}") # ── 4. Visualise train/test fold assignments ───────────────────────────── # Run split() fully first so index_train_test_ is complete. _ = [s for s in cv.split(X_outer)] # exhaust generator fig = cv.plot_train_test_folds() fig.show()
Teil IV: Kombinatorisch symmetrische Kreuzvalidierung
Von Schutz zu Messung
Während V-in-V und CPCV schützende Mechanismen sind – sie reduzieren Overfitting durch Strukturierung des Forschungsprozesses und Bereinigung von Datenlecks –, ist CSCV primär ein Diagnose- und Messinstrument. Ihr Ergebnis ist eine einzelne Zahl: die Wahrscheinlichkeit für Backtest-Overfitting (PBO).
CSCV wurde von David Bailey und Marcos Lopez de Prado als eine Methode zur Quantifizierung des Grades eingeführt, in dem ein Strategieauswahlprozess Ergebnisse liefert, die eher auf In-Sample-Optimierung als auf echter Out-of-Sample-Vorhersagekraft beruhen. Die PBO ist eine falsifizierbare, kommunizierbare Aussage, kein qualitatives Urteil. Sie kann in jeder Phase der Forschungspipeline angewendet werden, in der eine Kandidatenmenge existiert – nicht ausschließlich nach der V-in-V-Shortlisting-Phase –, was sie zu einem flexiblen Prüfungsinstrument macht.
Die Mechanik
Das Verfahren beginnt mit der Unterteilung der historischen Daten in S gleiche Teilmengen, typischerweise zwischen 8 und 16. Alle C(S, S/2) Möglichkeiten, diese Teilmengen in eine Trainingshälfte und eine Testhälfte aufzuteilen, werden dann generiert. Für jede Aufteilung wird die Strategie mit der besten In-Sample-Performance identifiziert. Ihr Rang unter allen Strategien in der Out-of-Sample-Hälfte derselben Aufteilung wird dann aufgezeichnet.

Abb. 9. CSCV Schritt 1: Die Daten werden in S gleiche Teilmengen unterteilt. Alle C(S, S/2) Möglichkeiten, die Hälfte der In-Sample (IS, grün) und die Hälfte der Out-of-Sample (OOS, rot) zuzuordnen, werden dann aufgezählt

Abb. 10. CSCV Schritt 2: Für jede Aufteilung wird die beste In-Sample (IS)-Strategie identifiziert (S3★), dann wird ihr Rang in der Out-of-Sample (OOS)-Hälfte aufgezeichnet. Hier belegt sie den 4. Platz von 6 – unter dem Median – was zum PBO-Wert beiträgt.

Abb. 11. CSCV Schritt 3: Der Anteil der Aufteilungen, bei denen die beste In-Sample-Strategie unter dem Out-of-Sample-Median rangierte, ist die PBO-Schätzung. Ein PBO von 0,38 deutet auf einen signifikanten Selektionsbias im Forschungsprozess hin

Abb. 12. PBO-Interpretation. Ein PBO nahe 0 zeigt an, dass der Auswahlprozess zuverlässig die tatsächlich besten Out-of-Sample-Strategien identifiziert. Ein PBO nahe 0.5 zeigt an, dass die beste In-Sample-Strategie Out-of-Sample im Wesentlichen zufällig eingestuft wird. Der Schwellenwert für „akzeptabel“ variiert je nach Kontext und Größe der Kandidatenmenge – es ist eine Faustregel, keine formale statistische Grenze
Was CSCV nicht tut
CSCV behebt kein Overfitting. Es bereinigt keine zeitlichen Lecks, kontrolliert nicht die Freiheitsgrade des Forschers und strukturiert nicht den Entwicklungsprozess. Ein hoher PBO-Wert sagt Ihnen, dass Ihr Auswahlprozess unzuverlässig ist – er sagt Ihnen nicht, wie Sie ihn zuverlässig machen können.
CSCV ist am effektivsten, wenn es als abschließende Prüfung eingesetzt wird: Nach der Zusammenstellung Ihrer Kandidaten-Shortlist mithilfe des V-in-V-Frameworks erzeugt es einen PBO-Wert für den Auswahlprozess als Ganzes. Ein niedriger PBO-Wert bietet eine quantitative, kommunizierbare Sicherheit, dass der Auswahlprozess eher Signale als Rauschen erzeugte. Da CSCV jedoch auf jeder beliebigen Sammlung von Strategie-Testergebnissen arbeitet, kann es gleichermaßen in früheren Phasen angewendet werden, beispielsweise zur Prüfung des Kandidatengenerierungsprozesses innerhalb des Outer Training Set.
Umsetzung
CSCV verwendet CombinatorialPurgedCV direkt wieder. Da CSCV alle Teilmengen symmetrisch behandelt – es gibt keinen zeitlichen Pfeil zwischen In-Sample- und Out-of-Sample-Hälften –, setzen Sie pct_embargo=0.0 und n_test_folds=n_folds//2. Dies erzeugt genau die oben beschriebenen symmetrischen C(S, S/2)-Splits.
""" Probability of Backtest Overfitting (PBO) — Bailey & Lopez de Prado (2014). Uses CombinatorialPurgedCV with pct_embargo=0.0 and n_test_folds=n_folds//2 to generate the C(S, S/2) symmetric IS/OOS splits required by CSCV. """ from math import comb from typing import Callable, Dict, List import numpy as np import pandas as pd from scipy.stats import norm from combinatorial import CombinatorialPurgedCV def compute_pbo( returns_matrix: pd.DataFrame, t1: pd.Series, n_folds: int = 8, metric: Callable = None, ) -> Dict: """ Compute the Probability of Backtest Overfitting (PBO). Parameters ---------- returns_matrix : pd.DataFrame, shape (T, N) Columns are candidate strategies; rows are time-ordered observations. Values should be per-period returns (or any scalar performance measure that is comparable across strategies at a given time step). t1 : pd.Series Event end-times aligned with returns_matrix.index. Because CSCV is symmetric (no temporal arrow), pct_embargo is set to 0.0, so t1 is used only to satisfy the CombinatorialPurgedCV interface — you may pass t1 = pd.Series(index, index=index) as a neutral value. n_folds : int, default=8 Number of equal subsets S. Must be even. C(S, S/2) splits are generated. Bailey & Lopez de Prado recommend 8–16. metric : callable(returns: pd.Series) -> float, optional Aggregates a column of returns into a scalar performance measure. Defaults to the Sharpe ratio (mean/std). Returns ------- dict with keys: pbo – float in [0, 1]: estimated probability of overfitting n_splits – total number of IS/OOS splits evaluated (= C(S, S/2)) below_median – number of splits where best-IS ranked below OOS median oos_ranks – list of normalised OOS ranks for the best-IS strategy (0 = best OOS, 1 = worst OOS) logit_sr – logit-transformed OOS score (if in (0,1)) for best-IS strategy per split """ if n_folds % 2 != 0: raise ValueError(f"n_folds must be even for symmetric CSCV; got {n_folds}.") n_test_folds = n_folds // 2 # ← symmetric split: half IS, half OOS if metric is None: def metric(r: pd.Series) -> float: """Sharpe ratio.""" return r.mean() / r.std(ddof=1) if r.std() > 0 else 0.0 # ── Build the CSCV generator ────────────────────────────────────────── # pct_embargo=0.0 → no embargo (CSCV is symmetric, not temporal) # n_test_folds=n_folds//2 → exactly half the folds form the OOS set cv = CombinatorialPurgedCV( n_folds=n_folds, n_test_folds=n_test_folds, # = n_folds // 2 t1=t1, pct_embargo=0.0, # ← required for symmetric CSCV ) n_strategies = returns_matrix.shape[1] below_median = 0 oos_ranks = [] logit_sr = [] # ── Iterate over all C(n_folds, n_folds//2) symmetric splits ──────── for train_idx, test_idx_list in cv.split(returns_matrix): test_idx = np.concatenate(test_idx_list) # IS half: compute metric for every strategy is_scores = returns_matrix.iloc[train_idx].apply(metric, axis=0) # OOS half: compute metric for every strategy oos_scores = returns_matrix.iloc[test_idx].apply(metric, axis=0) # Best IS strategy best_is_col = is_scores.idxmax() # Rank of that strategy OOS (0-based; 0 = best OOS performer) oos_sorted = oos_scores.rank(ascending=False) # rank 1 = best oos_rank = oos_sorted[best_is_col] # 1-indexed # Normalise rank to [0, 1]: 0 = best OOS, 1 = worst OOS norm_rank = (oos_rank - 1) / (n_strategies - 1) if n_strategies > 1 else 0.0 oos_ranks.append(float(norm_rank)) # Below median? (norm_rank > 0.5 means worse than median) if norm_rank > 0.5: below_median += 1 # Logit-transformed OOS score (if in (0,1)) for the best-IS strategy oos_sr = float(oos_scores[best_is_col]) logit_sr.append(np.log(oos_sr / (1 - oos_sr)) if 0 < oos_sr < 1 else np.nan) n_splits = cv.n_splits pbo = below_median / n_splits return { "pbo": pbo, "n_splits": n_splits, "below_median": below_median, "oos_ranks": oos_ranks, "logit_sr": [v for v in logit_sr if not np.isnan(v)], } # ── Example usage ───────────────────────────────────────────────────────── if __name__ == "__main__": # Simulate 50 strategy return streams over 500 time steps rng = np.random.default_rng(42) dates = pd.date_range("2020-01-01", periods=500, freq="B") returns = pd.DataFrame( rng.normal(0.0001, 0.01, size=(500, 50)), index=dates, columns=[f"strategy_{i}" for i in range(50)], ) # Neutral t1: event start == event end (no label horizon, no purging effect) t1_neutral = pd.Series(dates, index=dates) result = compute_pbo( returns_matrix=returns, t1=t1_neutral, n_folds=8, # → n_test_folds=4, C(8,4)=70 splits ) print(f"PBO : {result['pbo']:.3f}") print(f"Splits evaluated : {result['n_splits']}") print(f"Below median (IS best → OOS below median): {result['below_median']}") # Interpret: if returns are pure noise, PBO ≈ 0.5 # A strategy with genuine edge should push PBO toward 0
Teil V: Eine einheitliche Pipeline
Zuordnung der Techniken zu den Forschungsphasen
Die Beziehung zwischen diesen drei Techniken wird deutlich, wenn sie den Phasen des V-in-V-Entwicklungsprozesses zugeordnet werden. Sie konkurrieren nicht miteinander. Sie nehmen unterschiedliche Positionen in der Pipeline ein und schützen vor unterschiedlichen Bedrohungen.
| Technik | Rolle in der Pipeline | Adressierte Bedrohung | Ausgabe |
|---|---|---|---|
| CPCV | Innerhalb des äußeren Trainingsbereichs (Phase 1) | Zeitliche Leckage innerhalb jeder Trainings-/Testauswertung | Saubere Leistungspfade pro Auswertung |
| V-in-V | Steuert den gesamten Entwicklungsprozess | Über Iterationen hinweg akkumulierte Freiheitsgrade des Forschers | Entwicklungsprozessstruktur mit drei geschützten Zonen |
| CSCV | Nach der Vorauswahl der Kandidaten (Phase 2) oder in jeder Phase, in der eine Kandidatenmenge existiert | Selektionsbias bei der Kandidatenmenge | PBO – eine falsifizierbare Wahrscheinlichkeitsschätzung |
Lösen sie das gleiche Problem?
Die drei Verfahren sind insofern miteinander verwandt, als sie alle existieren, weil naive Trainings-/Test-Splits bei Finanzdaten zu optimistischen Leistungsschätzungen führen. Sie adressieren jedoch grundlegend unterschiedliche Mechanismen dieses gemeinsamen Problems.
Validation-within-Validation adressiert die Verunreinigung durch Verhaltensweisen, die sich über den gesamten Entwicklungsprozess hinweg ansammeln, wenn ein menschlicher Forscher iterative Modellierungsentscheidungen trifft. Diese Kontamination wird vom Forscher eingebracht und würde nicht existieren, wenn von Anfang an eine einzige, nicht-iterative Strategiewahl getroffen würde.
Combinatorially Purged Cross-Validation adressiert strukturelle Kontamination, die in der zeitlichen Abhängigkeit von Finanzbeobachtungen eingebettet ist. Diese Kontamination existiert in den Daten selbst und würde selbst dann bestehen bleiben, wenn der Forscher überhaupt keine iterativen Entscheidungen treffen würde – sie ist eine mathematische Eigenschaft von Finanzzeitreihen.
Combinatorially Symmetric Cross-Validation quantifiziert die Wahrscheinlichkeit, dass der Auswahlprozess als Ganzes ein überangepasstes Ergebnis geliefert hat, und liefert eine falsifizierbare, kommunizierbare Aussage über die Zuverlässigkeit der gewählten Strategie, die keines der beiden anderen Verfahren liefert.
Die Durchführung von CPCV ohne die strukturelle Disziplin von V-in-V liefert zwar saubere Einzelbewertungen, lässt aber die Freiheitsgrade des Forschers unkontrolliert. Ein Entwickler könnte CPCV durch fünfzig Iterationen der Strategieverfeinerung laufen lassen, jedes Ergebnis beobachten und jede Beobachtung die nächste Modellierungsentscheidung beeinflussen lassen. Jede einzelne Bewertung wäre sauber, aber der Forschungsprozess als Ganzes wäre kontaminiert.
Umgekehrt bewahrt die Durchführung von V-in-V ohne CPCV innerhalb von Phase 1 den Final Test Set perfekt, kann aber einen Kandidaten aus Phase 1 hervorbringen, der auf der Grundlage von durch Leckagen aufgeblähten Bewertungen ausgewählt wurde. Die dreischichtige Struktur schützt den Final Test Set, aber der Kandidatenmechanismus, der ihn speist, ist beeinträchtigt.
Das Weglassen von CSCV bedeutet, dass der Anwender keine quantitative Schätzung darüber hat, wie zuverlässig der Auswahlprozess war. Sie haben sich zwar gegen bekannte Leckagekanäle geschützt, verfügen aber über keine prüfbare, kommunizierbare Aussage über die Wahrscheinlichkeit, dass die gewählte Strategie einen echten Vorteil darstellt und nicht nur ein Glückstreffer ist.
Welches Verfahren ist am wichtigsten?
Wenn man gezwungen ist, nach der Schwere der Verzerrung zu priorisieren, die entsteht, wenn jedes Verfahren isoliert weggelassen wird, ist die folgende Reihenfolge ein vernünftiger Ausgangspunkt – wobei Anwender beachten sollten, dass dies ein redaktionelles Urteil und kein gesichertes empirisches Ergebnis ist und die relative Bedeutung jedes Verfahrens mit der Länge des Datensatzes, der Komplexität der Merkmale und der Intensität des Forschungsprogramms variiert.
1. CPCV – Nicht verhandelbar für Finanzzeitreihen. Die standardmäßige k-fache Kreuzvalidierung ist für Finanzdaten nicht nur suboptimal; sie ist strukturell falsch. Die i.i.d.-Annahme, die sie erfordert, wird von praktisch jedem Finanzmerkmalsatz verletzt. Zeitliche Leckagen blähen Leistungsschätzungen auf eine Weise auf, die nachträglich nicht mehr korrigiert werden kann.
2. V-in-V – Nicht verhandelbar für jedes iterative Forschungsprogramm. In der Praxis beinhaltet jede ernsthafte Strategieentwicklungsbemühung wochen- oder monatelange iterative Verfeinerungen. Ohne die strukturelle Disziplin von V-in-V stellt jede Beobachtung der Out-of-Sample-Performance, die eine nachfolgende Entscheidung beeinflusst, nicht berücksichtigte Freiheitsgrade des Forschers dar. In einem langen, intensiven Forschungsprogramm kann dieser kumulierte Effekt jede einzelne Quelle von Datenlecks in den Schatten stellen – was ein starkes Argument dafür liefert, dass V-in-V in der Praxis genauso wichtig oder wichtiger als CPCV sein kann.
3. CSCV – Einzigartig wertvoll für die Kommunikation von Ergebnissen. Sein quantitativer PBO-Output bietet etwas, das die anderen beiden Techniken nicht können: eine einzelne, vertretbare Zahl, die die Zuverlässigkeit des gesamten Auswahlprozesses zusammenfasst. Dies macht es besonders wertvoll in institutionellen Kontexten, in denen Forschung überprüft, von Fachkollegen validiert oder an Stakeholder kommuniziert werden muss, die die vollständige Methodik nicht einsehen können.
Schlussfolgerung
Der „Spaghetti-an-die-Wand“-Ansatz zur Strategiefindung muss nicht intellektuell unseriös sein. Eine breite explorative Suche über große Indikatorräume kann echte Marktvorteile zutage fördern, vorausgesetzt, der Forscher hat eine Validierungsinfrastruktur konstruiert, die rigoros genug ist, um echte Signale von dem statistischen Rauschen zu trennen, das eine Brute-Force-Suche zwangsläufig erzeugt.
Keine einzelne Technik ist ausreichend. V-in-V, CPCV und CSCV adressieren jeweils einen unterschiedlichen Fehlermodus, arbeiten in einer anderen Phase der Forschungspipeline und bieten eine andere Form von Schutz oder Messung. Der Einsatz einer Technik ohne die anderen lässt erhebliche Angriffsflächen offen.
CPCV stellt sicher, dass jede Train/Test-Auswertung innerhalb der explorativen Phase frei von zeitlichen Informationslecks ist. V-in-V stellt sicher, dass der Entwicklungsprozess als Ganzes keine Freiheitsgrade des Forschers ansammelt, die die scheinbare Signifikanz der endgültigen Strategieauswahl aufblähen. CSCV liefert eine quantitative, prüfbare Schätzung der Wahrscheinlichkeit, dass der Auswahlprozess selbst einen echten Vorteil hervorgebracht hat und nicht ein optimiertes Artefakt der verfügbaren Daten.
Zusammen transformieren diese drei Techniken die Sharpe-Ratio (oder jede andere Leistungskennzahl) eines Backtests von einer Zahl, die widerspiegelt, was ein Forscher zu finden hoffte, in eine Zahl, die repräsentiert, was die Strategie tatsächlich voraussichtlich liefern wird.
Code-Referenz
Die Implementierungsbeispiele in diesem Artikel stützen sich auf fünf Dateien. Die folgende Tabelle fasst ihre Rollen und Beziehungen zusammen.
| Datei | Zweck | Wichtige Inhalte | Abhängigkeiten |
|---|---|---|---|
| cross_validation.py | Grundbausteine für Core Purging und Walk-Forward-Kreuzvalidierung. Grundlage für alle übergeordneten Komponenten. | ml_get_train_times – vektorisiertes ereignisbasiertes Purging, PurgedKFold – standardmäßiges Purged K-Fold, PurgedWalkForwardCV – verankertes (expandierendes) oder rollierendes Walk-Forward mit Purging und Embargo, ml_cross_val_score – CV-Runner für eine einzelne Metrik, analyze_cross_val_scores – CV-Runner für mehrere Metriken mit Aufschlüsselung der Konfusionsmatrix. | Keine (Basis-Modul) |
| combinatorial.py | Engine für kombinatorisch bereinigte Kreuzvalidierung. Generiert alle C(N,k) Trainings-/Test-Splits, rekombiniert diese zu φ[N,k] vollständigen Backtest-Pfaden und berechnet die vollständige Verteilung der Leistungskennzahlen auf Pfadebene. | CombinatorialPurgedCV – sklearn-kompatibler CV-Generator mit Purging, Embargo, Pfadrekombination und Visualisierung der Fold-Aufteilung, CPCVAnalyzer – paralleles Fit/Predict und MtM-Sharpe-Verteilung über alle Pfade, optimal_folds_number – findet das (N, k)-Paar, das am besten zur Ziel-Trainingsgröße und Pfadanzahl passt. | cross_validation.py (importiert ml_get_train_times) |
| anchored_walkforward.py | Implementiert den vollständigen dreistufigen V-in-V-Entwicklungsprozess unter Verwendung eines verankerten, expandierenden Fensters. Erzwingt eine strikte Trennung der äußeren Trainings-, inneren Validierungs- und Final Test-Zonen. | vin_v_anchored_walkforward – unterteilt Daten in drei zeitliche Zonen, führt PurgedWalkForwardCV(expanding_window=True) innerhalb der äußeren Zone aus, legt den inneren Validierungssatz nur für die Vorauswahl offen und öffnet den Final Test Set genau einmal nach einer schriftlichen Festlegung. | cross_validation.py |
| cpcv_usage.py | Anwendungsbeispiel, das zeigt, wie CPCV innerhalb der äußeren Trainingszone einer V-in-V-Pipeline konfiguriert und ausgeführt wird, die Sharpe-Verteilung über Pfade hinweg untersucht und die Methoden zur Visualisierung der Fold-Aufteilung korrekt aufgerufen werden. | Demonstriert optimal_folds_number, CombinatorialPurgedCV.summary, CPCVAnalyzer.fit_predict, get_distribution_metrics sowie die korrekte Aufrufreihenfolge für plot_train_test_index und plot_train_test_folds. | combinatorial.py |
| pbo.py | Berechnet die Wahrscheinlichkeit einer Backtest-Überanpassung (PBO) unter Verwendung von CombinatorialPurgedCV, konfiguriert für symmetrische CSCV-Splits. Akzeptiert eine beliebige Matrix von Strategierenditen sowie eine Leistungskennzahl und gibt die PBO-Schätzung zusammen mit OOS-Rangdiagnostiken pro Split zurück. | compute_pbo – setzt pct_embargo=0.0 und n_test_folds=n_folds//2, um die für CSCV erforderlichen symmetrischen C(S, S/2)-Splits zu erzeugen; identifiziert die beste In-Sample-Strategie pro Split; zeichnet deren normalisierten Out-of-Sample-Rang auf; und aggregiert den Anteil der Splits, bei denen sie unter dem OOS-Median rangierte. | combinatorial.py |
Weiterführende Literatur
- Masters, T. (1995). Advanced Algorithms for Neural Networks. John Wiley & Sons.
- Masters, T. (2013). Permutation and Randomization Tests for Trading System Development. Self-published.
- Lopez de Prado, M. (2018). Advances in Financial Machine Learning. John Wiley & Sons.
- Bailey, D. H., & Lopez de Prado, M. (2014). The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting and Non-Normality. Journal of Portfolio Management.
- Bailey, D. H., Borwein, J., Lopez de Prado, M., & Zhu, Q. J. (2014). Pseudo-Mathematics and Financial Charlatanism: The Effects of Backtest Overfitting on Out-of-Sample Performance. Notices of the AMS.
- White, H. (2000). A Reality Check for Data Snooping. Econometrica, 68(5), 1097–1126.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21603
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Vom Einsteiger zum Experten: Optimierung von Liquiditätsstrategien durch strukturelle Bestätigung über mehrere Zeitrahmen in MQL5
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Entwicklung eines Toolkits zur Price-Action-Analyse (Teil 64): Manuell gezeichnete Trendlinien mit der automatisierten Überwachung synchronisieren
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Oh, ich bin mit meinen Gedanken etwas spät dran, Warren Giddings hat bereits gute Argumente vorgebracht. ;-)
Insbesondere möchte ich erwähnen, dass eine sehr wichtige Metaoptimierung in dem Artikel außer Acht gelassen wurde – nämlich die Anpassung der In-Sample-Fenster- und Vorwärtsschrittgrößen. Denn Walkforward ist nicht auf „Rolling“ und „Anchored“ beschränkt, sondern es gibt auch die Cluster-Walkforward-Optimierung.
Daher sollten alle beschriebenen Methoden sozusagen in einer weiteren, senkrechten Dimension von IS/OOS-Größenkombinationen erneut angewendet und im Testzeitraum überprüft werden.
„MetaTrader 5 Machine Learning Blueprint“. Dieser Artikel sollte den Lesern, die sich noch nie viele Gedanken über die Art von Überanpassung gemacht haben, die ich in diesem Artikel thematisiert habe, die Augen öffnen. Aus diesem Grund habe ich ihn bewusst getrennt von den anderen Artikeln der ML-Blueprint-Reihe verfasst.
Ach, ich bin mit meinen Gedanken etwas spät dran – Warren Giddings hat bereits gute Argumente vorgebracht. ;-)
Insbesondere möchte ich darauf hinweisen, dass eine sehr wichtige Meta-Optimierung in dem Artikel außer Acht gelassen wurde – nämlich die Anpassung der In-Sample-Fenster- und Vorwärtsschrittgrößen. Denn Walkforward ist nicht nur durch „Rolling“ und „Anchored“ begrenzt, sondern es gibt auch die Cluster-Walkforward-Optimierung.
Daher sollten alle beschriebenen Methoden sozusagen in einer weiteren, senkrechten Dimension von IS/OOS-Kombinationen der Größen neu aufgerufen und im Testzeitraum überprüft werden.
Tolle Arbeit! Vielen Dank!