← Alle Insights

Reproduzierbare Backtest-Umgebungen dasselbe Ergebnis auch in zwei Jahren.

Ein Backtest, den niemand außer Ihnen — und vielleicht nicht einmal Sie selbst in zwei Jahren — exakt nachrechnen kann, ist keine Evidenz, sondern eine Behauptung. Das klingt streng, ist aber die ehrliche Diagnose vieler Strategie-Auswertungen: Sie laufen einmal, liefern eine schöne Equity-Kurve, und der genaue Zustand aus Code, Daten, Zufallszahlen und Bibliotheksversionen, der diese Kurve erzeugt hat, ist nie festgehalten worden. Ändert sich später irgendeine dieser Zutaten — ein Pandas-Update, ein nachträglich korrigierter Kurs, ein anderer Seed — kommt ein anderes Ergebnis heraus, und niemand weiß, welches das echte war. Reproduzierbarkeit ist deshalb keine akademische Tugend, sondern die Voraussetzung dafür, einer Strategie überhaupt vertrauen zu dürfen. Dieser Beitrag zeigt die vier Quellen von Nicht-Reproduzierbarkeit — Zufall, Abhängigkeiten, Daten und Umgebung — und wie man sie konkret in den Griff bekommt, mit ehrlichem Blick auf den Aufwand, der sich lohnt, und den, der übertrieben ist.

Was Reproduzierbarkeit konkret bedeutet.

Reproduzierbarkeit heißt: Derselbe Code, auf denselben Daten, mit derselben Konfiguration, liefert dasselbe Ergebnis — heute, nächste Woche und in zwei Jahren, auf der Maschine eines Kollegen genauso wie auf Ihrer. Das ist mehr, als die meisten glauben, denn jede dieser Bedingungen kann still verletzt sein.

Man unterscheidet sinnvoll zwei Stufen. Bit-genaue Reproduzierbarkeit bedeutet, dass das Ergebnis exakt identisch ist — jede Zahl, jeder Trade, jede Nachkommastelle. Statistische Reproduzierbarkeit bedeutet, dass die Kennzahlen — Sharpe, Drawdown, Trefferquote — innerhalb einer kleinen, erklärbaren Toleranz übereinstimmen. Bit-genau ist das Ideal und für jeden Audit der Goldstandard; statistisch ist oft das Erreichbare, wenn nicht-deterministische Hardware oder Parallelität im Spiel ist.

Der entscheidende Punkt: Eine Abweichung ist nur dann harmlos, wenn man sie erklären kann. Eine unerklärte Differenz zwischen zwei Läufen ist immer ein rotes Tuch — sie bedeutet, dass irgendeine unkontrollierte Variable das Ergebnis mitbestimmt, und man weiß nicht, welche.

Zufall kontrollieren: Seeds richtig setzen.

Jede Stelle, an der Zufall eine Rolle spielt — Train-Test-Splits, Bootstrap, Modell-Initialisierung, Sampling, Dropout —, muss von einem fixierten Seed gesteuert sein. Das klingt banal, wird aber regelmäßig unvollständig umgesetzt. Ein häufiger Fehler: Man setzt den Seed der einen Bibliothek, vergisst aber, dass das Modell intern eine andere nutzt.

In einem typischen Python-Stack müssen mindestens drei Generatoren konsistent geseedet werden: der von Python selbst, der von NumPy, und der des verwendeten ML-Frameworks. Wer GPUs nutzt, hat ein zusätzliches Problem: Viele GPU-Operationen sind von Natur aus nicht deterministisch, weil die Reihenfolge paralleler Gleitkomma-Additionen variiert. Hier muss man den deterministischen Modus des Frameworks aktiv einschalten und akzeptieren, dass das oft Performance kostet.

Eine unterschätzte Falle ist die Parallelisierung selbst: Wenn mehrere Worker Ergebnisse in nicht-deterministischer Reihenfolge zusammenführen, kann selbst bei fixem Seed das Endergebnis schwanken. Wer Bit-Genauigkeit braucht, muss entweder die Aggregationsreihenfolge erzwingen oder bewusst auf etwas Parallelität verzichten.

Abhängigkeiten einfrieren: das Pinning-Problem.

Die unsichtbarste Fehlerquelle sind die Bibliotheken. Ein Backtest, der vor einem Jahr mit einer bestimmten Version einer Numerik-Bibliothek lief, kann nach einem Update minimal andere Zahlen liefern — weil sich ein Algorithmus, eine Default-Einstellung oder eine Rundungsregel geändert hat. Solche Differenzen sind klein, aber sie summieren sich über tausende Trades.

Die Antwort ist striktes Pinning: nicht „pandas“, sondern die exakte Version, und zwar nicht nur der direkten, sondern auch der transitiven Abhängigkeiten. Ein Lock-File, das den kompletten Dependency-Baum mit exakten Versionen und Prüfsummen festhält, ist das Minimum. Noch robuster ist es, die gesamte Laufzeitumgebung in einem Container festzuschreiben, sodass selbst die System-Bibliotheken unter der Python-Ebene fixiert sind.

EbeneWas variieren kannAbsicherung
Direkte PaketeAPI, Defaultsexakte Versions-Pins
Transitive Paketestille Algorithmus-ÄnderungLock-File mit Hashes
System-BibliothekenBLAS, CompilerContainer-Image
HardwareCPU/GPU-Arithmetikdokumentieren, ggf. fixieren

Daten versionieren: der am häufigsten übersehene Teil.

Code lässt sich versionieren, das ist Standard. Daten werden viel zu oft als gegeben behandelt — und genau das bricht Reproduzierbarkeit am häufigsten. Marktdaten sind nicht statisch: Kurse werden nachträglich korrigiert, Corporate Actions verändern historische Reihen rückwirkend, ein Vendor liefert für denselben Tag heute andere Werte als vor einem Jahr. Wer gegen „die Datenbank“ backtestet, testet gegen ein bewegliches Ziel.

Die Lösung ist, den Datenstand zum Zeitpunkt des Backtests als unveränderlichen Snapshot festzuschreiben und mit einer Version zu kennzeichnen. Der Backtest referenziert dann nicht „die aktuellen Daten“, sondern „Snapshot vom 14.03., Hash abc123“. Wird die Strategie später erneut geprüft, läuft sie gegen exakt denselben Datenstand — und eine Abweichung kann nicht mehr an stillen Datenkorrekturen liegen.

Das gilt nicht nur für Kursdaten, sondern für jede Eingabe: Fundamentaldaten, Indexzusammensetzungen, Kalender, sogar die Parameterdateien. Alles, was in das Ergebnis einfließt, muss versioniert und referenzierbar sein, sonst hat man eine offene Flanke.

Die Umgebung als Ganzes festschreiben.

Selbst wenn Code, Seeds, Pakete und Daten fixiert sind, kann die Umgebung drumherum Ergebnisse verschieben. Die Zeitzone des Systems beeinflusst, wie Bars geschnitten werden. Das Locale verändert, wie Zahlen geparst werden. Eine Umgebungsvariable schaltet einen anderen Code-Pfad frei. Diese Dinge sind unsichtbar, bis sie ein Ergebnis verändern.

Der saubere Weg ist, die komplette Ausführungsumgebung deklarativ zu beschreiben und in einem Container zu kapseln — inklusive Betriebssystem-Basis, Systembibliotheken, Zeitzone, Locale und allen relevanten Umgebungsvariablen. Ein Backtest läuft dann nicht „auf meinem Laptop“, sondern in einem definierten, reproduzierbaren Image, das überall identisch startet.

Dazu gehört, jeden Lauf mit einem Manifest zu protokollieren: welcher Code-Commit, welcher Daten-Snapshot, welche Seeds, welches Container-Image, welche Konfiguration. Dieses Manifest ist die Eintrittskarte zur Reproduktion — mit ihm kann jeder den Lauf nachstellen, ohne es ist jede Reproduktion Detektivarbeit.

Was Reproduzierbarkeit kostet — und was sie spart.

Ehrlich gesagt: Reproduzierbarkeit sauber aufzusetzen kostet zu Beginn Zeit. Realistisch sind für ein bestehendes, gewachsenes Backtest-Setup mehrere Tage bis wenige Wochen, bis Seeding, Pinning, Daten-Snapshots und Container sauber stehen. Das ist Aufwand, der sich nicht in der nächsten Equity-Kurve zeigt, sondern erst, wenn etwas schiefgeht.

Genau dann zahlt er sich aus. Wenn eine Strategie im Live-Betrieb anders performt als im Backtest, ist die erste Frage immer: Liegt es an der Strategie oder am Setup? Mit reproduzierbarer Umgebung kann man den alten Backtest exakt nachstellen und die Differenz eingrenzen. Ohne sie beginnt ein Ratespiel, das oft tagelang Kapazität bindet und manchmal gar nicht aufgelöst wird.

Hinzu kommt der Vertrauens- und Compliance-Aspekt: Wer einem Investor, einem Partner oder einer Prüfung gegenübersteht, muss zeigen können, wie ein Ergebnis zustande kam. Eine nicht reproduzierbare Behauptung hält dieser Frage nicht stand.

Wo man es übertreiben kann.

Reproduzierbarkeit ist wichtig, aber nicht jede Stufe lohnt für jeden Zweck. Für eine schnelle, explorative Idee, die in einer Stunde verworfen oder weiterverfolgt wird, ist voller Container-Aufbau übertrieben — hier genügen ein fixer Seed und ein klarer Datenbezug. Der hohe Aufwand gehört zu den Strategien, die in Produktion gehen oder die man verteidigen muss.

Auch die Jagd nach absoluter Bit-Genauigkeit hat Grenzen. Auf manchen GPU-Setups ist sie nur mit erheblichem Performance-Verlust erreichbar. Wenn die statistische Reproduzierbarkeit innerhalb einer engen, dokumentierten Toleranz liegt und man die Quelle der Restabweichung kennt und benennt, ist das für viele Zwecke ausreichend — solange man ehrlich ist, dass es keine Bit-Genauigkeit ist.

Die Kunst liegt im Abstufen: Explorationsphase leichtgewichtig, Produktionsstrecke streng. Wer jeden Wegwerf-Versuch in Container packt, verlangsamt sich ohne Nutzen; wer die Produktionsstrategie nicht reproduzierbar macht, baut auf Sand.

Sie wollen Ihre Backtest-Strecke so aufstellen, dass Ergebnisse auch in zwei Jahren noch prüfbar sind? Unverbindlich anfragen — wir schauen gemeinsam auf Ihr aktuelles Setup, finden die offenen Flanken bei Seeds, Dependencies und Daten und priorisieren, wo strenge Reproduzierbarkeit wirklich zählt.