Python do backtestowania strategii forex — stos, cykl, pułapki
Strategy Tester w MT5 wystarcza dopóki strategia mieści się w jego logice — proste reguły wejścia, jeden instrument, brak obróbki danych makro. Gdy zaczynasz robić coś bardziej autorskiego, MT5 robi się ciasny, a okazuje się, że społeczność systematyczna od lat trzyma swoje testy w Pythonie. Powód jest prosty: pełna kontrola nad regułami, biblioteki statystyczne klasy naukowej, dowolne źródła danych w jednym skrypcie i pętla badawcza, w której między pomysłem a krzywą kapitału mija kilkanaście minut.
Dlaczego ludzie odchodzą od Strategy Testera
Strategy Tester radzi sobie z optymalizacją jednego eksperta na jednej parze i jednym okresie — i tam się kończy. Próby wpięcia danych spoza brokera, łączenia kilku par w jeden sygnał czy policzenia Sharpe'a obok rozkładu drawdownu wymagają ręcznego klejenia plików w Excelu. Python zamyka to w jednym skrypcie: dane z brokera idą do `pandas`, dane makro dokładasz tym samym `merge`, a metryki ryzyka liczy biblioteka pisana przez akademików. Tło o automatyzacji rzemiosła leży w artykule o pierwszych krokach w handlu algorytmicznym; jeśli wahasz się między ekosystemem MetaQuotes a Pythonem, kontekst daje porównanie botów MQL5 i Pythona.
Stos narzędzi — co składa się na typowy projekt
Praktyczny stos jest krótki. Do danych służą `pandas` i `numpy` — pierwszy odpowiada za szeregi czasowe i resampling z M1 na H1, drugi za szybką arytmetykę wektorową, na której opierają się indykatory. Silnikiem backtestu jest zwykle `backtrader` autorstwa Daniela Rodrigueza, ułożony jako klasy Strategy i Cerebro w stylu event-driven, albo `vectorbt` Olega Polakowa, gdy zależy ci na przebadaniu setek wariantów parametrów w kilkadziesiąt sekund. Wykresy rysują `matplotlib` i `plotly` — pierwszy do raportów statycznych, drugi do interaktywnego przeglądania transakcji. Dane pobierasz przez `yfinance` dla zamknięć dziennych, `ccxt` dla rynku krypto oraz oficjalny pakiet `MetaTrader5`, gdy chcesz pracować na realnych notowaniach brokera. Cały stos jest darmowy i open-source.
Praca w cyklu badawczym — od reguły do walidacji
Cykl projektu rozpada się na cztery kroki, które warto trzymać oddzielnie. Najpierw spisujesz reguły wejścia i wyjścia po polsku — jeśli nie potrafisz powiedzieć „kupuję, gdy nachylenie EMA-50 jest dodatnie, a zamknięcie przebija dwudziestodniowe maksimum", nie ma sensu pisać kodu. Drugi krok to przepisanie reguł na wyrażenia wektorowe w `pandas`: warunki na całej kolumnie naraz, bez pętli po świecach — dzięki temu test dziesięciu lat H1 mieści się w sekundach. Trzeci krok to uruchomienie silnika na pełnej historii z prowizją, poślizgiem, stopem i take profitem — efektem jest krzywa kapitału z listą transakcji. Czwarty — i właśnie tu początkujący odpuszczają — to walidacja poza próbą: rezerwujesz ostatnie dwa lata, optymalizujesz parametry tylko na wcześniejszym okresie, a werdykt czytasz z out-of-sample. Mechanika kroczącego okna leży w artykule o walk-forward analysis, a ramy doboru danych i metryk — w poradniku, jak backtestować strategie.
Czego Python sam z siebie nie zrobi
Świeży użytkownik wnioskuje z dokumentacji `backtradera`, że biblioteka liczy wszystko za niego. Nie liczy. Domyślny silnik nie zna spreadu twojego brokera, nie wie, że na newsach kwotowania rozjeżdżają się o pięć pipsów, a prowizja ECN to siedem dolarów od lota i wchodzi po obu stronach transakcji. Musisz to wpisać ręcznie — `commission`, `slippage_perc`, własny model spreadu zależnego od godziny. Druga pułapka to jakość danych: darmowe szeregi z `yfinance` mają luki weekendowe i pojedyncze braki minut, a tick data z Dukascopy bywa niekompletna dla par egzotycznych. Trzeci grzech to przyzwyczajenie do gładkich krzywych — backtest in-sample z pięcioma optymalizowanymi parametrami niemal zawsze obieca 200 procent rocznie, dopóki nie odetniesz mu zmiennej i nie spojrzysz, jak krzywa wygląda poza próbą. Realistyczny test pokazuje zwykle 30–50 procent mniej, niż obiecywała wersja naiwna.
Przykład hipotetyczny — projekt na EUR/USD
Załóżmy strategię na otwarciu sesji londyńskiej dla EUR/USD na M15. Reguła brzmi: kup, gdy kurs przebija maksimum z pięciu świec po godzinie 9.00 czasu warszawskiego, sprzedaj na przełamaniu minimum; stop loss 1,5-krotność ATR z dwudziestu okresów, take profit dwukrotność stopu. Dane na lata 2018–2024 ściągasz przez pakiet `MetaTrader5`, ładujesz do `pandas` i resamplujesz na M15. Backtester z prowizją sześciu dolarów od lota, spreadem 0,8 pipsa poza newsami oraz losowym pipsem poślizgu daje skuteczność 51 procent, profit factor 1,28, maksymalny drawdown 14 procent i Sharpe 0,9. Po podziale na cztery lata in-sample i dwa lata out-of-sample średnia OOS spada o około jedną trzecią — co zostawia decyzję wdrożeniową na poziomie rozsądnym, choć nie spektakularnym. Wszystkie liczby są wyłącznie ilustracyjne i pokazują kształt wyniku, nie obietnicę.
„Python has become a powerful programming language and ecosystem for the financial industry — for everything from analyzing financial data to algorithmic trading to risk management." — Yves Hilpisch, Python for Finance: Mastering Data-Driven Finance, O'Reilly, 2018
Zastrzeżenia, które należą do każdego raportu
Po zamknięciu backtestu warto dopisać do raportu krótką ramkę z czterema zastrzeżeniami. Pierwsze: jaki spread przyjąłeś i czy uwzględniłeś jego rozszerzenie wokół danych makro — różnica między stałym 0,8 a realnym 2,5 pipsa na NFP to często cała marża strategii. Drugie: czy dane są wolne od look-ahead bias, to znaczy czy nie liczysz wskaźnika z zamknięcia bieżącej świecy, którego w realnym czasie jeszcze nie znasz. Trzecie: ile parametrów optymalizowałeś jednocześnie — pięć to próg, powyżej którego nawet walk-forward nie ratuje. Czwarte: czy wynik utrzymał się na out-of-sample, czy tylko na pełnej historii. Bez tych zdań raport jest marketingiem, nie audytem. Szerszy kontekst metodyki znajdziesz w sekcji demo, backtesting i forward testing na MyBank.pl.
Co zrobić jutro
- Zainstaluj Pythona w wersji 3.11 lub nowszej z bibliotekami `pandas`, `numpy`, `backtrader`, `matplotlib` oraz oficjalnym pakietem `MetaTrader5` — wszystko jednym poleceniem `pip install` — a następnie ściągnij dwa lata danych H1 dla pary, którą faktycznie handlujesz, i zapisz je w pliku CSV, żeby mieć stałe źródło dla kolejnych iteracji bez ponownych pobrań.
- Spisz po polsku reguły strategii, którą znasz najlepiej — jedno zdanie na warunek wejścia, jedno na wyjście, jedno na stop loss i jedno na take profit — i dopiero potem przepisz je na wektorowe wyrażenia w `pandas` tak, żeby cały test mieścił się w trzydziestu liniach kodu, bez pętli po świecach i bez kombinowania z optymalizacją parametrów na tym etapie.
- Uruchom backtest z prowizją realną dla twojego brokera, stałym spreadem i losowym poślizgiem jednego pipsa, a następnie porównaj wynik z wersją bez kosztów; różnica między krzywymi mówi ci, ile domniemanej przewagi to artefakt założenia o cenie środkowej — jeśli koszty zjadają więcej niż połowę zysku, strategia jest zbyt cienka na rachunek live.
- Podziel historię na cztery lata in-sample i rok out-of-sample, zoptymalizuj nie więcej niż dwa parametry wyłącznie na in-sample, zamroź najlepszy zestaw i raz uruchom test na out-of-sample; jeżeli skuteczność spada o więcej niż jedną trzecią, masz curve-fitting i trzeba uprościć logikę, a nie szukać kolejnej optymalizacji.
- Załóż prosty notes — choćby plik Markdown w tym samym repozytorium — w którym po każdym backteście zapisujesz cztery zdania: jaki spread, czy uwzględniłeś look-ahead, ile parametrów optymalizowałeś i czy wynik out-of-sample utrzymał się powyżej połowy in-sample; bez tego dziennika po trzech miesiącach nie będziesz pamiętać, który raport był uczciwy, a który po cichu naciągnięty.
Źródła i bibliografia
-
Backtrader Backtrader documentation — Introduction · oficjalna dokumentacja open-source'owej biblioteki Daniela Rodrigueza: model event-driven, klasy Strategy i Cerebro, integracja danych www.backtrader.com ↗
-
vectorbt vectorbt usage documentation · oficjalny przewodnik po wektorowym backtestowaniu w numpy/pandas — przykłady i sweep parametrów vectorbt.dev ↗
-
O'Reilly Media Yves Hilpisch — Python for Finance, 2nd Edition (2018) · kanoniczna pozycja o zastosowaniach Pythona w analityce finansowej, algorytmice i zarządzaniu ryzykiem www.oreilly.com ↗
-
MQL5 MetaTrader 5 Python Integration — official reference · oficjalne API pakietu MetaTrader5: pobieranie historycznych OHLC i tików, dostęp do konta i składanie zleceń z Pythona www.mql5.com ↗
-
pandas pandas — Time series / date functionality · referencja czasu w pandas: konwersje, resampling, indeksowanie i przesunięcia używane w każdym backteście pandas.pydata.org ↗
Najczęstsze pytania
Czy potrzebuję zaawansowanej znajomości Pythona, żeby zacząć?
Wystarczy znajomość podstaw — pętle, listy, funkcje, importowanie bibliotek i czytanie pliku CSV. Reszta to pandas, którego nauczysz się po drodze, bo każdy projekt backtestowy używa tych samych kilku operacji: wczytanie danych, resampling, obliczenie wskaźnika, zaplanowanie warunku wejścia, agregacja wyników. Pierwsza strategia zwykle zajmuje trzydzieści linii kodu, więc nie ma sensu czekać aż „opanujesz Pythona w 100 procent". Lepiej trzymać dwa równoległe tory: krótki kurs podstaw (cztery do sześciu tygodni po godzinie dziennie) i natychmiastowy projekt, który właśnie ćwiczysz. Jeżeli umiesz napisać Excel z formułami i rozumiesz, czym jest funkcja, masz wystarczające minimum — biblioteka pandas niemal całkowicie zastępuje arkusz, tylko działa szybciej i pozwala na pełną walidację out-of-sample.
Czemu wybrać backtrader zamiast vectorbt — albo odwrotnie?
To dwie różne filozofie. Backtrader jest event-driven: silnik przechodzi po świecach jedna po drugiej, woła metodę next() na twojej klasie Strategy i odwzorowuje przepływ realnego handlu — łatwo dopinasz tu logikę zarządzania pozycją, trailing stop, częściowe wyjścia. Cena to szybkość: dziesięć lat M5 testowanych na jednej parze potrafi zająć kilka minut. Vectorbt idzie inną drogą — całą logikę wyrażasz wektorowo na kolumnach pandas, silnik liczy wszystko równolegle w numpy, a sweep stu wariantów parametrów wykonuje się w kilkanaście sekund. Cena to ograniczenie ekspresji: skomplikowane reguły wejścia uzależnione od stanu portfela trudniej tu zakodować. W praktyce trader trzyma oba — vectorbt do szybkich eksploracji i optymalizacji, backtrader do finalnej walidacji najlepszej kandydatki z realnym modelem prowizji i poślizgu.
Skąd brać dane historyczne, żeby były wiarygodne?
Z trzech źródeł, w kolejności rozsądku. Pierwsze: oficjalny pakiet MetaTrader5 — dostajesz historię z platformy własnego brokera, więc spread, swap i prowizja w backteście odpowiadają temu, co potem zobaczysz na rachunku live. To najuczciwsza opcja dla strategii, którą zamierzasz wdrożyć. Drugie: Dukascopy publikuje dane tickowe i M1 dla głównych par od 2003 roku — jakość jest profesjonalna, ale spread tam pochodzi z platformy Dukascopy, niekoniecznie twojego brokera. Trzecie: yfinance dla zamknięć dziennych i CCXT dla rynku krypto — wystarczają do prototypowania, ale mają luki weekendowe i pojedyncze braki danych, więc nie nadają się do walidacji strategii intradayowych. Każde źródło dokładnie udokumentuj w nagłówku skryptu, żeby za pół roku wiedzieć, na czym opierał się raport — to drobiazg, który ratuje pracę przy każdym audycie.
Jak rozpoznać, że backtest jest zbyt piękny, żeby był prawdziwy?
Cztery sygnały wystarczają. Pierwszy: skuteczność powyżej 75 procent na ponad dwustu transakcjach — to praktycznie nieosiągalne poza skalpingiem na bardzo wąskich rynkach, więc taki wynik wskazuje na look-ahead lub błąd w danych. Drugi: krzywa kapitału bez większych zjazdów — realne strategie mają drawdowny dwucyfrowe, więc czysta linia w górę to ostrzeżenie o curve-fitcie. Trzeci: profit factor powyżej 3,5 — bardzo rzadko spotykany na rynku forex, w którym ECN spread sam zjada kilkanaście procent przewagi. Czwarty: silna wrażliwość na parametry, czyli sytuacja, w której zmiana okresu średniej z 14 na 12 załamuje wynik — to dowód, że strategia nauczyła się szumu zamiast struktury. Jeśli dwa z czterech sygnałów świecą się jednocześnie, raport jest podejrzany niezależnie od metryk; należy uprościć logikę, zmniejszyć liczbę optymalizowanych zmiennych i powtórzyć test na out-of-sample.