Un Donchian de 20 de zile pare inofensiv într-un spec. Agentul scrie un rolling max pe 20 de bare, backtest-ul pornește de la primul rând al fișierului parquet, iar în primele două săptămâni „high-ul de 20 de zile” e doar maximul rulant al barelor care există. În ziua patru e un high de patru zile. Strategia pe care credeai că o testezi nu e cea care a produs primele douăsprezece fill-uri.
Am un notebook pe bare zilnice ETHUSDT unde Sharpe-ul pe tot eșantionul e 1.4. Tai primele 20 de sesiuni și scade la 0.6. Păstrezi acele 20 și arunci restul primului an, și printează 3.1. Nimeni n-a scurs ziua de mâine. Strategia a folosit trecutul, doar că nu destul din el, iar raportul a tratat acele tranzacții pe jumătate formate ca dovezi.
Lasă fereastra să tranzacționeze cât încă se umple
Paguba obișnuită e min_periods=1. Pandas calculează o medie rolling pe o singură bară dacă îl lași. Agenții fac asta mereu, pentru că un NaN în capul frame-ului pare un bug, iar umplerea lui face unit test-ul să treacă pe verde.
O țintă de realized-vol pe 20 de zile cu două observații de randament e o explozie de size. Varianța a două numere e o aruncare cu banul, o inversezi, iar prima poziție e de câteva ori riscul intenționat. Un z-score față de o deviație standard aproape de zero face același truc cu un semn: strategia pare să aibă o intrare chirurgicală care nu mai apare odată ce fereastra e onestă.
Breakout-urile sunt mai rele, pentru că arată ca regula reală. Un canal de 20 de bare pe bara șase e un canal de șase bare. Primești un cluster de tranzacții chiar la început, adesea pe o porțiune liniștită care n-ar fi declanșat indicatorul terminat. Piețele liniștite derivă puțin. Un canal strâns se declanșează. Curba de equity capătă un avans pe care niciun an ulterior nu-l repetă.
Zero-fill și forward-fill inventează o minciună diferită. RSI la 0 se citește ca supravândut. Un ATR lipsă de 0 aruncă în aer unit size-ul, iar backtest-ul cumpără conștiincios un depozit. Prefer ca un NaN să omoare ordinul decât ca un 0 să inventeze unul.
Încă mă prind că plotez de la rândul 0 pentru că o lună goală în stânga graficului pare neterminată. Luna goală e partea onestă.
Taie curba de equity, păstrează poziția
Asta trece de code review. Calculezi feature-urile de 20 de zile corect, tai primele 20 de rânduri din curba plotată și publici de la 21 ianuarie. Poziția din 21 ianuarie a fost deschisă pe 14 ianuarie. High-water-ul trailing stop-ului, numărul de piramidare, cash-ul după acele burn-in-uri: totul a intrat în eșantionul „curat”.
Paguba e o strategie paper care nu poate fi pornită la data din articol. Restartezi de la flat la startul raportat și ordinele din prima săptămână nu coincid. Uneori backtest-ul e deja într-o tranzacție pe care sistemul paper n-are niciun motiv să o deschidă. Uneori sistemul paper intră pentru că lookback-ul e rece, iar backtest-ul stă deoparte pentru că luna lui ascunsă a luat deja breakout-ul. Două cărți de deschidere, o singură dată de start publicată.
Fold-urile walk-forward fac asta în tăcere. Fiecare fereastră de test are o coadă de lookback pe care n-o plotezi. Sari peste simularea acelei cozi și primele tranzacții de test sunt din nou tranzacții pe fereastră neterminată. O simulezi și raportezi doar zilele de test, iar poziția zilei de test e condiționată de fill-uri pe care nu le arăți. A doua variantă poate fi o alegere legitimă de cercetare. Să numești prima dimineață a fold-ului un cold start, în timp ce un trailing stop e deja live din coadă, e cum doi cercetători scot două Sharpe-uri dintr-un singur config.
Fă warmup-ul în zile calendaristice când feature-ul nu e o zi
„Dă-i 30 de zile” lasă feature-ul insuficient specificat. Treizeci de zile calendaristice pe un Donchian de 5 minute sunt mii de bare peste un lookback de 20 de perioade. Treizeci de zile pe un feature zilnic care are nevoie de 20 de sesiuni e ori gras, ori subțire, în funcție de weekend-uri și găuri.
Un z-score pe funding-rate de pe unul dintre venue-urile noastre de perp a primit un warmup de 30 de zile pentru că spec-ul spunea 30 de perioade și cineva a tradus perioadele în calendar. Funding-ul acolo se printează la fiecare opt ore. Treizeci de zile sunt circa 90 de decontări. Treizeci de perioade sunt zece zile. Cele două luni extra de burn-in s-au nimerit să includă o săptămână de depeg. Tăierea lor a făcut fereastra live să pară mai calmă decât istoria reală a strategiei. Lumea a dat vina pe sensibilitatea la data de start. Warmup-ul mâncase regimul.
Iar crypto versus acțiuni face același număr să-și schimbe sensul fără ca cineva să editeze un parametru. Douăzeci de „zile” de ETH includ sâmbetele. Douăzeci de sesiuni pe acțiuni sunt aproape o lună calendaristică. Copiezi un lookback de 20 de zile dintr-un notebook de acțiuni pe lumânări orare de perpetual și ai un canal de 20 de ore cu un nume de 20 de zile. Prima bară validă e atunci ori la 20 de ore, ori la 20 de zile dacă o funcție ulterioară face resample, și am văzut ambele în același diff de agent.
Fixează primul timestamp tranzacționabil
Warmup-ul e o cerință de date, nu un eșantion bonus. Fiecare feature specifică numărul de observații terminate de care are nevoie, în unitățile în care e calculat. Primul timestamp tranzacționabil e prima bară unde toate acestea sunt adevărate și un cont flat ar fi avut voie să trimită un ordin.
| Feature | Ce spune de obicei spec-ul | Ce trebuie să existe de fapt | Substitut tăcut |
|---|---|---|---|
| Donchian de 20 de zile | 20 de high-uri zilnice | 20 de sesiuni complete | Running max cu min_periods=1 |
| realized vol de 30 de zile | fereastră de 30 de zile | 30 de randamente close-to-close | Varianță pe două zile, inversată în size |
| z-score de funding pe 20 de printuri | 20 de zile | 20 de decontări de funding | 20 de zile calendaristice (~90 de printuri), sau 20 de bare din timeframe-ul greșit |
| RSI de 14 perioade pe 5m | 14 perioade | 14 bare de cinci minute închise | 14 zile, sau 14 bare cu goluri forward-filled |
P&L-ul de dinaintea acelui timestamp rămâne în afara track record-ului. Starea de dinaintea acelui timestamp poate totuși necesita simulare, astfel încât un trailing stop sau un cooldown să fie reale când ceasul ajunge la prima bară tranzacționabilă, dar acele fill-uri nu intră în Sharpe, în numărul de tranzacții sau în matematica de capacity. Apoi rulezi verificarea urâtă: restart de la flat la acel timestamp, cu lookback-ul disponibil doar ca date. Dacă ordinele se schimbă, livrai tranzacții ascunse.
Paper trading-ul pornește pe același contract. La fel și fiecare fereastră de test walk-forward. Dacă graficul pare că-i lipsește o lună, las-o lipsă.
← Toate articolele


