11. september 2026 · reprodutseeritavus

Sama kood, samad andmed, kaks erinevat Sharpe’i: mittedeterminismi audit, mida sinu tagasittest vajab

Sama kood, samad andmed, kaks erinevat Sharpe’i: mittedeterminismi audit, mida sinu tagasittest vajab

Sama commit, samad parquet-failid, sama masin, kaks käivitust tunniajase vahega. Sharpe 1.34 ja Sharpe 1.19. Kogutootlus 41.2% ja 37.8%. Tehinguid 1,418 ja 1,421. Kahe aktsiakapitali kõvera iga kuvatud kümnendkoht klappis 11,205 baari järjest, enne kui kõverad lahknesid.

0.15Sharpe’i erinevus, sisendid identsed
3 / 1,418erinevat tehingut
11,205identset baari enne lahknemist

Esimesed kolm arvu ajavad närvi. Viimane on huvitav, sest näitab, et probleem ei seisne kogu jooksu peale laiali valgunud lohakates ujukomaarvutustes. Ühel kindlal baaril juhtus midagi diskreetset ja kõik ülejäänu kuhjus selle järel. See postitus räägib selle baari leidmisest ning sellest, mida see 0.15 tähendab igale parameetriotsingule, mida oled teinud.

Strateegia: ristlõikeline momentum 30 suurima mahuga USDⓈ-M perp’ide seas, tasakaalustamine iga 4 tunni järel, pikk positsioon viies suurima 12 tunni tootlusega instrumendis ja lühike positsioon viies väikseima tootlusega instrumendis, 18 kuu ajalugu, tasud ja rahastus arvestatakse iga positsiooni kohta eraldi.

Lahknevuse algusbaari leidmine

Kui logid iga baari aktsiakapitali täistäpsusega, võtab see umbes neli minutit. Salvesta mõlema käivituse tulemused CSV-faili kujul (bar_index, equity, open_positions_hash), laadi mõlemad sisse ja leia esimene indeks, kus need erinevad. Olime selle skripti juba teise vea jaoks kirjutanud; ainult seetõttu ei kulutanud ma selle peale tervet hommikut.

Baar 11,206, 2025-03-14T08:00 UTC. Eelmise baari aktsiakapital oli identne 13 tüvenumbrini. Baaris 11,206 on positsioonide räsid erinevad: käivitus A on SOL-is pikas positsioonis, käivitus B AVAX-is. Suund sama, nimiväärtus sama, sümbol erinev. Kõik pärast seda tuleneb sellest.

Seega printisin mõlema käivituse kohta selle baari järjestuse sisendid. Need olid identsed. Baidi pealt samad: samad 30 sümbolit, samad 30 skoori. Kahe skoori väärtus oli 0.0. Täpselt null mõlemal, sest mõlema instrumendi puhul oli tagasivaateaknas 4 tunni baar, kus tehinguid ei toimunud, mistõttu sulgemishinna suhe eelmise sulgemishinnaga oli üks ja logaritm null. Mitte ümardatult null. Null.

Kaks sümbolit jäid viiendale kohale viiki. Valikusse pääsesid viis esimest. See, kumb kahest sisse sai, sõltus nende sortimisel alles jäänud järjekorrast, ja sort ei toiminud nii, nagu ma eeldasin.

Viik, ebastabiilne sortimine ja asi, mida olin kaks aastat eiranud

Järjestus arvutati rühmitatud koondamise kaudu ning sellele antav andmeraam tuli sõnastikukoostisest, mis käis üle sümbolite hulga. See hulk ehitati igal baaril uuesti asünkroonsest päringust. Hulga läbimise järjekord muutub räsiseemne järgi ning Python juhuslikustab stringide räsiseemne igas protsessis, kui sa ei määra PYTHONHASHSEED. Seega erines ridade järjekord enne sortimist käivituste vahel ning viigi lahendas erinevalt ebastabiilne sortimine, kus sortimisvõtme väärtused olid võrdsed.

Sellised viigid pole mingi veider erand. Need on süsteemi sisse ehitatud. Iga kord, kui tunnus saavutab ülempiiri või kärbitakse, tekib täpne võrdsus: nullmahuga baarid annavad täpselt nulltootluse, kärbitud z-skoor jääb väärtusele ±3.0, väheste eristuvate väärtustega järjestusteisendus tekitab kümneid viike, tõeväärtusfilter annab kõigile läbinutele skoori 1.0. Sellel käivitusel tekkis 18 kuu jooksul 4 tunni baaridega valikupiiril viik 47 baaril. Kolm neist muutsid valitud korvi. Ülejäänud viigid olid kahe sümboli vahel, mis olid mõlemad juba valitud või mõlemad valikust välja jäänud.

Umbes päeva olin veendunud, et andmete laadija käitub mittedeterministlikult, sest see oleks põnev vastus. Ei käitunud. Kunagi ei käitu. Süüdi on hulk, viik ja eeldus sortimise stabiilsuse kohta, mida keegi polnud kirja pannud.

Miks kolm tehingut annavad Sharpe’i erinevuseks 0.15

Just sellele kiputakse vastu vaidlema. Vastus on, et omakapitali osakaaluga positsioonisuurusi kasutav tagasitest on teekonnast sõltuv süsteem. Kui iga positsiooni suurus on 8% jooksvast omakapitalist, tähendab omakapitali erinevus baari n ajal erinevust igas nimiväärtuses alates baarist n edasi.

Esimene lahknevus maksis iseenesest väga vähe. Käivituse B AVAX-i positsioon kaotas üheksa tunniga 2.1%; käivituse A SOL-i positsioon teenis 0.4%. Aktsiakapitali erinevus pärast seda tehingut: 0.21%. Tühine. Kuid sealt edasi ei olnud need kaks käivitust enam sama strateegia. Positsioonide suurused erinesid pisut, mistõttu erinesid pisut ka kogunevad rahastuskulud. Samuti lahenes hiljem kaks piiriviiki taas erinevalt, sest nende aluseks olevad skoorid arvutati pisut erinevatest hoitavatest positsioonidest. Üks neist juhtus 2025-03-27, päev enne kuuepäevast trendi, mis andis ligikaudu kolmandiku jooksu kogukasumist. Käivitus A püsis kogu liikumise vältel positsioonis; käivitus B sisenes ühe tasakaalustamise võrra hiljem.

Tootluse erinevus: 3.4 protsendipunkti. Sharpe’i erinevus on tootluse erinevusest järelduvale suurem, sest käivituse B ümberjärjestatud tehingud langesid kokku volatiilsema perioodiga: nimetaja kasvas, samal ajal kui lugeja kahanes. Väike algpõhjus, kaks võimendavat tegurit.

Kui sinu positsioonisuurused on fikseeritud nimiväärtusega ja sisenemised ei sõltu hetkel hoitavatest positsioonidest, oled selliste mõjude eest palju paremini kaitstud. Enamik huvitavaid strateegiaid pole kumbagi.

Viis kohta, kus see tegelikult juhtub

AllikasTunnusLahendus
Määramata PYTHONHASHSEED, kus hulga või sõnastiku läbimise järjekord mõjutab sortimistViigi lahendus muutub käivituste vahel; lahknemine algab konkreetsel baarilMäära seeme; kasuta viikide deterministlikuks lahendamiseks selgesõnalist teisest võtit (sümbolit)
Ebastabiilne sortimine võrdsete sortimisvõtmete korral (quicksort NumPy/pandas’e vaikekäitumine)Sama mis eespool, esineb ka siis, kui seeme on määratudkind="stable" või muuda sortimisvõti täielikuks järjestuseks
Seemendamata RNG bootstrap’is, treening-/testandmete segamisel või sünteetiliste täitmiste juhuslikus kõikumisesKogu jooks triivib, selget lahknemiskohta poleKasuta iga komponendi jaoks üht selgesõnalist seemet ja salvesta see käivituse manifesti
Paralleelne ujukomaarvude summeerimine (summeerimisjärjekord sõltub lõimede arvust)Erinevused viimastes bittides; tavaliselt ohutud, kuni jõuavad lävendi võrdluseniMäära uurimistööde käivitustel lõimede arv; ära kunagi võrdle otsustuspiiril ujukomaarve operaatoriga ==
Määramata teekide versioonidTäna reprodutseeritav, novembris enam mitteLisa andmehetktõmmise räsi kõrvale manifesti lukustusfaili räsi

Neljas rida pole nii sageli oluline, kui kardetakse; esimene rida tekitab aga alatasa probleeme.

Bititäpne reprodutseeritavus on töövahend, mitte voorus

Determinismi on vaja selleks, et ühe rea muutmisel saaks aktsiakapitali kõvera erinevuse omistada just sellele reale. See ongi kogu põhjus. Nüüd kirjutavad kõik Stratmilli agendikäivitused manifesti, mis sisaldab andmehetktõmmise räsi, lukustusfaili räsi ja kõiki seemneid. Kui korduskäivitus ei taasta varasemat kõverat bititäpselt, loetakse järk ebaõnnestunuks, mitte lihtsalt huvitavaks.

Aga kui oled suutnud tulemuse reprodutseerida, muuda see siis meelega ebastabiilseks. Käivita strateegia 64 korda 64 seemnega ja vaata tulemuste hajuvust:

Kõikumisvahemik. Sama strateegia, samad andmed, 64 seemendatud viigilahenduse ja täitmisjärjestuse permutatsiooni. Sharpe’i p5 1.12, mediaan 1.27, p95 1.41. Vahemiku laius 0.29.

Nüüd vaata uuesti parameetriotsingut. Parima konfiguratsiooni skoor oli 1.46. 96 konfiguratsiooni seas 40. kohal oleva skoor oli 1.31. Nende vahe on 0.15 ehk pool kõikumisvahemikust. Otsing ei reastanud neid kahte konfiguratsiooni. See võttis kummagi jaotusest ühe juhusliku väärtuse ja pani need saadud väärtuste põhjal järjekorda.

See muutis meie valikupõhimõtteid. Otsingu tulemus annab konfiguratsioonide pingerea ainult siis, kui nende skooride vahed ületavad ühe konfiguratsiooni enda kõikumise. Teekonnast sõltuva strateegia puhul, millel on 1,400 tehingut, on kõikumine tavaliselt piisavalt suur, et muuta edetabeli ülemine kolmandik üheks suureks viigiks. Siis tasub valida millegi põhjal, mida kõikumisvahemik varjata ei saa: väiksem käibekiirus, vähem parameetreid, kulude eeldus, mida julgeksid skeptikule põhjendada, või parem käitumine sulle kõige vähem meeldivas edasi liikuvate akendega valideerimise jaotuses. Need on päris viigimurdjad. Sharpe’i eelis 0.15 seda pole.

Enne kui midagi sellest usaldad, tasub teha veel üks asi. Käivita oma tagasitest kohe kaks korda, võrdle iga baari aktsiakapitali ja selgita välja, kas sinu tulemus on bititäpse või 0.15 rühmas. See on 15-minutiline katse, mis näitab, kui suur osa sinu senisest uurimistööst mõõtis strateegiat ja kui suur osa räsiseemet.

determinismtagasitestimineandmetehnikaületreeniminepython
← Kõik postitused