22 października 2026 · dane

Odtworzyliśmy backtest po zmianie historii danych przez dostawcę

Odtworzyliśmy backtest po zmianie historii danych przez dostawcę

Sześć tygodni po uruchomieniu backtestu uruchomiliśmy go ponownie, używając tego samego kodu, konfiguracji i zakresu dat. Kapitał końcowy był inny. Różnica nie była ogromna, ale wystarczyła, by wskaźnik Sharpe'a spadł z 1.31 do 1.08 i zmieniła się ocena najlepszego zestawu parametrów.

Strategia się nie zmieniła. Zmieniły się dane historyczne. Dostawca zrewidował dane transakcyjne i słupki z kilku dni, a w pierwszym uruchomieniu nie zachowaliśmy dokładnej migawki danych wejściowych. Przez kolejne kilka dni ustalaliśmy, czy nowy wynik był skutkiem korekty danych, błędu w potoku czy procesu badawczego, którego nie potrafiliśmy odtworzyć.

Poniedziałek: wyniki ponownego uruchomienia się nie zgadzały

Pierwszą wskazówką była niewielka rozbieżność w płynnej parze spot. W pierwotnym słupku maksimum wynosiło 62,418. W wyniku ponownego uruchomienia było to 62,431. Różnica wydawała się nieistotna, dopóki nie prześledziliśmy wejścia strategii: jej próg znajdował się między tymi cenami. Zmiana kilku słupków oznaczała inne sygnały, a te z kolei — inne pozycje kilka dni później.

Porównaliśmy dzienną liczbę transakcji i ustaliliśmy, że rozbieżność zaczynała się w jednym konkretnym dniu. Następnie porównaliśmy surowe wiersze użyte do wyznaczenia danych za ten dzień. Bieżąca odpowiedź dostawcy zawierała kilka skorygowanych transakcji i zrewidowany słupek zbiorczy. Do odpowiedzi, z której korzystaliśmy, nie dołączono dziennika zmian.

To normalne zachowanie danych. Giełdy naprawiają rekordy, dostawcy uzupełniają braki, korygują błędne transakcje i zmieniają logikę agregacji. Zapytanie o „dane za styczeń” może zwrócić inną odpowiedź w marcu.

Wtorek: znaleźliśmy brakujące potwierdzenie

W rekordzie uruchomienia zapisaliśmy nazwę dostawcy, symbol i zakres dat. Nie zachowaliśmy samej odpowiedzi ani skrótu treści dla każdej partycji danych wejściowych. Mogliśmy więc potwierdzić, o co zapytaliśmy, ale nie jakie bajty zwrócił dostawca.

Pobraliśmy najstarsze pliki z pamięci podręcznej, jakie udało nam się znaleźć, i porównaliśmy je z bieżącą odpowiedzią. Różnica nie wynikała z korekty związanej z podziałem akcji ani z konwersji strefy czasowej. Chodziło o niewielki zestaw skorygowanych rekordów transakcji, które trafiały do naszego modułu budowania słupków. To wykluczyło jedną z hipotez, ale pamięć podręczna była niekompletna, więc nadal nie mogliśmy dokładnie odtworzyć wszystkich zmienionych słupków.

Co zapisaliśmyNa jakie pytanie mogliśmy odpowiedziećCzego brakowało
Dostawca, symbol, zakres datJakie zapytanie wykonało uruchomienieTreść odpowiedzi
Wersja potokuJaki kod zbudował słupkiDokładne wiersze wejściowe
Znacznik czasu uruchomieniaKiedy uruchomiliśmy zadanieKtóra rewizja danych dostawcy była wtedy dostępna

Założyliśmy, że „to samo zapytanie” oznacza „te same dane”. To założenie sprawiło, że eksperyment wyglądał na bardziej odtwarzalny, niż był w rzeczywistości.

Środa: korekta zmieniła obraz sytuacji

Odtworzyliśmy przebieg dla zmienionych dat, korzystając z obu dostępnych wersji danych. Zrewidowane słupki usunęły dwa wejścia i przesunęły jedno wyjście o jeden słupek. Te trzy transakcje odpowiadały za większość zmiany wskaźnika Sharpe'a. Strategia była wrażliwa na kilka obserwacji w pobliżu progu aktywacji, mimo że na wykresie cały szereg cen wyglądał niemal identycznie.

Nie oznaczało to, że pierwotny wynik był nieuczciwy ani że zrewidowany wynik automatycznie był lepszy. Oznaczało, że trzeba było określić zakres tego twierdzenia: poprzednie uruchomienie opisywało migawkę danych, którą mieliśmy w tamtym czasie, a ponowne uruchomienie — bieżącą historię dostawcy. Nie mogliśmy uczciwie nazwać jednego odtworzeniem drugiego.

Czwartek: zmieniliśmy zapis badań

Do nowych uruchomień badawczych dodaliśmy niezmienne migawki danych wejściowych. Każda partycja otrzymuje skrót treści, a manifest uruchomienia zapisuje źródło, czas pobrania, wersję schematu, zasady korekt i wersję kodu transformacji. W przypadku dużych zbiorów przechowujemy same pliki w magazynie adresowanym treścią, dzięki czemu identyczne partycje można współdzielić bez nadpisywania wcześniejszych wersji.

Gdy zasady przechowywania lub licencja nie pozwalają nam zachować surowych danych, zachowujemy najsilniejsze potwierdzenie dozwolone przez warunki: skróty, parametry zapytania, identyfikatory rewizji źródła i małą próbkę sparsowanych wierszy do diagnostyki. Skrót nie pozwala odzyskać danych, ale może nam powiedzieć, że dwa uruchomienia nie korzystały z identycznych bajtów.

Odtwarzalny backtest wymaga identyfikatora danych wejściowych, a nie tylko URL-a do danych. Zapisuj, co otrzymaliśmy, kiedy to otrzymaliśmy i jak przekształciliśmy dane w wiersze wykorzystane przez strategię.

Piątek: z czego zrezygnowalibyśmy następnym razem

Zrezygnowalibyśmy z porównywania wyłącznie zbiorczych wskaźników. Zmieniony wskaźnik Sharpe'a mówi, że coś się przesunęło, ale nie wskazuje przyczyny. Zacznij od liczby wierszy i skrótów partycji, a potem znajdź pierwszą różniącą się datę danych wejściowych. Następnie porównaj w tej kolejności przekształcone wiersze, sygnały, zlecenia i realizacje.

Zrezygnowalibyśmy też z cichego zastępowania starych danych skorygowaną historią. Korekty są przydatne, ale stare uruchomienia powinny pozostać powiązane z pierwotnymi danymi wejściowymi. Nowy zbiór danych powinien tworzyć nowe uruchomienie, z widocznym powiązaniem z poprzednim.

Nasza praktyczna zasada jest teraz prosta: jeśli ponowne uruchomienie ma odtworzyć eksperyment, przypnij migawkę danych. Jeśli ma korzystać ze skorygowanej historii, oznacz je jako nowy eksperyment. Nauczenie się tego zajęło nam kilka dni, a sama zasada zajmuje jeden wiersz w manifeście uruchomienia.

dane rynkowebacktestinginżynieria danychodtwarzalność
← Wszystkie wpisy