Sama commit, samat parquet-tiedostot, sama kone, kaksi ajoa tunnin välein. Sharpe 1.34 ja Sharpe 1.19. Kokonaistuotto 41.2% ja 37.8%. Kauppojen määrä 1,418 ja 1,421. Ja osakekäyrät täsmäsivät jokaiseen tulostettuun desimaaliin 11,205 peräkkäisen palkin ajan ennen kuin ne erkanivat.
Kolme ensimmäistä lukua ovat harmillisia. Viimeinen on kiinnostava, koska se kertoo, ettei ongelma ole koko ajon läpi leviävä huolimaton liukulukulaskenta. Tietyllä palkilla tapahtui jotain diskreettiä, ja kaikki sen jälkeen kertautui. Tässä kirjoituksessa etsimme tuon palkin ja tutkimme, mitä 0.15:n suuruinen ero merkitsee jokaiselle koskaan ajamallesi parametripyyhkäisylle.
Strategia: poikkileikkauksellinen momentti 30 suurimman volyymin USDⓈ-M-perpetuaalifutuurissa, uudelleenpainotus 4 tunnin välein, pitkä positio viidessä 12 tunnin tuoton parhaiten tuottaneessa ja lyhyt viidessä heikoimmassa, 18 kuukauden historia, kulut ja rahoitusmaksut veloitetaan positiota kohden.
Palkin löytäminen, jossa ajot erkanevat
Jos kirjaat osakearvon jokaiselta palkilta täydellä tarkkuudella, tähän menee noin neljä minuuttia. Tallenna molemmat ajot CSV-tiedostoiksi muodossa (bar_index, equity, open_positions_hash), lataa ne ja etsi ensimmäinen indeksi, jossa ne eroavat. Olimme jo kirjoittaneet tuon skriptin toista bugia varten, ja vain siksi en käyttänyt tähän koko aamua.
Palkki 11,206, 2025-03-14T08:00 UTC. Osakearvot olivat edellisellä palkilla identtiset 13 merkitsevän numeron tarkkuudella. Palkilla 11,206 positioiden tiivisteet eroavat: ajossa A ollaan pitkässä SOL-positiossa, ajossa B pitkässä AVAX-positiossa. Sama suunta, sama nimellisarvo, eri symboli. Kaikki sen jälkeen johtuu tästä.
Tulostin siis molempien ajojen ranking-syötteet kyseiseltä palkilta. Ne olivat identtiset. Tavu tavulta samat, samat 30 symbolia, samat 30 pistemäärää. Kahden pistemäärä oli 0.0. Täsmälleen nolla molemmilla, koska molempien instrumenttien 4 tunnin palkilla ei ollut kauppoja tarkastelujaksolla. Siksi päätöshinnan suhde edelliseen päätöshintaan oli yksi ja logaritmi nolla. Kyse ei ollut pyöristyksestä nollaan. Arvo oli nolla.
Kaksi symbolia oli tasoissa sijalla viisi. Valintaan otettiin viisi parasta. Kumpi tasatuloksen symboleista pääsi mukaan, riippui siitä, missä järjestyksessä lajittelu jätti ne, eikä lajittelu toiminut niin kuin oletin.
Tasatulos, epävakaa lajittelu ja asia, jonka olin sivuuttanut kahden vuoden ajan
Ranking tehtiin ryhmitellyn aggregoinnin kautta, ja sille syötetty DataFrame rakennettiin joukolle tehdyn dict comprehensionin avulla. Symbolijoukko muodostettiin uudelleen joka palkilla asynkronisen haun perusteella. Joukkojen iteraatiojärjestys vaihtelee hash seedin mukaan, ja Python satunnaistaa merkkijonojen hash seedin prosessikohtaisesti, ellei arvoa PYTHONHASHSEED aseteta kiinteäksi. Siksi rivien järjestys ennen lajittelua vaihteli ajojen välillä, ja tasatuloksen sisältävän avaimen epävakaa lajittelu ratkaisi tasatilanteen eri tavoin.
Tällaiset tasatulokset eivät ole harvinaisia poikkeuksia. Ne ovat rakenteellisia. Aina kun ominaisuus saturoituu tai leikataan, syntyy täsmällisiä yhtäsuuruuksia: nollavolyymin palkit tuottavat täsmälleen nollatuottoja, leikattu z-score lukittuu arvoon ±3.0, vähän eri arvoja sisältävä rank-muunnos tuottaa kymmeniä tasatuloksia, ja binäärinen suodatin antaa kaikille läpäisseille pistemääräksi 1.0. Tällä ajolla oli 18 kuukauden 4 tunnin palkeissa 47 palkkia, joilla valintarajalla oli tasatulos. Kolmessa niistä valittu kori vaihtui. Muissa tasatuloksessa olivat kaksi instrumenttia, jotka molemmat olivat joko jo mukana tai molemmat ulkona.
Olin noin päivän ajan vakuuttunut, että datan lataus oli epädeterministinen, koska se olisi ollut jännittävä vastaus. Niin ei ollut. Ei koskaan ole. Kyse on joukosta, tasatuloksesta ja oletuksesta lajittelun vakaudesta, jota kukaan ei kirjannut ylös.
Miksi kolmella kaupalla on 0.15:n Sharpe-vaikutus
Tätä kohtaa ihmiset usein kyseenalaistavat. Vastaus on, että osakepääoman osuuteen perustuvalla positioiden koolla backtest on polkuriippuvainen järjestelmä. Kun kunkin position koko on 8% nykyisestä osakepääomasta, ero osakepääomassa palkilla n tarkoittaa eroa kaikissa nimellisarvoissa palkista n eteenpäin.
Ensimmäinen erkaantuminen maksoi itsessään hyvin vähän. Ajossa B AVAX-positio menetti 2.1% yhdeksässä tunnissa; ajossa A SOL-positio tuotti 0.4%. Ero osakepääomassa tuon kaupan jälkeen: 0.21%. Mitätön. Mutta siitä eteenpäin ajot eivät enää ole sama strategia. Niissä on hieman erikokoinen positio, joten rahoitusmaksut kertyvät hieman eri tavoin, ja kaksi myöhempää valintarajan tasatulosta ratkesi jälleen eri tavoin, koska niitä syöttäneet pistemäärät perustuivat nyt hieman erilaisiin avoimiin positioihin. Toinen niistä osui päivälle 2025-03-27, päivää ennen kuuden päivän trendiä, joka tuotti noin kolmanneksen ajon kokonaistuotosta. Ajo A oli mukana koko liikkeessä; ajo B tuli mukaan yhden uudelleenpainotuksen verran myöhemmin.
Tuottoero: 3.4 prosenttiyksikköä. Sharpe-ero on tuottoeroa suurempi, koska ajon B uudelleenjärjestetyt kaupat osuivat volatiilimpaan jaksoon, jolloin nimittäjä kasvoi samalla kun osoittaja pieneni. Pieni syy, kaksi voimistavaa tekijää.
Jos positiosi nimellisarvo on kiinteä eivätkä avaukset riipu nykyisistä positioista, suojaudut tältä paljon paremmin. Useimmat kiinnostavat strategiat eivät kuitenkaan ole tällaisia.
Viisi kohtaa, joissa ongelma käytännössä ilmenee
| Lähde | Oire | Korjaus |
|---|---|---|
Kiinnittämätön PYTHONHASHSEED, kun joukon tai sanakirjan iteraatiojärjestys vaikuttaa lajitteluun | Tasatuloksen ratkaisu vaihtuu ajojen välillä; ensimmäinen ero näkyy tietyllä palkilla | Aseta seed kiinteäksi; lajittele eksplisiittisen toissijaisen avaimen (symbolin) mukaan, jotta tasatulokset ratkeavat deterministisesti |
Epävakaa lajittelu tasatuloksissa (quicksort oletusarvo NumPyssa/pandaksessa) | Sama kuin yllä, ongelma säilyy seedin kiinnittämisestä huolimatta | kind="stable" tai tee lajitteluavaimesta yksikäsitteinen |
| Ilman seediä käytetty RNG bootstrapissa, opetus- ja testijoukon sekoituksissa tai synteettisten täyttöjen vaihtelussa | Koko ajon aikainen poikkeama, ei selkeää erkanemiskohtaa | Yksi eksplisiittinen seed kullekin komponentille, kirjattuna ajon manifestiin |
| Rinnakkainen liukulukujen vähennyslaskenta (summauksen järjestys riippuu säikeiden määrästä) | Erot viimeisissä biteissä, yleensä harmittomia, kunnes ne ylittävät vertailurajan | Aseta säikeiden määrä kiinteäksi tutkimusajoissa; älä koskaan vertaa liukulukuja käyttämällä == päätöksenteon rajalla |
| Kiinnittämättömät kirjastoversiot | Toistettavissa tänään, ei enää marraskuussa | Kirjaa manifestiin lukitustiedoston tiiviste datan tilannevedoksen tiivisteen rinnalle |
Neljännellä rivillä kuvattu ongelma ei ole niin yleinen kuin moni pelkää, ja ensimmäinen rivi aiheuttaa jatkuvasti harmia.
Bittitarkka toistettavuus on työkalu, ei hyve
Tarvitset determinismiä, jotta tiedät yhtä riviä muuttaessasi, että osakekäyrän ero johtuu juuri tuosta rivistä. Siinä koko syy. Jokainen Stratmillin agenttiajo kirjoittaa nyt manifestin, joka sisältää datan tilannevedoksen tiivisteen, lukitustiedoston tiivisteen ja kaikki seed-arvot. Uudelleenajo, joka ei tuota aiempaa käyrää bittitarkasti, on epäonnistunut build, ei kiinnostava kuriositeetti.
Mutta kun pystyt toistamaan ajon, riko determinismiä tarkoituksella. Aja backtest 64 kertaa 64 eri seedillä ja tarkastele tulosten hajontaa:
Vaihteluväli. Sama strategia, sama data, 64 seedattua permutaatiota tasatulosten ratkaisuun ja täyttöjen järjestykseen. Sharpen p5 1.12, mediaani 1.27, p95 1.41. Vaihteluvälin leveys 0.29.
Palaa nyt parametripyyhkäisyn tuloksiin. Paras kokoonpano sai 1.46. Sijalla 40 kaikkiaan 96 kokoonpanosta ollut sai 1.31. Niiden ero on 0.15 eli puolet vaihteluvälin leveydestä. Pyyhkäisy ei asettanut näitä kahta kokoonpanoa paremmuusjärjestykseen. Se poimi kummankin jakaumasta yhden satunnaisotoksen ja järjesti otokset.
Tämä muutti tapaamme valita. Pyyhkäisyn tulos on paremmuusjärjestys vain, jos kokoonpanojen väliset erot ylittävät yksittäisen kokoonpanon vaihtelun. Polkuriippuvaisessa strategiassa, jossa on 1,400 kauppaa, vaihtelu on yleensä niin suurta, että tulostaulukon ylin kolmannes litistyy tasatulokseksi. Silloin kannattaa valita jonkin sellaisen perusteella, mitä vaihteluväli ei peitä: vähäisempi kaupankäynti, vähemmän parametreja, kustannusoletus, jonka pystyt perustelemaan kriitikolle, tai parempi toiminta siinä walk-forward-jaksossa, josta pidät vähiten. Ne ovat todellisia tasatilanteen ratkaisijoita. Sharpen 0.15:n etu ei ole.
Ennen kuin luotat tuloksiin, tee vielä yksi asia. Aja backtestisi nyt kahdesti, vertaa palkkikohtaisia osakearvoja ja selvitä, kuulutko bittitarkasti toistettavien vai 0.15:n joukkoon. Kokeilu vie 15 minuuttia ja kertoo, kuinka suuri osa tutkimushistoriastasi mittasi strategiaa ja kuinka suuri osa hash seediä.
← Kaikki kirjoitukset
