Look-Ahead Bias: Wie ein Ein-Balken-Fehler aus reinem Rauschen eine Sharpe Ratio von 15 fabriziert
Teil der Serie "Backtests ohne Illusionen".
📄 Aus diesem Artikel ist ein Forschungspapier geworden. Drei subtile Look-Ahead-Lecks werden in einem kontrollierten Test gegen eine bekannte Ground Truth geprüft (4.000 simulierte Historien). Lesen Sie das Paper online (interaktive Version + PDF) auf lookahead.marketmaker.cc, Code und Daten unter github.com/suenot/lookahead-inflation.
Vor ein paar Wochen hat unser Parameter-Search-Benchmark uns belogen, und wir hätten es fast nicht bemerkt.
Die Engine wirkte sauber. Closed-Bar-Logik, ein ehrlicher rollierender Walk-Forward-Split, eine Sobol/QMC-Suche über den Parameterraum, ein zurückgehaltenes Testfenster. Die Suche fand Konfigurationen, die In-Sample gut aussahen. Das einzige Problem: Out-of-Sample war fast alles negativ. Wir nahmen an, die Strategie sei einfach schwach.
Dann fanden wir eine Zeile. Das Signal wurde beim Schluss von Balken i entschieden, aber die Ausführung wurde auf demselben Balken i verbucht statt auf dem Open des nächsten Balkens. Ein Off-by-One im Ausführungsindex. Wir verschoben die Ausführung auf open[i+1] — den einzigen Preis, zu dem man nach dem Schluss von Balken i tatsächlich handeln konnte — und das Out-of-Sample-Ergebnis drehte das Vorzeichen um. Die Sobol-Suche ging von einem Verlust in einen Gewinn über. An der Strategie hatte sich nichts geändert. Wir hatten nur aufgehört, in der Vergangenheit zu handeln.
Das ist Look-Ahead Bias, und das Beunruhigende ist, wie klein der Fehler war und wie groß die Konsequenz. Dieser Artikel ist ein kontrolliertes Selbst-Audit: Wir bauen einen Simulator, dessen Ground Truth durch Konstruktion bekannt ist, injizieren die subtilen Lecks eines nach dem anderen und messen genau, wie stark jedes davon den Backtest aufbläht. Die Kernaussage: mit überhaupt keinem realen Edge fabriziert eine Ausführung im selben Balken eine annualisierte Sharpe Ratio von +14,8 aus purem Rauschen.
Was Look-Ahead Bias tatsächlich ist

Look-Ahead Bias ist jeder Punkt in Ihrer Pipeline, an dem eine Entscheidung oder Messung Informationen nutzt, die in Echtzeit, zum Zeitpunkt ihrer Verwendung, nicht verfügbar gewesen wären. Die Lehrbuchbeispiele sind grob — die Verwendung der Jahresgewinne einer Aktie im Januar, oder eine Restatement, die noch nicht veröffentlicht war. Diese sind leicht zu erkennen. Diejenigen, die ein Code-Review überstehen, sind subtil und verstecken sich an drei Stellen:
- Ausführung — Sie entscheiden auf Balken
iund führen auf Balkeniaus (oder verwenden das Hoch/Tief von Balkenifür Stops auf genau dem Balken, der das Signal erzeugt hat). Sie handeln zu einem Preis, der mit dem korreliert ist, was Sie ausgelöst hat. - Normalisierung — Sie berechnen einen Z-Score, Min-Max oder skalieren ein Feature anderweitig anhand von Statistiken, die über die gesamte Serie berechnet wurden, einschließlich der Zukunft. Der Scaler "kennt" das Testset.
- Indikatoren / Features — Sie glätten oder filtern mit einem Fenster, das zentriert ist (oder anderweitig in die Zukunft blickt), sodass der Wert bei Balken
ibereits ein Stück von Balkeni+1enthält.
Alle drei sind Formen dessen, was die Machine-Learning-Literatur Leakage nennt: die Kontamination von Training/Evaluation mit Informationen aus der Zukunft des Ziels (Kaufman et al., 2012; Kapoor & Narayanan, 2023). Im Finanzbereich ist die kanonische Abhandlung López de Prados Advances in Financial Machine Learning (2018) — purged Cross-Validation, Embargoing, die Gefahren des Backtestings. Die Point-in-Time-Disziplin geht mindestens auf Fama & French (1992) zurück, die Buchhaltungsdaten bewusst um sechs Monate verzögern, damit die Variable bekannt ist, bevor die Rendite, die sie erklärt, eintritt.
Die Frage, die dieser Artikel beantwortet, ist quantitativ: nicht "ist Leakage schlecht" (darüber sind sich alle einig), sondern "wie viele Sharpe-Punkte kauft Ihnen jede Form ein, und welche sind gefährlich?" Ohne eine Zahl können Sie nicht darüber nachdenken. Sie können nicht unterscheiden, ob eine Aufblähung um +0,3 Rauschen ist oder eine Aufblähung um +14 ein Beweisstück.
Ein Simulator mit bekannter Ground Truth

Um Aufblähung zu messen, müssen Sie die Wahrheit kennen. Echte Daten verraten Ihnen nie die Wahrheit — sie geben Ihnen eine Realisierung und kein Orakel. Also bauen wir einen synthetischen Markt, in dem wir den Edge festlegen.
Der datengenerierende Prozess ist strikt kausal und nicht-explosiv:
Hier ist eine exogene persistente latente Drift (ein AR(1) mit ), und die Balkenrendite hat eine kleine Drift , die einen Balken im Voraus bekannt ist. Da nicht von vergangenen Renditen abhängt, gibt es kein Feedback und nichts explodiert. Der Parameter ist der Regler dafür, wie viel realer Edge existiert:
- — die Null: kein Edge überhaupt. Jede positive Backtest-Sharpe ist zu 100% ein Artefakt.
- — ein realer, handelbarer Edge: eine ehrliche Momentum-Regel verdient tatsächlich Geld.
Die Strategie ist bewusst einfach — eine Momentum-Vorzeichenregel. Das Feature ist die trailing--Summe der Renditen ( Balken), und die Position ist ihr Vorzeichen:
csum = np.concatenate(([0.0], np.cumsum(r))) # csum[k] = sum r[0..k-1]
mom = np.full(n, np.nan)
tt = np.arange(L - 1, n)
mom[tt] = csum[tt + 1] - csum[tt - L + 1]
signal = np.sign(mom) # position for the next bar
Dieses Momentum-Feature ist das perfekte Vehikel, um das Same-Bar-Leck zu untersuchen, weil es eine Eigenschaft hat, die echte Indikatoren teilen: es enthält mechanisch den aktuellen Balken. mom[t] enthält r[t]. Wenn Sie also r[t] als Ihren Trade verbuchen, wetten Sie teilweise auf eine Größe, die bereits in Ihrem eigenen Signal enthalten ist. Das ist das Leck, konkret gemacht.
Setup: (1% Volatilität pro Balken), eine Einweg-Gebühr von 0,00045 (Round-Trip 0,09%, passend zu unserer Engine), Sharpe annualisiert mit (stündliche Balken), 4.000 unabhängige Historien mit je 4.000 Balken. Alles ist geseedet und deterministisch.
Die ehrliche Pipeline (die einzig handelbare)
Entscheiden am Schluss von Balken t, die Rendite des nächsten Balkens verdienen, Gebühren bei Positionsänderungen zahlen:
def sharpe(sig, ret_booked):
dpos = np.abs(np.diff(np.concatenate(([0.0], sig))))
pnl = sig * ret_booked - FEE_ONEWAY * dpos
return pnl.mean() / pnl.std() * np.sqrt(8760)
honest = sharpe(signal[idx], r[idx + 1]) # earn r[t+1]: tradable
Die drei Lecks, jedes eine einzige chirurgische Änderung
same_bar = sharpe(signal[idx], r[idx])
z_full = (mom - mom[valid].mean()) / mom[valid].std()
norm_full = sharpe(np.sign(z_full[idx]), r[idx + 1])
z_sm = (mom[:-2] + mom[1:-1] + mom[2:]) / 3.0 # uses t-1, t, t+1
indicator = sharpe(np.sign(z_sm[idx]), r[idx + 1])
Jedes Leck ist eine Zeile von der ehrlichen Pipeline entfernt. Das ist der ganze Punkt: Das sind keine exotischen Fehler, es sind die Art von Dingen, die ein Review überstehen.
Ergebnisse: die Größenordnung jedes Lecks

Über 4.000 Seeds hinweg ausgeführt, hier die annualisierte Sharpe Ratio, die jede Pipeline meldet, unter der Null (kein Edge) und unter einem realen Edge (, so eingestellt, dass die ehrliche Sharpe ein glaubwürdiges +1,57 ist):
| Pipeline | Null (kein Edge) | Realer Edge |
|---|---|---|
| Ehrlich (die Wahrheit) | −0,74 | +1,57 |
| Same-Bar Fill | +14,79 | +15,85 |
| Indikator-Peek (1 Balken) | +4,76 | +6,62 |
| Whole-Series-Normalisierung | −0,84 | +1,46 |
95%-Konfidenzintervalle über die Seeds sind ±0,05 oder enger in jeder Zelle; gepaarte t-Tests auf die Aufblähung sind astronomisch signifikant, wo der Effekt real ist (t > 400, p ≈ 0).
Lesen Sie zuerst die Null-Spalte, denn sie ist das sauberste mögliche Experiment: Es gibt keinen Edge, also verliert die ehrliche Pipeline korrekterweise Geld (−0,74, der Widerstand durch Gebühren beim Handeln von Rauschen). Sehen Sie nun, was die Lecks mit diesem Nichts anstellen:
- Same-Bar Fill: −0,74 → +14,79. Eine Strategie ohne jede Vorhersagekraft, die reines Rauschen handelt, meldet eine annualisierte Sharpe Ratio von fast 15. Das ist keine subtile Verzerrung; es ist eine Fabrikation. Der Mechanismus ist genau der, den wir eingebaut haben: Das Momentum-Feature enthält
r[t], also ist das Verbuchen vonr[t]eine Wette auf das eigene Signal. - Indikator-Peek: −0,74 → +4,76. Wenn der Glätter einen Balken in die Zukunft blicken darf, fabriziert das eine Sharpe Ratio nahe 5 aus Rauschen, weil der geglättete Wert bei
tnun mit demr[t+1]korreliert, das Sie gerade verdienen werden. - Whole-Series-Normalisierung: −0,74 → −0,84. Praktisch keine Aufblähung. Das ist das ehrliche, nicht-offensichtliche Ergebnis (dazu mehr weiter unten).
Die Edge-Spalte liefert die noch heimtückischere Botschaft. Wenn ein realer Edge tatsächlich existiert (ehrlich +1,57), addieren die Lecks nicht nur eine Konstante — sie treiben die gemessene Sharpe Ratio auf +15,85 und +6,62, weit über die +1,57, die man tatsächlich handeln könnte. Die gemessene Zahl kann also Skill nicht von Leck unterscheiden. Ein geleaktes +6 und ein ehrliches +6 sehen im Report identisch aus. Man erfährt erst nach dem Einsatz von Kapital, welches von beiden man hatte.
Das Leck ist ein Gradient, kein Schalter

Ein natürlicher Einwand: "Den gesamten Signalbalken zu verbuchen ist ein extremer, unrealistischer Fehler." Also haben wir die Dosis durchgefahren — den Anteil des Signalbalkens, der vom Leck erfasst wird, von 0 (ehrlich) bis 1 (voller Same-Bar-Leck):
| Erfassungsanteil | Null-Sharpe | Edge-Sharpe |
|---|---|---|
| 0,00 (ehrlich) | −0,74 | +1,57 |
| 0,25 | +3,90 | +6,41 |
| 0,50 | +9,86 | +12,20 |
| 1,00 (volles Leck) | +14,79 | +15,85 |
Bereits das Erfassen von einem Viertel des Signalbalkens bringt eine Strategie ohne Edge von −0,74 auf +3,90. Man braucht nicht den vollen Off-by-One, um getäuscht zu werden; eine Ausführung, die etwas zu günstig ist — ein Hauch optimistischen Slippages auf dem Signalbalken, ein Intrabar-Stop, der gegen genau den Balken geprüft wird, der ihn ausgelöst hat — reicht, um die meisten "deploybaren" Schwellenwerte zu überschreiten. Die Aufblähung ist glatt und monoton darin, wie viel von der Gegenwart man sich selbst zu handeln erlaubt.
Wie oft bringt das eine verlustbringende Strategie in Produktion?
Die Zahl, die einen Praktiker beunruhigen sollte, ist die False-Deployment-Rate: wie oft ein Leck eine tatsächlich geldverlierende Konfiguration den Balken überschreiten lässt, den man zur Freigabe verwenden würde. Mit "annualisierte Sharpe ≥ 1,0" als Deploy-Kriterium, unter der Null:
- Same-Bar Fill: 68% der Strategien ohne Edge sehen deploybar aus und verlieren tatsächlich Geld. Zwei von drei reinen Rauschkonfigurationen würden ein Sharpe-≥-1-Gate passieren und live Geld verlieren. (Diese Rate ist hier sauber definiert, weil das Leck rein in der Ausführung liegt — das ehrliche Gegenstück ist dasselbe Signal mit einer ehrlichen Ausführung.)
- Indikator-Peek: Er drückt praktisch jede Strategie ohne Edge über die Deploy-Schwelle (99,9% erreichen Sharpe ≥ 1) — er würde Rauschen direkt in die Produktion winken.
- Whole-Series-Normalisierung: 12% überschreiten die Schwelle — im Wesentlichen die Basisrate von Rauschen, keine Leakage-Prämie.
Die Taxonomie, und wie man jedes Leck erkennt
Die drei Lecks sind nicht gleich gefährlich, und die Unterschiede sind lehrreich.
1. Ausführungs-Leakage (die teure)

Symptom: Der Ausführungspreis korreliert mit dem Signal, weil beide aus demselben Balken stammen. Größenordnung: enorm (+15 aus Rauschen bei voller Dosis, +3,9 bei Viertel-Dosis). Warum es das Schlimmste ist: Ihr Signal ist, fast per Definition, aus jüngstem Preisverhalten aufgebaut, also ist die Rendite des Signalbalkens genau das, womit Ihr Feature am stärksten korreliert. Sie zu verbuchen kommt dem Nachschlagen der Antwort nahe.
Erkennung — der One-Bar-Shift-Test. Dies ist die wertvollste einzelne Diagnose in diesem Artikel. Nehmen Sie Ihren Backtest und verschieben Sie jede Ausführung um einen Balken später (entscheiden auf i, ausführen auf open[i+1]). Wenn sich das Ergebnis kaum bewegt, war Ihre Ausführung ehrlich. Wenn das Ergebnis einbricht oder das Vorzeichen wechselt, haben Sie in der Vergangenheit gehandelt. Genau das ist mit unserer Sobol-Suche passiert: Ausführungen verschieben, und ein "profitables" OOS entpuppte sich als Verlust — oder besser gesagt, die echte Beziehung trat zutage, sobald das Leck entfernt war.
entry_price = open_[i + 1] # NOT close[i], NOT open[i]
2. Indikator-/Feature-Leakage (die stille)
Symptom: Ein Indikator bei Balken i hängt von Daten aus i+1 oder später ab — ein zentrierter gleitender Durchschnitt, ein Filter ohne kausale Verzögerung, ein Hoch-/Tiefpunkt-Label, das zur Bestätigung zukünftige Balken benötigt, eine Heikin-Ashi-artige Transformation, die mit zukünftigen Kerzen gefüttert wird. Größenordnung: groß (+4,8 aus Rauschen). Warum es sich versteckt: Das Leck ist tief in einem Bibliotheksaufruf vergraben. scipy.signal.filtfilt ist Zero-Phase — und Zero-Phase bedeutet nicht-kausal. Ein Feature "dieser Balken ist ein lokales Maximum" ist unbekannbar, bis der nächste Balken gedruckt wird.
Erkennung: Fragen Sie für jeden Indikator: welchen höchsten Index liest er? Wenn die Berechnung des Werts bei t jemals t+1 berührt, ist er nicht-kausal. Berechnen Sie Indikatoren über ein expandierendes/rollierendes kausales Fenster und überprüfen Sie, dass der Wert bei Balken t identisch ist, egal ob Balken nach t im Array existieren oder nicht. (Unsere HMA-/ADX-Implementierungen bestehen diesen Test: Jede Ausgabe bei t liest nur Eingaben bei ≤ t.)
3. Normalisierungs-Leakage (die kanalspezifische)
Symptom: Ein Scaler (StandardScaler, Min-Max, ein globaler Z-Score) wird auf dem gesamten Datensatz gefittet, Testset eingeschlossen. Die kanonischen ML-Warnungen sind explizit dazu — Hastie, Tibshirani & Friedmans Elements of Statistical Learning §7.10.2 ("the wrong and right way to do cross-validation") und der eigene Common-Pitfalls-Guide von scikit-learn: "the average should be the average of the train subset, not the average of all the data."
Größenordnung in unserem Test: ≈ null (−0,74 → −0,84). Das ist das überraschende, ehrliche Ergebnis, und es lohnt sich, es zu verstehen statt es auswendig zu lernen.
Warum hat es nicht aufgebläht? Weil unsere Strategie das Feature nur über sein Vorzeichen verwendet (eine Nullschwelle). Standardabweichungs-Skalierung ändert nie ein Vorzeichen, und globales Mean-Centering verschiebt den Nulldurchgang nur geringfügig. Also ist die Whole-Series-Standardisierung einer reinen Vorzeichenregel nahezu harmlos.
Verallgemeinern Sie das nicht. Normalisierungs-Leakage ist kanalspezifisch. In dem Moment, in dem Ihre Strategie die Magnitude des Features nutzt — Positionsgrößen proportional zu einem Z-Score, ein Nicht-Null-Einstiegsschwellenwert, der durch Betrachten der skalierten Verteilung gewählt wurde, ein neuronales Netz, das standardisierte Inputs konsumiert —, beginnt der zukunftsbewusste Scaler eine Rolle zu spielen, und zwar umso mehr, je stärker sich die globalen Statistiken von den kausalen unterscheiden. Unser Ergebnis ist nicht "Normalisierungs-Leakage ist sicher." Es ist "die Größenordnung der Leakage hängt vom Kanal ab, durch den die geleakte Größe in die Entscheidung eintritt, und man sollte sie messen statt sie anzunehmen." Eine Vorzeichenregel ist der eine Fall, in dem dieses spezielle Leck billig ist.
Wo das anknüpft
Look-Ahead Bias ist das erste Glied in einer Kette, die diese Serie dokumentiert:
- Es korrumpiert den Input zur Validierung. Ein geleakter Backtest wird problemlos durch einen Walk-Forward-Split segeln und wie ein breites Plateau statt eines Overfit-Peaks aussehen — das Leck ist über die Folds hinweg konsistent, sodass Cross-Validation es nicht erkennen kann. Leakage ist der Fehlermodus vor dem Overfitting, und keine noch so ehrliche Validierung danach wird Sie retten.
- Es interagiert mit Parameter Search: Eine Suche über Tausende Trials auf geleakten Daten wird die Konfiguration finden, die das Leck am aggressivsten ausnutzt. Der "Gewinner" ist der schlimmste Übeltäter.
- Es ist der Grund, warum Backtest-Live-Parität auseinanderdriftet. Ein Leck ist die sauberste Erklärung für eine Lücke von 30–50% zwischen Backtest und Bot, weil Live-Trading mechanisch der eine Ort ist, an dem man nicht spicken kann.
Die Disziplin, die all das erfasst, ist dieselbe, zu der die akademische Literatur seit Jahren mahnt: Behandeln Sie einen Backtest als statistisches Experiment mit einer strikten Informationsgrenze. Bailey, Borwein, López de Prado & Zhu zeigten, wie leicht Overfitting gefälschte Performance fabriziert (2014); Arnott, Harveys & Markowitz' Backtesting-Protokoll (2019) kodifiziert die Hygiene. Look-Ahead Bias ist die grundlegendste Grenze von allen — die Grenze in der Zeit — und die billigste, um sie versehentlich zu verletzen.
Zum Mitnehmen

- Look-Ahead Bias ist quantitativ riesig und qualitativ unsichtbar. Ein einziger Ein-Balken-Ausführungsfehler verwandelte eine Sharpe Ratio von −0,74 (reines Rauschen, korrekterweise verlierend) in +14,79. Der Fehler ist eine Zeile; die Konsequenz ist ein fabrizierter Track Record.
- Es ist ein Gradient. Das Erfassen von auch nur 25% des Signalbalkens ergibt +3,90 aus dem Nichts. Man braucht keinen offensichtlichen Bug — ein bisschen zu viel Optimismus bei den Ausführungen reicht.
- Die gemessene Zahl kann Skill nicht von Leck unterscheiden. Wenn ein realer Edge existiert, blähen Lecks den Report weit über die handelbare Wahrheit hinaus auf. Die einzige Verteidigung ist der Prozess, nicht die Metrik.
- Der One-Bar-Shift-Test ist Ihre schnellste Diagnose. Verschieben Sie jede Ausführung um einen Balken später. Bricht die Performance ein, haben Sie in der Vergangenheit gehandelt.
- Die Größenordnung der Leakage ist kanalspezifisch. Ausführungs- und Indikator-Peeks sind verheerend; die Whole-Series-Normalisierung einer Vorzeichenregel ist nahezu kostenlos. Messen Sie das Leck durch den Kanal, über den es tatsächlich eintritt — nehmen Sie es nicht an.
Die vollständige kontrollierte Studie — alle drei Lecks, der Dosis-Sweep, die False-Deployment-Analyse, die formalen Methoden und jede Zahl reproduzierbar aus einem einzigen deterministischen Skript — steht im begleitenden Paper unter lookahead.marketmaker.cc, mit Code und Daten unter github.com/suenot/lookahead-inflation.
Die Strategie in unserem Null-Experiment hatte überhaupt keinen Edge. Sie zeigte trotzdem eine Sharpe Ratio von 15. Wenn Ihr Backtest zu gut aussieht, ist das Erste, was Sie verdächtigen sollten, nicht Ihr Genie — es ist Ihre Uhr.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.