Her er én rad fra en daglig aksjefil. NVDA, fredag 7. juni 2024, siste handelsdag før 10-for-1-splitten trådte i kraft.
| Kolonne | Verdi |
|---|---|
| dato | 2024-06-07 |
| sluttkurs | 1208.88 |
| volum | 42,800,000 (avrundet) |
| split_factor | 1.0 |
| utbytte | 0.00 |
| justert sluttkurs | 120.8781 |
Jeg har bevisst utelatt OHLC-kolonnene her. De kommer tilbake mot slutten, og det er de som i det stille skaper problemer.
Alt det interessante ved denne raden ligger i avstanden mellom kolonne to og kolonne seks. Tape-avlesningen 7. juni viste 1208.88. Ingen kommer noensinne til å handle til den prisen igjen, fordi aksjen den viste til, ikke lenger finnes som enhet. 120.8781 er en rekonstruksjon, og det er ikke bare 1208.88 delt på ti. La oss gå gjennom kolonnene én etter én.
dato: 2024-06-07, og de to andre datoene som betyr mer
Splitten ble annonsert 22. mai 2024 sammen med resultatene.
Registreringsdatoen var 6. juni. Handelen startet på splittjustert basis
mandag 10. juni. Tre datoer, og split_factor-kolonnen fra
dataleverandøren din ligger på nøyaktig én av dem, som regel den første
splittjusterte handelsdagen. Feil med én dag er den vanligste feilen i kode
for selskapshendelser jeg ser, og den er usynlig i aggregater:
egenkapitalkurven får et fall på 90 % eller et hopp på 900 % på én enkelt dag.
Det gir knapt utslag på en Sharpe beregnet over 2,000 dager, men det merkes
godt på største verdifall eller en stop-loss-regel.
Utbyttet har et eget datoproblem, og reglene endret seg mens alle så på i 2024. NVDA-utbyttet etter splitten på $0.01 per kvartal hadde registreringsdato 11. juni. Oppgjør i USA gikk over til T+1 28. mai 2024, noe som betyr at ex-dato og registreringsdato nå er samme dag. Alle datalastere som beregner ex-dato som «registreringsdato minus én virkedag» — noe som var riktig i flere tiår — begynte i stillhet å justere utbyttet én handelsdag for tidlig i juni 2024. Ingenting gir en feil. Faktoren havner bare på feil rad.
Feil i kode for selskapshendelser gir aldri unntak. De gir et tall som er én dag feil, men ser helt plausibelt ut.
sluttkurs: 1208.88 — prisen som ikke finnes i filen
Dette er den ujusterte noteringen, og i de fleste forskningspipeliner finnes
den bare som en kolonne ingen leser. Signaler beregnes på
adj_close. Og enhver regel som viser til et prisnivå i dollar per
aksje, viser nå til et tall ingen markedsaktører noensinne så.
NVDA-oppgangen mot $1,000 våren 2024 var reell, med reelle ordre bak: runde tall som magneter, opsjonsstrikepriser samlet rundt 950 og 1000, og nyhetsrisiko hver gang kursen nærmet seg. I justerte data skjer hele denne episoden ved $100. Hvis du tester atferd rundt psykologiske prisnivåer, logikk for å fylle gap nær runde tall eller en funksjon for avstand til strikepris, tester du mot en prisserie som ble skalert om av en hendelse som ennå ikke hadde skjedd.
Det samme gjelder filtre for aksjeuniverset. «Utelat aksjer under $5» er en legitim stedfortreder for en reell samling mikrostrukturproblemer: store relative spreader, regler for belåningsadgang og tick-regimer. Bruk det på justert historikk, så utelater du selskaper i perioder da de ble handlet til $30 og senere gjennomgikk en omvendt split. Bruk «kurs over $500», så fjerner du i stillhet NVDA fra hvert år før juni 2024. Prisfiltre skal bruke ujusterte noteringer. Ferdig snakka.
split_factor: 1.0 — og 0.1 på neste rad
Faktoren er et kumulativt produkt som beregnes bakover fra i dag. Hver rad før 10. juni 2024 har en 0.1 fra denne splitten, multiplisert med 0.25 fra 4-for-1-splitten i 2021, multiplisert med alt som kom før. NVDA-historikken før 2021 skaleres med 0.025, så en bar som ble handlet til $200 i 2019, vises som $5.00.
To følger det er verdt å huske. For det første endres den justerte historikken hver gang en ny selskapshendelse inntreffer. En backtest du kjørte i mai 2024, og en du kjører i dag, gir ulike tall med identisk kode over identiske datoer. Hvis du bufret funksjoner med symbol og dato som nøkkel uten et dataversjonsstempel, har du nå en buffer med blandede skalaer. Vi tar med justeringsepoken i nøkkelen til funksjonen nettopp av denne grunnen; det kostet oss en svært forvirrende ettermiddag å lære.
For det andre: Med skalering på 0.025 beveger en aksje som ble handlet i tick på $0.01 seg nå i trinn på $0.00025. Tick-avrundingslogikken din, sammenligningene for «er dette en ny toppnotering» og flyttallssammenligningene dine: Alle opererer nå langt under oppløsningen markedet faktisk hadde.
utbytte: 0.01 — tallet som forskyver alle priser bakover fra ørenettet
Én cent. Det ser ut som støy, men det er grunnen til at
adj_close viser 120.8781 i stedet for et pent 120.888.
Sluttkursen 10. juni var 121.79. Ex-utbyttefaktoren er (121.79 − 0.01) / 121.79 = 0.99991789. Faktoren multipliserer alle priser før 11. juni, slik at den splittjusterte kursen 120.888 blir 120.878074. Lite, permanent og brukt på nytt ved hver av de rundt seksti ex-datoene gjennom NVDA-historikken. Det er rentesrenteeffekten som teller: Når du ser på 2015, ser du på en pris som er multiplisert med dusinvis av faktorer som er litt irrasjonelle. Ingen justert historisk pris ligger på ørenettet.
Det betyr at enhver ==-sammenligning mot et rundt nivå er feil,
enhver kontroll av «traff vi limitprisen?» trenger en toleranse, og
rekonstruerte OHLC-data oppfyller ikke lenger invarianten om at low ≤ open ≤
high etter naiv avrunding til 4 desimaler. Det betyr også at
adj_close er en totalavkastningsserie, mens close er
en kursavkastningsserie. Beregner du avkastning fra den ene og verdifall fra
den andre, blander du to ulike definisjoner av hva som skjedde.
volum: 42,800,000 — divisoren ingen bruker konsekvent
Aksjevolumet må multipliseres med 10 for å kunne sammenlignes med handelsdager etter splitten. Noen dataleverandører gjør det. Noen gjør det ikke. Noen justerer OHLC, men lar volumet være ujustert, og det er verst av de tre alternativene fordi filen ser konsistent ut helt til du multipliserer pris med volum.
Det ryddigste er å slutte å bruke aksjevolum som en funksjon og heller bruke dollarvolum, som per definisjon ikke påvirkes av splitter. Et likviditetsfilter basert på gjennomsnittlig dollarvolum over 20 dager trenger ingen justeringslogikk og kan ikke ødelegges av en selskapshendelse. Funksjoner som er invariante er bedre enn korrekt justeringskode, for korrekt justeringskode er noe du hele tiden må passe på at er riktig.
Tre typer datafiler fra leverandører, tre feiltyper.
| Det som leveres | Det som går galt |
|---|---|
| Bare justert sluttkurs, ujustert OHLC | Gap- og intradaglogikk beregnes på tvers av to prisskalaer |
| Justert OHLC, ujustert volum | Dollarvolumet bommer med splittratioen; ADV-filtre gir feil utslag |
| Ujustert OHLCV pluss faktorkolonner | Ingenting hvis du bruker dem. Alt hvis du bruker dem på feil rad |
Kolonnene som mangler: justert open, justert high, justert low
Legg nå tilbake OHLC-dataene. Hvis en datastrøm bare justerer sluttkursen, og
strategien beregner et gap over natten som
open / prev_adj_close − 1, beregner den 120.37 / 120.8781 − 1 den
10. juni 2024, som er et fint tall, omtrent −0.4 %. Flytt den samme koden
tilbake til 7. juni 2024, så beregner den open fra den ujusterte filen
(omtrent 1207) mot en sluttkurs justert med 0.025 fra eldre splitter.
Resultatet blir et gap på flere tusen prosent.
Alt som blander en ujustert kolonne med en justert, er regning på tvers av to koordinatsystemer. Nå behandler jeg avlesninger med blandet justering som en kontroll på datalasternivå, i stedet for noe forskeren selv må huske på, for det kommer de ikke til å gjøre, meg selv inkludert.
Det ingen kolonne forteller deg: hvor mange aksjer du faktisk ville ha kjøpt
Dette er det som koster penger i backtestens kostnadsmodell, og grunnen til at jeg i det hele tatt skriver om en aksjesplitt.
Provisjon på amerikanske aksjer beregnes ofte per aksje. Si at den er $0.005, med en minstepris på $1.00 per ordre, en vanlig fast prisklasse hos en nettmegler. Kjør så en backtest på en NVDA-posisjon på $400,000 ett år før splitten, da den ujusterte kursen lå nær $400 og den justerte kursen vises som $40.
| Aksjer | Provisjon | |
|---|---|---|
| Det som faktisk skjedde (ujustert $400) | 1,000 | $5.00 |
| Det backtesten ser (justert $40) | 10,000 | $50.00 |
Samme eksponering, samme handel, 10 ganger så høye modellerte kostnader. Det er $45 oppspinn per handel, $90 per tur-retur, og en strategi med daglig omsetning på $400,000 påfører rundt $22,000 i året i kostnader som aldri har eksistert. Det tilsvarer 5.6 prosentpoeng i årlig avkastning på en portefølje på $400k, skapt utelukkende av en divisor. Her får den deg til å se dårligere ut enn virkeligheten, og det tåler vi.
Omvendte splitter virker motsatt vei, og det er farlig. En omvendt split på 1-for-10 betyr at justert historikk viser priser som er 10 ganger høyere og aksjetall som er 10 ganger lavere enn det du ville ha handlet. Kostnadsmodellen din deler i stillhet provisjonen per aksje på ti. Aksjeuniverset der omvendte splitter er vanlige, er universet av småselskaper under $5 med brede spreader, der gebyrer per aksje utgjør den største enkeltposten i kostnadene. Backtesten pynter på akkurat aksjene den burde straffe.
Løsningen er enkel og strukturell: Fastsett posisjonsstørrelse i dollar, konverter til aksjer med den ujusterte kursen for den datoen, og beregn gebyr per aksje ut fra det antallet. Behold kolonnen med ujustert kurs i datalasteren for alltid, ikke som et spor for revisjon, men som en aktiv inndata til fill-motoren.
- Signaler og avkastning: justerte serier.
- Aksjetall, gebyr per aksje, tick-avrunding, prisnivåfiltre: ujusterte serier.
- Likviditet og kapasitet: dollarvolum, som ikke trenger noen av delene.
Seks kolonner, én rad, én nokså vanlig selskapshendelse som ble annonsert tre uker i forveien og ikke overrasket noen. Markedet håndterte den i løpet av en formiddag. Det er betydelig vanskeligere å gjenskape den nøyaktig i en forskningspipeline, og tegnene på at en pipeline ikke gjør det, er nesten aldri en feilmelding.
← Alle innlegg


