Hier ist eine Zeile aus einer täglichen Aktiendatei. NVDA, Freitag, der 7. Juni 2024, die letzte Sitzung vor Inkrafttreten des Splits 10:1.
| Spalte | Wert |
|---|---|
| Datum | 2024-06-07 |
| Schlusskurs | 1208.88 |
| Volumen | 42,800,000 (gerundet) |
| split_factor | 1.0 |
| Dividende | 0.00 |
| adj_close | 120.8781 |
Die OHLC-Spalten habe ich hier bewusst weggelassen. Gegen Ende kommen sie wieder, und genau sie sorgen für die stillen Fehler.
Alles Interessante an dieser Zeile steckt in der Lücke zwischen Spalte zwei und Spalte sechs. Der Ticker zeigte am 7. Juni 1208.88. Zu diesem Kurs wird nie wieder jemand handeln, denn die Aktie, auf die sich der Preis bezog, gibt es als Einheit nicht mehr. Die 120.8781 ist eine Rekonstruktion und nicht einfach 1208.88 geteilt durch zehn. Gehen wir die Spalten der Reihe nach durch.
date: 2024-06-07 und die beiden anderen, wichtigeren Daten
Der Split wurde am 22. Mai 2024 zusammen mit den Geschäftszahlen angekündigt.
Der Stichtag war der 6. Juni. Ab Montag, dem 10. Juni, wurde auf
splitbereinigter Basis gehandelt. Drei Daten, und die Spalte
split_factor deines Datenanbieters bezieht sich genau auf eines
davon, meist auf die erste Sitzung nach dem Split. Ein Fehler um einen Tag ist
der häufigste Fehler bei Kapitalmaßnahmen, den ich sehe, und in aggregierten
Kennzahlen bleibt er unsichtbar: Deine Equity-Kurve bekommt an einem einzigen
Tag entweder eine Lücke von 90% nach unten oder von 900% nach oben. Ein über
2,000 Tage berechneter Sharpe bildet das kaum ab, ein maximaler Drawdown oder
eine Stop-Loss-Regel dagegen sehr wohl.
Auch bei der Dividende gibt es ein Datumsproblem, und 2024 hat sich die Grundlage dafür für alle geändert. NVDA hatte für die Quartalsdividende von $0.01 nach dem Split den 11. Juni als Stichtag. Am 28. Mai 2024 wechselten die USA zur Abwicklung T+1. Ex-Tag und Stichtag fallen damit nun auf denselben Tag. Jeder Loader, der den Ex-Tag als „Stichtag minus einen Geschäftstag“ ableitet – was jahrzehntelang korrekt war –, begann im Juni 2024 stillschweigend, die Dividendenanpassung eine Sitzung zu früh vorzunehmen. Es gibt keinen Fehler. Nur der Faktor landet in der falschen Zeile.
Fehler in Code für Kapitalmaßnahmen äußern sich nie als Exception. Es ist eine Zahl, die um einen Tag verschoben ist und vollkommen plausibel aussieht.
close: 1208.88 – der Kurs, der nicht in der Datei steht
Das ist der unveränderte Börsenkurs, und in den meisten Research-Pipelines
existiert er nur als Spalte, die niemand liest. Signale werden auf
adj_close berechnet. Jede Regel, die sich auf einen Kurs pro
Aktie in Dollar bezieht, verwendet jetzt eine Zahl, die kein Marktteilnehmer
je gesehen hat.
NVDA näherte sich im Frühjahr 2024 Schritt für Schritt der Marke von $1,000. Das war real und ging mit realem Orderflow einher: runde Zahlen als Anziehungspunkte, gebündelte Optionsstrikes bei 950 und 1000, Schlagzeilenrisiko bei jedem Anlauf. In bereinigten Daten spielt sich die ganze Episode bei $100 ab. Wenn du das Verhalten an psychologischen Kursmarken, Gap-Fill-Logik in der Nähe runder Zahlen oder ein Strike-Nähe-Feature testest, prüfst du es anhand einer Kursreihe, die durch ein noch nicht eingetretenes Ereignis skaliert wurde.
Dasselbe gilt für Universe-Filter. „Aktien unter $5 ausschließen“ ist ein sinnvoller Näherungswert für eine Reihe realer Mikrostrukturprobleme: hohe relative Spreads, Regeln zur Beleihbarkeit, Tick-Größen. Wende den Filter auf bereinigte historische Daten an, und du schließt Unternehmen in Zeiträumen aus, in denen sie bei $30 notierten und später einen Reverse Split durchführten. Verwende „Kurs über $500“, und NVDA fällt für jedes Jahr vor Juni 2024 unbemerkt heraus. Kursfilter gehören auf unveränderte Börsenkurse. Punkt.
split_factor: 1.0 – und 0.1 in der nächsten Zeile
Der Faktor ist ein kumulatives Produkt, das von heute rückwärts berechnet wird. Jede Zeile vor dem 10. Juni 2024 enthält wegen dieses Splits einen Faktor von 0.1, multipliziert mit 0.25 aus dem Split 4:1 von 2021 und den Faktoren früherer Kapitalmaßnahmen. NVDA-Daten aus der Zeit vor 2021 werden mit 0.025 skaliert. Ein Balken, der 2019 bei $200 notierte, erscheint dadurch mit $5.00.
Zwei Folgen sollte man im Hinterkopf behalten. Erstens ändert sich die bereinigte Historie bei jeder neuen Kapitalmaßnahme. Ein Backtest, den du im Mai 2024 ausgeführt hast, und einer von heute liefern mit identischem Code über dieselben Daten unterschiedliche Ergebnisse. Wenn du Features nach Symbol und Datum zwischengespeichert hast, aber keine Datenversion erfasst, enthält dein Cache jetzt Daten in zwei verschiedenen Skalierungen. Genau deshalb nehmen wir die Anpassungsepoche in den Feature-Schlüssel auf. Eine ziemlich verwirrende Stunde hat uns das gelehrt.
Zweitens bewegt sich eine Aktie bei einer Skalierung mit 0.025, die zuvor in Ticks von $0.01 gehandelt wurde, jetzt in Schritten von $0.00025. Deine Tick-Rundung, deine Vergleiche nach dem Muster „ist das ein neues Hoch?“ und deine Float-Gleichheitsprüfungen: All das arbeitet nun weit unterhalb der Auflösung, die der Markt tatsächlich hatte.
dividend: 0.01 – die Zahl, die das Cent-Raster aller vorherigen Kurse verschiebt
Ein Cent. Sieht nach Rauschen aus und erklärt, warum
adj_close den Wert 120.8781 statt einer glatten 120.888 ausweist.
Der Schlusskurs am 10. Juni lag bei 121.79. Der Ex-Dividenden-Faktor ist (121.79 − 0.01) / 121.79 = 0.99991789. Mit diesem Faktor wird jeder Kurs vor dem 11. Juni multipliziert; aus dem splitbereinigten Kurs von 120.888 werden so 120.878074. Eine kleine, dauerhafte Anpassung, die bei jedem der rund sechzig Ex-Tage in NVDAs Historie erneut angewandt wird. Entscheidend ist der Zinseszinseffekt: Wenn du auf 2015 zurückblickst, siehst du einen Kurs, der mit Dutzenden Faktoren multipliziert wurde, die sich kaum sinnvoll als glatte Werte bezeichnen lassen. Kein einziger bereinigter historischer Kurs liegt mehr auf dem Cent-Raster.
Das bedeutet: Jeder ==-Vergleich mit einem runden Kursniveau ist
falsch, jede Prüfung „wurde der Limitkurs erreicht?“ braucht eine Toleranz,
und nach einer naiven Rundung auf 4 Nachkommastellen erfüllt dein
rekonstruierter OHLC-Datensatz nicht mehr die Invariante low ≤ open ≤ high.
Außerdem ist adj_close eine Total-Return-Reihe, während
close eine Kursrendite-Reihe ist. Wenn du Renditen aus der einen
und Drawdowns aus der anderen berechnest, vermischst du zwei unterschiedliche
Definitionen dessen, was passiert ist.
volume: 42,800,000 – der Divisor, den nicht alle konsequent anwenden
Damit das Aktienvolumen mit Sitzungen nach dem Split vergleichbar ist, muss es mit 10 multipliziert werden. Manche Datenanbieter tun das. Andere nicht. Wieder andere passen den OHLC-Datensatz an und lassen das Volumen unverändert. Das ist die schlechteste Variante, denn die Datei wirkt in sich konsistent, bis du Kurs mit Volumen multiplizierst.
Der sinnvollste Schritt ist, das Aktienvolumen nicht mehr als Feature zu verwenden und stattdessen das Dollarvolumen zu nehmen, das konstruktionsbedingt splitneutral ist. Ein Liquiditätsfilter auf Basis des durchschnittlichen Dollarvolumens über 20 Tage braucht keinerlei Anpassungslogik und kann durch keine Kapitalmaßnahme verfälscht werden. Invariante Features sind besser als korrekter Anpassungscode, denn man muss den Anpassungscode dauerhaft richtig halten.
Drei Formen von Anbieterdateien, drei Fehlerquellen.
| Gelieferte Daten | Was schiefgeht |
|---|---|
| Nur Schlusskurs bereinigt, OHLC unverändert | Gap- und Intraday-Logik rechnet über zwei Kursskalen hinweg |
| OHLC bereinigt, Volumen unverändert | Dollarvolumen um den Splitfaktor falsch; ADV-Filter greifen nicht korrekt |
| Unveränderter OHLCV-Datensatz plus Faktorspalten | Nichts, wenn du die Faktoren anwendest. Alles, wenn du sie auf die falsche Zeile anwendest |
Die fehlenden Spalten: adj_open, adj_high, adj_low
Fügen wir den OHLC-Datensatz wieder hinzu. Wenn ein Datenfeed nur den
Schlusskurs anpasst und deine Strategie das Overnight-Gap als
open / prev_adj_close − 1 berechnet, ergibt sich für den 10. Juni
2024 die Rechnung 120.37 / 120.8781 − 1. Das ist ein plausibler Wert von
ungefähr −0.4%. Führst du denselben Code für den 7. Juni 2024 aus, nimmt er
den Eröffnungskurs aus der unveränderten Datei (etwa 1207) und vergleicht ihn
mit einem Schlusskurs, der wegen früherer Splits mit 0.025 bereinigt wurde.
Das Ergebnis ist ein Gap von mehreren tausend Prozent.
Jede Rechnung, die eine unveränderte mit einer bereinigten Spalte vermischt, führt Rechenoperationen über zwei Koordinatensysteme hinweg aus. Gemischte Anpassungen behandle ich inzwischen als Assertion auf Loader-Ebene, statt von Researchern zu erwarten, dass sie daran denken. Das werden sie nicht, mich eingeschlossen.
Was keine Spalte verrät: wie viele Aktien du tatsächlich gekauft hättest
Genau dieser Punkt schlägt im Kostenmodell des Backtests zu Buche. Er ist der Grund, warum ich überhaupt über einen Aktiensplit schreibe.
Bei US-Aktien fallen Kommissionen oft pro Aktie an. Nehmen wir $0.005 mit einer Mindestgebühr von $1.00 pro Order, eine gängige feste Tarifstufe bei Retail-Brokern. Nun testest du eine NVDA-Position im Wert von $400,000 ein Jahr vor dem Split, als der unveränderte Kurs bei etwa $400 lag und der bereinigte Kurs mit $40 ausgewiesen wird.
| Aktien | Kommission | |
|---|---|---|
| Tatsächlicher Verlauf (unverändert $400) | 1,000 | $5.00 |
| Was der Backtest sieht (bereinigt $40) | 10,000 | $50.00 |
Gleiches Exposure, gleicher Trade, 10x so hohe modellierte Kosten. Das sind $45 fiktive Kosten pro Seite, $90 pro Roundtrip. Eine Strategie mit täglichem Umschlag verursacht so etwa $22,000 jährliche Kosten, die nie angefallen sind. Bei einem Portfolio von $400k entspricht das 5.6 Prozentpunkten der Jahresrendite, vollständig durch einen Divisor erzeugt. Hier lässt es dich schlechter dastehen, als du tatsächlich warst. Damit kann man leben.
Bei Reverse Splits läuft es andersherum, und das ist die gefährliche Richtung. Bei einem Reverse Split von 1:10 weist die bereinigte Historie Kurse aus, die 10x höher sind, und Aktienzahlen, die 10x niedriger liegen als die tatsächlich gehandelten. Dein Kostenmodell teilt die Kommission pro Aktie stillschweigend durch zehn. In dem Anlageuniversum, in dem Reverse Splits häufig sind – Small Caps unter $5 mit breiten Spreads –, sind Gebühren pro Aktie der größte Einzelposten im Kostenblock. Der Backtest beschönigt ausgerechnet die Werte, die er am stärksten bestrafen sollte.
Die Lösung ist einfach und strukturell: Positionen in Dollar dimensionieren, in Aktien umrechnen und dafür den unveränderten Kurs des jeweiligen Tages verwenden; Gebühren pro Aktie auf Basis dieser Stückzahl berechnen. Die unveränderte Kursspalte muss dauerhaft im Loader bleiben – nicht als Prüfbeleg, sondern als aktiver Input für die Fill-Engine.
- Signale und Renditen: bereinigte Kursreihe.
- Aktienzahlen, Gebühren pro Aktie, Tick-Rundung, Kursfilter: unveränderte Kursreihe.
- Liquidität und Kapazität: Dollarvolumen, das keine Anpassung braucht.
Sechs Spalten, eine Zeile, eine ziemlich gewöhnliche Kapitalmaßnahme, die drei Wochen im Voraus angekündigt wurde und niemanden überraschte. Der Markt hatte sie innerhalb eines Morgens verarbeitet. Sie in einer Research-Pipeline zuverlässig nachzubilden, ist deutlich schwieriger. Und dass eine Pipeline das nicht schafft, erkennt man fast nie an einer Fehlermeldung.
← Alle Beiträge


