1 september 2026 · corporate actions

Split-aangepaste data, ontleed: wat de 10-voor-1 van NVDA met elke rij erachter doet

Split-aangepaste data, ontleed: wat de 10-voor-1 van NVDA met elke rij erachter doet

Hier is één rij uit een dagelijks aandelenbestand. NVDA, vrijdag 7 juni 2024, de laatste sessie voordat de 10-voor-1-split van kracht werd.

KolomWaarde
date2024-06-07
close1208.88
volume42,800,000 (afgerond)
split_factor1.0
dividend0.00
adj_close120.8781

Ik heb de OHLC-kolommen in dit overzicht bewust weggelaten. Ze komen tegen het einde terug, en het zijn precies die kolommen die stilletjes stukgaan.

Alles wat aan deze rij interessant is, zit in de kloof tussen kolom twee en kolom zes. De tape op 7 juni printte 1208.88. Niemand zal ooit nog tegen die prijs handelen, in die zin dat het aandeel waarnaar die prijs verwees als eenheid niet meer bestaat. De 120.8781 is een reconstructie, en het is niet simpelweg 1208.88 gedeeld door tien. Laten we kolom voor kolom langslopen.

10:1deler toegepast op elke eerdere rij
0.99991789dividendfactor, één dag later gestempeld
$45fantoomcommissie per round trip
2.8ppjaarlijkse drag verzonnen door een deler

date: 2024-06-07, en de twee andere data die er meer toe doen

De split werd op 22 mei 2024 aangekondigd samen met de kwartaalcijfers. De record date was 6 juni. De handel op split-aangepaste basis begon op maandag 10 juni. Drie data, en de split_factor kolom van je vendor zit op precies één daarvan, meestal de eerste split-aangepaste sessie. Off-by-one is hier de meest voorkomende corporate-action-bug die ik zie, en in het aggregaat is hij onzichtbaar: je equity curve krijgt één 90% gap-down of één 900% gap-up op een enkele dag, die een Sharpe berekend over 2,000 dagen nauwelijks opmerkt, maar een max-drawdown of een stop-lossregel absoluut wel.

Het dividend heeft een eigen datumprobleem, en in 2024 veranderde dat onder ieders voeten. Het kwartaaldividend van $0.01 van NVDA na de split had een record date van 11 juni. De Amerikaanse settlement ging op 28 mei 2024 over naar T+1, wat betekent dat ex-date en record date nu op dezelfde dag vallen. Elke loader die de ex-date afleidt als "record date minus één werkdag", wat decennialang klopte, ging in juni 2024 stilletjes dividendaanpassingen één sessie te vroeg produceren. Er wordt geen exception gegooid. De factor landt gewoon op de verkeerde rij.

De failure mode van corporate-action-code is nooit een exception. Het is een getal dat een dag ernaast zit en er volkomen geloofwaardig uitziet.

close: 1208.88 — de prijs die niet in het bestand staat

Dit is de ruwe print, en in de meeste research-pipelines bestaat hij alleen als een kolom die niemand leest. Signalen worden berekend op adj_close. En elke regel die naar een prijsniveau per aandeel in dollars verwijst, verwijst nu naar een getal dat geen enkele marktpartij ooit heeft gezien.

NVDA's klim naar $1,000 in het voorjaar van 2024 was een echt verschijnsel met echte orderstroom erachter: magneten van ronde getallen, options strikes geclusterd op 950 en 1000, headline-risico bij elke nadering. In aangepaste data speelt die hele episode zich af op $100. Als je gedrag rond psychologische niveaus test, gap-fill-logica bij ronde getallen, of een strike-proximity-feature, test je tegen een prijsreeks die is herschaald door een gebeurtenis die toen nog niet had plaatsgevonden.

Hetzelfde geldt voor universe-filters. "Sluit aandelen onder $5 uit" is een legitieme proxy voor een echt cluster van microstructuurproblemen: wijde relatieve spreads, marginability-regels, tick-regimes. Draai het tegen aangepaste historie en je sluit bedrijven uit voor periodes waarin ze op $30 handelden en later een reverse split kregen. Draai "prijs boven $500" en je laat NVDA stilletjes vallen uit elk jaar vóór juni 2024. Prijsfilters horen thuis op onaangepaste prints. Punt.

split_factor: 1.0 — en de 0.1 op de volgende rij

De factor is een cumulatief product dat vanaf vandaag achteruit loopt. Elke rij vóór 10 juni 2024 draagt een 0.1 van deze split, vermenigvuldigd met de 0.25 van de 4-voor-1 in 2021, vermenigvuldigd met wat daarvoor kwam. De pre-2021-historie van NVDA is geschaald met 0.025, dus een bar die in 2019 op $200 printte, verschijnt als $5.00.

Twee gevolgen om vast te houden. Ten eerste: de aangepaste historie verandert elke keer dat er een nieuwe corporate action landt. Een backtest die je in mei 2024 draaide en een die je vandaag draait, leveren verschillende getallen op met identieke code over identieke data, en als je features hebt gecached met sleutel op symbool en datum zonder dataversiestempel, heb je nu een cache die twee schalen mengt. We nemen de aanpassingsepoch om precies die reden op in de feature-sleutel; het heeft ons één bijzonder verwarrende middag gekost om dat te leren.

Ten tweede, bij een schaling van 0.025 beweegt een aandeel dat in ticks van $0.01 handelde nu in stappen van $0.00025. Je tick-afrondingslogica, je vergelijkingen van het type "is dit een nieuw high", je float-gelijkheidschecks: ze werken nu allemaal ver onder de resolutie die de markt daadwerkelijk had.

dividend: 0.01 — het getal dat elke prijs erachter van het raster haalt

Eén cent. Het lijkt ruis, en het is de reden dat adj_close 120.8781 leest in plaats van een nette 120.888.

De close van 10 juni was 121.79. De ex-dividendfactor is (121.79 − 0.01) / 121.79 = 0.99991789. Die factor vermenigvuldigt elke prijs vóór 11 juni, dus de split-aangepaste 120.888 wordt 120.878074. Klein, en permanent, en opnieuw toegepast op elk van de ruim zestig ex-dates terug door de historie van NVDA. De compounding is wat telt: tegen de tijd dat je naar 2015 kijkt, kijk je naar een prijs die is vermenigvuldigd met tientallen min of meer irrationale factoren. Niet één aangepaste historische prijs ligt op het centenraster.

Wat betekent dat elke == vergelijking tegen een rond niveau onwaar is, elke check "hebben we de limietprijs geraakt" een tolerantie nodig heeft, en je gereconstrueerde OHLC niet langer voldoet aan de invariant dat low ≤ open ≤ high na een naïeve afronding op 4 decimalen. Het betekent ook dat adj_close een total-returnreeks is terwijl close een price-returnreeks is, en als je rendementen uit de ene berekent en drawdowns uit de andere, heb je twee verschillende definities van wat er gebeurde door elkaar gehaald.

volume: 42,800,000 — de deler die niemand consistent toepast

Het stuksvolume moet met 10 worden vermenigvuldigd om vergelijkbaar te zijn met sessies na de split. Sommige vendors doen het. Sommige niet. Sommige passen de OHLC aan en laten volume rauw, wat het slechtste van de drie is omdat het bestand intern consistent lijkt tot je prijs met volume vermenigvuldigt.

De eerlijke zet is om te stoppen met stuksvolume als feature en dollarvolume te gebruiken, dat per constructie invariant is voor splits. Een liquiditeitsfilter op 20-daags gemiddeld dollarvolume heeft überhaupt geen aanpassingslogica nodig en kan niet stukgaan door een corporate action. Feature-invariantie verslaat correcte aanpassingscode, omdat correcte aanpassingscode iets is waar je steeds opnieuw gelijk in moet hebben.

Drie vormen van vendor-bestand, drie failure modes.

Wat geleverd wordtWat stukgaat
Alleen adjusted close, ruwe OHLCGap- en intrabar-logica rekent over twee prijsschalen
Aangepaste OHLC, ruw volumeDollarvolume ernaast met de splitratio; ADV-filters slaan mis
Ruwe OHLCV plus factorkolommenNiets, als je ze toepast. Alles, als je ze op de verkeerde rij toepast

De kolommen die hier niet staan: adj_open, adj_high, adj_low

Zet de OHLC er nu weer in. Als een feed alleen de close aanpast en je strategie een overnight gap berekent als open / prev_adj_close − 1, dan rekent die op 10 juni 2024 120.37 / 120.8781 − 1, een prima getal, grofweg −0.4%. Duw dezelfde code terug naar 7 juni 2024 en hij rekent de open uit het ruwe bestand (ongeveer 1207) tegen een close die met 0.025 is aangepast door oudere splits, en produceert een gap van enkele duizenden procent.

Alles wat een ruwe kolom met een aangepaste mengt, is rekenkunde over twee coördinatenstelsels. Ik behandel mixed-adjustment-reads nu als een assertion op loader-niveau in plaats van iets wat een researcher moet onthouden, want researchers doen dat niet, ikzelf inbegrepen.

Wat geen enkele kolom je vertelt: hoeveel aandelen je daadwerkelijk zou hebben gekocht

Dit is degene die geld kost in het kostenmodel van de backtest, en het is de reden dat ik überhaupt over een aandelensplit schrijf.

Commissies op Amerikaanse aandelen zijn vaak per aandeel. Noem het $0.005 met een orderminimum van $1.00, een gangbare vaste staffel bij retailbrokers. Backtest nu een NVDA-positie van $400,000 een jaar voor de split, toen de ruwe prijs rond $400 lag en de aangepaste prijs als $40 verschijnt.

AandelenCommissie
Wat er echt gebeurde (ruw $400)1,000$5.00
Wat de backtest ziet (aangepast $40)10,000$50.00

Dezelfde exposure, dezelfde trade, 10x de gemodelleerde kosten. Dat is $45 fictie per leg, $90 per round trip, en een strategie die dagelijks turnover draait eet zo'n $22,000 per jaar aan kosten die nooit hebben bestaan. Op een boek van $400k is dat 5.6 procentpunt jaarlijks rendement, volledig gefabriceerd door een deler. Hier laat het je slechter lijken dan de werkelijkheid, wat overleefbaar is.

Reverse splits werken de andere kant op, en dat is de gevaarlijke richting. Een 1-voor-10 reverse split betekent dat de aangepaste historie prijzen 10x hoger en aantallen aandelen 10x lager toont dan wat je zou hebben verhandeld. Je kostenmodel deelt de commissie per aandeel stilletjes door tien. Het universum waarin reverse splits gebruikelijk zijn, is het small-cap-, sub-$5-, wijde-spread-universum waar fees per aandeel de grootste enkele post in de kostenstapel zijn. De backtest vleit precies de namen die hij zou moeten afstraffen.

De fix is dus saai en structureel: bepaal posities in dollars, zet om naar aandelen met de onaangepaste prijs voor die datum, en reken fees per aandeel over dat aantal. Houd de ruwe prijskolom voor altijd in de loader, niet als audit-artefact maar als live input voor de fill-engine.

  1. Signalen en rendementen: aangepaste reeks.
  2. Aantallen aandelen, fees per aandeel, tick-afronding, prijsniveaufilters: onaangepaste reeks.
  3. Liquiditeit en capacity: dollarvolume, dat geen van beide nodig heeft.

Zes kolommen, één rij, één vrij gewone corporate action die drie weken van tevoren werd aangekondigd en niemand verraste. De markt had het in een ochtend afgehandeld. Het getrouw reconstrueren in een research-pipeline is een stuk lastiger, en het teken dat een pipeline dat niet heeft gedaan is bijna nooit een foutmelding.

corporate actionssplitaanpassingAmerikaanse aandelendata-engineeringbacktesten
← Alle artikelen