Long pozíció 84,120-nál. Stop 84,036-nál, célár 84,271-nél. Beérkezik a következő perc gyertyája: nyitó 84,118, csúcs 84,290, mélypont 84,010, záró 84,240.
Mindkét szint ezen a gyertyán belül van. A stopot is elérte az árfolyam, a célárat is, az OHLC négy száma pedig pontosan nulla információt tartalmaz arról, melyik történt előbb. A backtested mégis adott egy eredményt. A ciklus valamelyik pontján egy sor döntött — valószínűleg olyan sor, amelynek megírásakor nem gondoltál modellezési feltevésként a választásodra.
Rövid távú stratégiáknál ez a hamis teljesítmény legnagyobb forrása, amivel találkozom: megelőzi a díjakat és a csúszást is, mert nem feltevésnek látszik. Hanem a rendszer működésének részletének.
Első rossz módszer: hagyjuk, hogy az if-lánc döntsön
A megszokott szerkezet:
if bar.high >= target:
exit(target, "tp")
elif bar.low <= stop:
exit(stop, "sl")
Senki sem választotta azt, hogy „a célár teljesüljön előbb, mint a stop”. Egyszerűen a célár ellenőrzése került előre, mert ez a kedvező forgatókönyv, és éppen erre gondoltál. Cseréld fel a két ágat, és megváltozik a tőkegörbe; már ez is elég kell legyen ahhoz, hogy lásd: a stratégia P&L-je részben a szerkesztődön múlik.
Lefuttattam egy szándékosan átlagos átlaghoz-visszatérési skalpoló stratégiát BTCUSDT perpetual piacon, három hónapnyi 1-minute gyertyán, 4,812 ügylettel, a belépőtől számított 0.10%-os stoppal és 0.18%-os célárral. Ugyanazok a jelek, ugyanazok a díjak, csak a döntetlen feloldásának szabálya változott.
Az ügyletek nyolcada határozza meg az egész eredményt. Ez a szűk stop-célár páros matematikája: a kétértelmű ügyletek azok, amelyeknél az árfolyam mindkét irányba elmozdult — ezek a legérdekesebb esetek —, és mindegyikük a stop és a célár közötti teljes távolságot jelenti attól függően, melyik kimenetelt dobja a pénzfeldobás. Az ügyletek 12.6%-a × 0.28%-os távolság = a mintában az összesített névérték-forgalom 3.5%-a, ami messze meghaladja a stratégia tényleges előnyét.
Ez az arány a gyertya időtávjától függ a stop és a célár távolságához képest, és a gyertyák időtávjának növelésével gyorsan romlik. Ugyanaz a stop és célár, ugyanazok a jelek, újramintavételezve:
| Gyertya időtávja | Ügyletek, amelyeknél egy gyertyán belül mindkét szintet elérte az árfolyam | Sharpe (előbb a célár) |
|---|---|---|
| 1s | 0.3% | 0.91 |
| 1m | 12.6% | 2.31 |
| 5m | 34% | 3.60 |
| 15m | 49% | 4.42 |
| 1h | 71% | 5.88 |
Nézd meg, mit is mond valójában ez a táblázat. A durvább gyertyák javították a backtest eredményét. Minden kutató azt gondolná, hogy az órás gyertyák a konzervatív választás: kevesebb zaj, kisebb mikrostruktúra-túligazítás. De ha a gyertyán belüli feloldási szabály neked kedvez, a durvább gyertya csak egy nagyobb doboz, amelyen belül feltételezheted, hogy szerencséd volt. Az 1h gyertyáknál tízből hét ügylet kimenetele pusztán a konvención múlik. Ez a backtest nem a stratégiát teszteli, hanem azt, hogyan rendezted sorba két `if` utasítást 3,400 alkalommal.
Második rossz módszer: feltételezzük, hogy a stopárfolyamon teljesült a stop
Tegyük fel, hogy rögzíted a sorrendet. Előbb a stop teljesül, elszámolsz pontosan 0.10% veszteséget plusz taker díjat, és úgy érzed, alapos voltál. De még mindig két külön dologban tévedsz.
Először is, a stop egy aktiváló feltétel, nem maga a teljesülés. A Binance USDⓈ-M felületén a STOP_MARKET megbízás a triggerfeltétel teljesülésekor azonnal piaci megbízássá válik, majd felhasználja, ami éppen van az ajánlati könyvben. Egy nyugodt percben ez egy-két ticknyi csúszás. Abban a percben viszont, amely ténylegesen aktiválta a stopodat — abban, amelyikben 40 pontos gyertyatest és alatta likvidálási hullám volt — éppen azon az oldalon vékony a könyv, amelyet te eltalálsz. A mintámban a stopaktiválásokat a tick-adatfolyammal párosítva a teljesülési ár mediánja 1.4 bps-szel haladta meg az aktiválási szintet, a 95. percentilis pedig 11 bps volt. Egy 10 bps-es stopnál a szélső eset a meghatározni vélt kockázat további tizedébe kerül.
A második finomabb, és kifejezetten a perpetual ügyletekre jellemző: melyik árfolyam aktiválja a stopot. A Binance alapértelmezés szerint mark price alapján aktiválja a stopmegbízásokat. A mark price az indexárból és egy simított bázisból áll össze, nem az adott tőzsde utolsó kötési árából. Az OHLC-adatsorod az utolsó árat tartalmazza. Két különböző adatsorról van szó, és éppen azokban az eseményekben térnek el leginkább, amelyek a stopokat aktiválják.
| Utolsó ár (a kline-adatod) | Mark price (alapértelmezett aktiválási ár) | |
|---|---|---|
| Forrás | az adott tőzsde kötései | több tőzsde indexe + bázis |
| Kanóc viselkedése | teljes kilengés | erősen tompított |
| Jellemző eltérés | nyugodt piacon 1–3 bps, likvidálási hullám perceiben 20–35 bps | |
| Következmény a backtestben | tévesen aktivált stopok, és aktiválás nélkül maradt stopok | |
Tehát az utolsó árfolyamot mutató adatsorban látható 25 bps-es kanóc kiejt a backtestben, miközben az élő mark price 10 bps-re sem került a aktiválási szintedtől. Vagy ennek fordítottja történik azon a napon, amikor az index elmozdul, a te tőzsdéd pedig lemarad. Ha a workingType értékét CONTRACT_PRICE-ra állítod, legalább összhangba hozod az éles működést az adataiddal; kutatóként ez általában helyes döntés, hiszen a mark price alapú aktiválás tisztességes szimulálásához egy második adatsort is végig kell vezetned a teljes teljesülési motoron.
Erre emlékszem a legjobban: valaki a csapatunkból „javított” egy stratégián azzal, hogy a take-profitot 0.18%-ról 0.21%-ra emelte. A Sharpe 2.3-ról 3.1-re ugrott. Nem lett jobb a stratégia. A célár egyszerűen kikerült az 1-minute kanócok eloszlásának sűrű részéből, ezért kevesebb ügylet került abba a kétértelmű csoportba, ahol a kód észrevétlenül nekik ítélte a győzelmet. A döntetlen feloldását optimalizálták.
Harmadik rossz módszer: mindig a legrosszabbat feltételezzük, és ezt nevezzük konzervatívnak
Az ösztönös megoldás a pesszimizmus. Ha mindkét szintet elérte az árfolyam, számold el a stopot. Kész, nincs több optimizmus, mehet élesbe.
Régebben én is ezt csináltam. Jobb, mint az alternatíva, de két okból még mindig hibás.
Olyan stratégiákat is elkaszál, amelyek jól működnek. Ennél a stratégiánál a 12.6% ügyletre alkalmazott pesszimista feloldás 2.1 Sharpe-pontba került a tick-adatokkal feloldott 0.94-hez képest. Ha a valódi érték 0.94, a konvenciód pedig 0.18-at mutat, félreteszed az ötletet, és valami rosszabbon dolgozol tovább. A két Sharpe-ponttal tévedő konzervativizmus nem konzervativizmus: erkölcsi köntösbe bújtatott zaj.
Még rosszabb, hogy eltorzítja az optimalizálást. Adj a paramétereket végigpróbáló keresőnek pesszimista döntetlenfeloldást, és megtanulja kerülni a kétértelmű helyzeteket, mert azok innentől tiszta büntetést jelentenek. A széles stopok és a közeli célárak, vagy a lassú gyertyák felé mozdul, amelyeknél ritkán esik egybe a két szint; aztán olyan paramétereket mutat, amelyeket a teljesülési konvenciódhoz, nem pedig a piachoz igazított. Ugyanaz a hiba, mint a kedvező kimenetelt feltételező változatnál, csak ellentétes előjellel, és ugyanúgy láthatatlan az eredménykimutatásban.
Ökölszabályunk: ha a stop_distance + target_distance kisebb a gyertya időtávjához tartozó tartomány 75. percentilisénél, akkor a gyertyán belüli feltevés nagyobb hatással van a P&L-re, mint a jelzésed. Számold ki mindkét értéket. Négy sor az egész, és ez az egyetlen ellenőrzés több stratégiaértékelést zárt le, mint bármi más.
Mi működik valójában
A gyertyán belüli árfolyamút is adat. Szerezd meg, vagy mérd fel azt, amihez nem férsz hozzá.
- A lehető legrészletesebb adatsor alapján oldd fel a helyzetet. A releváns percek Binance aggTrades adatai csak néhány száz sort tesznek ki, és egyértelműen megválaszolják a kérdést: melyik szintet érte el előbb az árfolyam, és milyen áron teljesült a söprés. A teljes backtesthez nincs szükséged tick-adatokra, csak a kétértelmű gyertyákhoz. A mintámban ez 129,600-ból 606 perc volt. Ez kis letöltés, nem infrastruktúra-projekt.
- Ha nem állnak rendelkezésre tick-adatok, a feloldáshoz lépj egy vagy két időtávval lejjebb. Ha a jelzések 15m-en keletkeznek, a kilépést 1s vagy 1m gyertyák alapján oldd fel. A kétértelmű esetek aránya 49%-ról a százalék töredékére esik, a fennmaradó rész pedig elég kicsi ahhoz, hogy tisztességesen figyelmen kívül hagyhasd.
- Mindig tüntesd fel a tartományt. Futtasd le kétszer az összes backtestet: optimista és pesszimista feloldással, és a feloldott érték mellett írd ki mindkét Sharpe-mutatót. A különbség mutatja a gyertyán belüli bizonytalanságot; szerepeltetni kell az eredménykimutatásban, a Sharpe-mutatóhoz megadott konfidenciaintervallum mellett. Ha a tartomány 0.2–2.3, akkor a belső értékeire alapozott következtetés nem valós.
- Kezeld a kétértelmű esetek arányát alapvető mérőszámként. Nálunk minden stratégia adatlapjának tetején szerepel, a kötésszám és a forgalom mellett. Ha az arány nagyjából 5% fölött van, akkor a belépési logika helyett a kilépési logikát teszteled.
- Az aktiválást és a teljesülést külön modellezd. Az aktiválási feltételt azon az árfolyamsoron ellenőrizd, amelyet a tőzsde ténylegesen használ; a teljesülési árat pedig a tape alapján kalibrált csúszásmintával becsüld, ne az aktiválási árral egyezőnek vedd.
A részvénypiacokon ugyanez a probléma más köntösben jelenik meg. Ha egy részvény 62.00-s stopja éjszaka 58.40-re esik, a stop nem 62.00-n teljesül, hanem a nyitóár alatt valahol; egy napi gyertyás backtest pedig, amely árfolyamrés esetén −$0.00 csúszást számol, vidáman megmutatja, hogy a stop-loss réteg javította a visszaesést. Pedig nem. Egyszerűen nem tesztelte azokon a napokon, amelyek számítanak. A kereskedési szünetek is ugyanezt okozzák: a stopod ténylegesen az újranyitási aukción teljesül, olyan áron, amelyet a gyertya mélypontja nem mutat.
Ezek egyike sem különleges eset. Csupán annak felismerése, hogy a gyertya összefoglaló, a stop-célár stratégiád pedig arra fogad, milyen sorrendben következtek be azok az események, amelyeket ez az összefoglaló elhagyott. Amikor a paper-trading motor végre élő tape-en futtatja a stratégiát, a tape-nak van véleménye erről a sorrendről, és soha egyetlen alkalommal sem érdekelte, melyik if-ágat írtad meg előbb.
← Összes bejegyzés


