Oto jedna świeca. Kontrakt wieczysty Binance USDⓈ-M, BTCUSDT, minuta zaczynająca się o 13:46:00 UTC. Trafiła do nas z endpointu klines jako goła tablica dwunastu wartości i jest to najczęstsza jednostka danych wejściowych w badaniach ilościowych na poziomie detalicznym i zbliżonym do niego. Niemal każda strategia, którą generujemy, dotyka czegoś o takim właśnie kształcie.
Rozłóżmy ją więc na części, indeks po indeksie, i sprawdźmy, ile z tego backtest interpretuje błędnie.
| Indeks | Wartość | Nazwa |
|---|---|---|
| 0 | 1773495960000 | czas otwarcia (ms) |
| 1 | "84120.50" | otwarcie |
| 2 | "84177.90" | maksimum |
| 3 | "84098.10" | minimum |
| 4 | "84163.40" | zamknięcie |
| 5 | "38.417" | wolumen bazowy (BTC) |
| 6 | 1773496019999 | czas zamknięcia (ms) |
| 7 | "3232108.94" | wolumen w walucie kwotowanej (USDT) |
| 8 | 1204 | liczba transakcji |
| 9 | "21.883" | wolumen bazowy kupna takera |
| 10 | "1841203.55" | wolumen w walucie kwotowanej kupna takera |
| 11 | "0" | ignorowane |
[0] i [6]: która to minuta i czyj zegar to mówi
Czas otwarcia to 1773495960000, czas zamknięcia to 1773496019999. Zwróć uwagę na tę drugą wartość: kończy się na 19999, czyli o jedną milisekundę mniej niż otwarcie kolejnej świecy. Przedział jest domknięty z jednej strony, a otwarty z drugiej, i giełda mówi ci o tym wprost. Połowa błędów w danych, które ścigałem, zaczynała się od tego, że ktoś traktował oba końce przedziału jako domknięte i liczył podwójnie transakcję na granicy, albo tak dopasowywał resampling, że każda 5-minutowa świeca „pożyczała” milisekundę od sąsiadki.
Znaczniki czasu pochodzą z zegara silnika dopasowującego giełdy. Nie z twojego zegara, nie z zegara pobierania danych twojego dostawcy i nie z tego samego zegara, którego używa migawka funding rate. Gdy łączysz serię świec z serią stóp finansowania albo open interest pobraną z innego endpointu, łączysz źródła, które w większości przypadków zgadzają się co do mniej więcej sekundy. Dla strategii 1-minutowej to błąd czasowy rzędu 1,7% na każdym połączonym wierszu. Dla czegokolwiek poniżej minuty to błąd zabójczy.
A ta świeca jest znakowana czasem swojego otwarcia. Zawarta w niej informacja nie jest znana aż do 13:46:59.999. Każde pytanie o konwencję indeksowania — czy przesunąć o jeden, czy jako czas zdarzenia użyć znacznika zamknięcia czy otwarcia świecy — to w gruncie rzeczy to samo pytanie o lookahead w przebraniu.
[1] „84120.50”: cena otwarcia, na której nie zawrzesz transakcji
Cena otwarcia to cena pierwszej transakcji, jaka wydrukowała się w danym przedziale. To zawarta transakcja między dwiema innymi stronami. Zanim świeca pojawi się jako wiersz w twoim dataframe, ta cena ma już sześćdziesiąt sekund.
Poprzednia świeca zamknęła się na 84109.80, więc w luce między dwiema sąsiednimi świecami 1-minutowymi kryje się ruch ceny o $10.70. To 1,3 punktu bazowego, niewidoczne na wykresie i mniej więcej ćwiartka opłaty takera. W porządku. Ale spójrz na tę samą serię dla alta na kontrakcie wieczystym w trakcie publikacji amerykańskiego CPI, a te luki między świecami sięgają 15–40 pb. Backtest, który generuje sygnał na zamknięciu świecy, a realizuje zlecenie na otwarciu kolejnej, po cichu zakłada, że luka wynosi zero — a wynosi zero właśnie wtedy, gdy nie ma to znaczenia.
[2] i [3]: „84177.90” maksimum, „84098.10” minimum
To właśnie tu umiera większość silników realizacji zleceń, więc ta sekcja będzie długa.
Maksimum i minimum to skrajne dotknięte ceny. Nie niosą ze sobą żadnej informacji o wielkości ani czasie trwania. Z surowego zapisu transakcji dla tej samej minuty wynika, że wszystko po cenie 84100.00 lub niższej to 0.62 BTC rozłożone na dziewięć transakcji w oknie 1.4-sekundowym. Stop w backteście ustawiony na 84100 więc się „realizuje” — a jeśli twoja pozycja to 3 BTC, zjadłeś cały widoczny order book po drodze w dół, a reszta twojej wielkości została zrealizowana gdzieś w odbiciu 84105–84130. Świeca mówi, że minimum wyniosło 84098.10. Świeca nie mówi, że tylko $52,000 nominału zostało tam faktycznie zawartych.
Kierunek przeciwny jest gorszy, bo ci schlebia. Załóżmy, że masz oczekujące zlecenie limit sprzedaży na 84175. Maksimum świecy to 84177.90, więc naiwny silnik realizuje twoje zlecenie po 84175 i księguje rabat makera zamiast kosztu takera. Czy to zlecenie faktycznie zostało zrealizowane, zależy od pozycji w kolejce na tym poziomie ceny, czego świeca nie może wiedzieć, a ty najpewniej nigdy nie zarejestrowałeś. Dotknięte to nie to samo, co zrealizowane.
Zasada, na której się zatrzymaliśmy w naszym silniku realizacji zleceń: zlecenie oczekujące realizuje się tylko wtedy, gdy świeca przechodzi przez dany poziom, a nie jedynie go dotyka. Realizacje na dokładnie skrajnym ticku wymagają dowodu z zapisu transakcji na temat wolumenu przy danej cenie, w przeciwnym razie są odrzucane. To usunęło około 6% transakcji z typowego portfela mean-reversion i obniżyło backtestowany Sharpe jednego kandydata z 1,9 do 1,1. Ten kandydat nigdy nie był prawdziwy — zasada realizacji zleceń była po prostu pierwszą rzeczą na tyle uczciwą, by to powiedzieć.
Pokrewna pułapka: ścieżka wewnątrz świecy (intrabar). Jeśli zakres świecy obejmuje zarówno twój stop, jak i take-profit, OHLCV nie powie ci, co nastąpiło pierwsze. Każdy silnik musi przyjąć jakąś konwencję. Nasz zawsze zakłada, że najpierw jest stop — jest to pesymistyczne i czasem błędne, ale nigdy nie generuje fałszywego zysku. Jeśli twój silnik zakłada, że najpierw jest take-profit, świece o szerokim zakresie będą wytwarzać zysk z samej niejednoznaczności, a to właśnie świece o szerokim zakresie dominują w rozkładzie twojego PnL.
[4] „84163.40”: najmniej odporna liczba w świecy
Cena zamknięcia to ostatnia transakcja w danym przedziale. Tyle. Może to być odd-lot 0.002 BTC od bota zaokrąglającego pozycję o 13:46:59.8. Ten pojedynczy, strukturalnie przypadkowy tick to właśnie to, czego większość pipeline'ów badawczych używa do obliczania każdego sygnału, wyceny każdej pozycji i oceny każdego wyjścia.
Na kontraktach wieczystych BTC to niemal nie ma znaczenia; na cienkim kontrakcie na alta o 04:00 UTC ma znaczenie ogromne, a różnica między cenami zamknięcia dwóch giełd dla tej samej minuty może przekroczyć całą twoją przewagę na transakcję. Gdy PnL strategii zależy konkretnie od ceny zamknięcia, uruchamiamy ją ponownie, wyceniając po cenie mark z giełdy, która pochodzi z indeksu i jest znacznie trudniejsza do „szturchnięcia”. Jeśli wyniki się rozjeżdżają, strategia handlowała artefaktem.
[5] i [7]: „38.417” oraz „3232108.94” — wolumen w jakich jednostkach
Wolumen bazowy to BTC; wolumen w walucie kwotowanej to USDT. Tutaj mamy oba, co jest uprzejmością, na jaką nie każda giełda się zdobywa. Powód, dla którego warto o to dbać, to agregacja między giełdami. Kontrakty rozliczane w kryptowalucie są kwotowane w kontraktach o nominale $100. Niektóre feedy giełdowe raportują pełne lotny (round lots). Giełdy rynków predykcyjnych raportują liczbę udziałów, gdzie udział to binarne roszczenie denominowane w dolarach. Sumowanie „wolumenu” w mieszanym uniwersum bez normalizacji do jednej jednostki nominału daje ranking płynności, który jest czystym nonsensem — i będzie to nonsens w sposób pozornie stabilny, który przetrwa przegląd.
Normalizuj wszystko do nominału w walucie kwotowanej już przy pobieraniu danych. Zachowuj też surowe pole, ale nigdy nie pozwól, by strategia je zobaczyła.
[8] 1204: pole, które powinno kształtować twój model wpływu na rynek
Podzielenie wolumenu bazowego przez liczbę transakcji daje średnią transakcję na poziomie 0.032 BTC, czyli około $2,700. Jeśli twoja kandydacka strategia chce wejść od razu za $250,000, oznacza to żądanie transakcji około 92 razy większej niż typowa transakcja w tej minucie. To właśnie ta liczba, a nie jakaś ogólna stała poślizgu 5 pb, powinna napędzać twój człon wpływu proporcjonalny do pierwiastka kwadratowego. Obliczamy współczynnik uczestnictwa dla każdej świecy jako pełnoprawną kolumnę i odrzucamy strategie, w których mediana wielkości wejścia przekracza kilka procent nominału świecy, ponieważ wszystko, co dzieje się dalej, jest fikcją.
Liczba transakcji tanim kosztem wskazuje też dziwne minuty. Wolumen normalny, liczba transakcji spada do 11? Ktoś zrobił blok. Wolumen normalny, liczba transakcji sięga 9,000? To kaskada likwidacji przeżuwana na drobne kawałki.
[9] i [10]: „21.883”, pole, które wszyscy wyrzucają
Wolumen bazowy kupna takera. 21.883 z 38.417 BTC w tej świecy zostało zainicjowane przez kupującego, więc oznaczona delta wolumenu wynosi +5.349 BTC, a podział agresora to 57/43 na korzyść kupujących. Giełda oddaje ci za darmo dysproporcję przepływu zleceń w polu, którego prawie nikt nie czyta, bo pandas nie nazwał za nich tej kolumny.
Nie twierdzę, że samodzielnie przewiduje stopy zwrotu; naiwne strategie oparte na delcie są jednym z bardziej niezawodnych sposobów na oddawanie pieniędzy na rzecz opłat. Ale to naprawdę inny pomiar niż cena, dostępny w tym samym zapytaniu, które i tak już wysyłasz, i pozwalający odróżnić rajd, który został wykupiony, od rajdu, który wydarzył się dlatego, że sprzedający się wycofali. Te dwa scenariusze wyglądają identycznie w OHLC, a zachowują się różnie dziesięć minut później. Nasz agent badawczy traktuje hipotezę ignorującą podział takera na giełdzie, która go publikuje, jako pozostawianie dowodów na stole.
[11] „0”: ignorowane — i wszystko inne, czego tu nie ma
Indeks 11 to przestarzałe pole, zawsze równe zeru. Ciekawsza jest lista rzeczy, których ta świeca nie zawiera: żadnego bidu, żadnego asku, żadnego spreadu, żadnej głębokości order booka, żadnej stopy finansowania, żadnego open interest, żadnych likwidacji, żadnej ceny mark, żadnej ceny indeksowej. I co kluczowe — żadnego sposobu, by wiedzieć, czy twoje zlecenie byłoby makerem czy takerem, co na tej giełdzie stanowi różnicę między zapłaceniem 0.045% a zarobieniem 0.01%.
Więc każdy model opłat zbudowany wyłącznie na świecach to założenie przebrane za liczbę. Rozwiązujemy to, zmuszając każdą strategię do zadeklarowania z góry swojego stylu egzekucji, a następnie naliczając stawkę takera na wszystkim, co nie potrafi udowodnić inaczej.
Świeca, która nigdy się nie pojawiła
Ostatni element, i ten, który gryzie najmocniej poza głównymi parami. Minuta bez żadnych transakcji nie tworzy w ogóle żadnej świecy. Dostawcy danych i biblioteki zwykle uzupełniają ją metodą forward-fill: otwarcie = maksimum = minimum = zamknięcie = poprzednie zamknięcie, wolumen 0. Twój wskaźnik szczęśliwie się liczy. Twoja strategia widzi prawidłowy wiersz i może wygenerować sygnał w minucie, w której dosłownie nikt na świecie nie handlował tym instrumentem.
Na jednym kontrakcie wieczystym o średniej kapitalizacji, który pobraliśmy, 4.1% świec 1-minutowych w oknie dwunastu miesięcy miało zero transakcji. Kandydacka strategia mean-reversion na tym symbolu umieszczała 38% swoich wejść na sztucznych świecach, ponieważ płaskie, sztuczne ceny są jak magnes dla wszystkiego, co mierzy odchylenie od średniej kroczącej. Backtestował się pięknie. Handlował dziurami w danych.
Dlatego warstwa pobierania danych niesie teraz flagę logiczną synthetic przy każdej świecy, propagowaną przez każdy resampling, a gauntlet weryfikacyjny odrzuca każdą strategię, której transakcje się na niej skupiają. Tania kolumna. Zabiła więcej kandydatów niż jakikolwiek wskaźnik, jaki kiedykolwiek napisaliśmy.
Dwanaście wartości. Cztery z nich są rutynowo błędnie odczytywane, dwie rutynowo odrzucane, a cała ich kategoria jest nieobecna w wierszu i wymyślana przez silnik. Zanim uruchomisz kolejny backtest, wyciągnij jedną surową świecę ze swojego magazynu danych i przeczytaj na głos każde pole, konfrontując je z tym, co zakłada o nim twoja logika realizacji zleceń. To ćwiczenie na dwadzieścia minut i nigdy nie widziałem, żeby ktoś je zrobił i niczego nie znalazł.
← Wszystkie wpisy