Moduł 2 z 2
Python dla Tradera - Od Backtestu do Raportu
Python nie jest magią która zarabia za Ciebie. Python jest skalowalnym Excelem. Jeśli Twoje laboratorium z lekcji 4 ma 5000 transakcji, 5 rynków i 3 reżimy, Excel zaczyna się dławić. Python pozwala: pobrać dane, policzyć 10 systemów naraz, zrobić Walk-Forward, Monte Carlo 10k...
- Autor
- Zordon Intelligence Sp. z o.o.
- Aktualizacja
- Czas czytania
- 6 min
- Źródła
- 2
Wstęp
Python nie jest magią która zarabia za Ciebie. Python jest skalowalnym Excelem. Jeśli Twoje laboratorium z lekcji 4 ma 5000 transakcji, 5 rynków i 3 reżimy, Excel zaczyna się dławić. Python pozwala: pobrać dane, policzyć 10 systemów naraz, zrobić Walk-Forward, Monte Carlo 10k symulacji i wygenerować PDF raport bez klikania.
W tej lekcji nie uczysz się Pythona od zera jak programista. Uczysz się minimalnego stacku tradera systemowego: pandas, vectorized backtest, metryki R, raport który możesz powtórzyć za rok.
Minimalny stack i struktura projektu
Potrzebujesz tylko: Python 3.10+, pandas, numpy, matplotlib. Nie potrzebujesz 15 bibliotek.
trading_lab/
data/ BTCUSDT_H1.csv (OHLCV)
notebooks/ 01_lab.ipynb
src/ backtest_engine.py
reports/ BTC_H1_trend_R.pdf
Zasada: notebook do eksploracji, plik .py do powtarzalnego silnika. Nie pisz wszystkiego w notebooku, bo za miesiąc nie odtworzysz wyniku.
Instalacja:
pip install pandas numpy matplotlib yfinance
Pobranie danych i pierwszy backtest wektorowy
Nie zaczynaj od biblioteki backtesting.py która ukrywa logikę. Napisz wektorowy silnik który widzisz.
import pandas as pd
import numpy as np
# Wczytaj dane H1 - kolumny: timestamp, open, high, low, close, volume
df = pd.read_csv("BTCUSDT_H1.csv", parse_dates=["timestamp"])
df = df.sort_values("timestamp")
# Wskaźniki - tylko pandas, bez magii
df["sma200"] = df["close"].rolling(200).mean()
df["sma20"] = df["close"].rolling(20).mean()
df["atr14"] = (df["high"]-df["low"]).rolling(14).mean()
df["rsi"] = df["close"].rolling(14).apply(lambda x: 100 - (100/(1+x.pct_change().mean())))
# Hipoteza: pullback w trendzie wzrostowym
df["trend"] = df["close"] > df["sma200"]
df["pullback"] = df["close"] < df["sma20"]
df["signal"] = (df["trend"] & df["pullback"]).astype(int)
# BEZ LOOKAHEAD: przesuń sygnał o 1 bar
df["signal_shifted"] = df["signal"].shift(1).fillna(0)
# Parametry ryzyka R
atr_mult_sl = 1.5
atr_mult_tp = 3.0
df["sl_dist"] = df["atr14"] * atr_mult_sl
df["tp_dist"] = df["atr14"] * atr_mult_tp
Kontynuacja logiki w pętli pozycyjnej:
position = 0
entry_price = 0
sl = 0
tp = 0
trades = []
for i in range(1, len(df)):
row = df.iloc[i]
if position == 0 and row["signal_shifted"] == 1:
position = 1
entry_price = row["open"]
sl = entry_price - row["sl_dist"]
tp = entry_price + row["tp_dist"]
entry_time = row["timestamp"]
risk = entry_price - sl
elif position == 1:
low = row["low"]
high = row["high"]
exit_price = None
exit_reason = None
if low ≤ sl:
exit_price = sl
exit_reason = "SL"
elif high >= tp:
exit_price = tp
exit_reason = "TP"
if exit_price is not None:
pnl = exit_price - entry_price
r_multiple = pnl / risk if risk !=0 else 0
trades.append((
"entry_time": entry_time,
"exit_time": row["timestamp"],
"entry": entry_price,
"exit": exit_price,
"pnl": pnl,
"R": r_multiple,
"reason": exit_reason
))
position = 0
trades_df = pd.DataFrame(trades)
To jest Twój silnik. Każda linia widoczna, każda decyzja z shift(1).
Metryki systemowe w pandas - to samo co w Excelu ale automatycznie
def system_metrics(trades_df, costs_R=0.05):
trades_df["R_net"] = trades_df["R"] - costs_R
N = len(trades_df)
win_rate = (trades_df["R_net"] > 0).mean()
avg_win = trades_df.loc[trades_df["R_net"]>0, "R_net"].mean()
avg_loss = trades_df.loc[trades_df["R_net"]<0, "R_net"].mean()
ev = trades_df["R_net"].mean()
pf = trades_df.loc[trades_df["R_net"]>0, "R_net"].sum() / abs(trades_df.loc[trades_df["R_net"]<0, "R_net"].sum())
sqn = (N**0.5) * (trades_df["R_net"].mean() / trades_df["R_net"].std()) if N>5 else 0
trades_df["equity_R"] = trades_df["R_net"].cumsum()
trades_df["peak_R"] = trades_df["equity_R"].cummax()
trades_df["dd_R"] = trades_df["equity_R"] - trades_df["peak_R"]
max_dd = trades_df["dd_R"].min()
return (
"N": N,
"WinRate": win_rate,
"AvgWin_R": avg_win,
"AvgLoss_R": avg_loss,
"EV_R": ev,
"PF": pf,
"SQN": sqn,
"MaxDD_R": max_dd
)
| Metryka Python | Interpretacja tak samo jak w Excelu |
|---|---|
| EV_R | Czy >0.15R po kosztach? |
| PF | >1.3 to filtr |
| MaxDD_R | Ile R musisz przeżyć psychicznie |
| SQN | >2.0 do handlu |
Jeśli nie potrafisz policzyć tego w pandas, wróć do lekcji 4. Python ma tylko przyspieszyć, nie zastąpić zrozumienia.
Wizualizacja która daje decyzję, nie tylko ładny wykres
import matplotlib.pyplot as plt
trades_df["equity_R"].plot(title="Equity Curve w R - koszty wliczone")
plt.axhline(0, color="black", linewidth=0.5)
plt.ylabel("R skumulowane")
plt.show()
trades_df["R_net"].hist(bins=30)
plt.title("Rozklad R-multiple - czy masz ogon zyskow?")
plt.show()
Szukasz: czy rozkład R ma długi prawy ogon (kilka dużych wygranych) czy jest symetryczny? System trendujący musi mieć prawy ogon. Jeśli nie ma - filtr lub TP za bliski.
Raport automatyczny - to jest produkt tej lekcji
Zamiast screenshotów, generujesz raport md:
metrics = system_metrics(trades_df)
report = f"""
# Raport Systemu - BTC H1 Pullback Trend
N: {metrics['N']}
WinRate: {metrics['WinRate']}
EV: {metrics['EV_R']} R
PF: {metrics['PF']}
SQN: {metrics['SQN']}
MaxDD: {metrics['MaxDD_R']} R
"""
with open("reports/report.md","w") as f:
f.write(report)
Za 6 miesięcy otworzysz raport i w 30 sekund wiesz czy system zdegradował.
Kiedy plan mówi nie handluj
- Gdy nie masz
shift(1)na sygnale: Twój backtest ma lookahead. W Pythonie to jedna linijka która niszczy iluzję. - Gdy używasz funkcji liczących na przyszłych danych bez shift: Każdy wskaźnik liczony na barze który zawiera przyszłe dane w realu.
- Gdy testujesz tylko na jednym pliku CSV z hossy: Podziel na IS/OOS czasowo, jak w lekcji 3.
- Gdy nie masz
costs_Rw metrykach: Prowizja i poślizg muszą być wR_net. Bez tego raport jest fikcją. - Gdy Twój silnik otwiera 3 pozycje na raz a liczysz MaxDD per trade: Musisz zasymulować pozycję portfelową, inaczej MaxDD zaniżony.
Błędy które kosztują najwięcej
- Pętla po DataFrame bez wektoryzacji dla 1M wierszy. Dla nauki pętla jest ok (czytelna). Dla produkcji użyj wektoryzacji lub
numba. Ale nie optymalizuj przedwcześnie. - Pobieranie danych z yfinance bez sprawdzenia dziur. yfinance ma dziury, split adjustment, brak delistowanych. Waliduj
isna()i wolumen. - Ignorowanie
openvsclose. Wejście na close w kodzie a w realu na next open = inny wynik. Zawszeentry = open[i+1]. - Brak seed dla Monte Carlo i brak wersji danych. Za miesiąc nie odtworzysz wyniku bo dane się zmieniły lub losowanie inne. Zapisuj hash pliku danych i seed.
- Ufanie bibliotece backtest która ukrywa egzekucję. Biblioteki typu
backtradermają domyślnycheat_on_close. Musisz wiedzieć co robi silnik.
Ćwiczenie mierzalne
Cel: Zbudować minimalny silnik backtestu i raport dla 1 rynku.
Zadanie 2h:
- Pobierz BTCUSDT H1 z Binance (CSV) - min 2 lata.
- Skopiuj kod silnika z lekcji, uruchom.
- Wyeksportuj
trades_dfdo CSV i porównaj metryki z Excelem z lekcji 4 - muszą się zgadzać +/- 5%. - Dodaj 2 koszty:
costs_R = 0.05icosts_R = 0.10. Policz jak zmienia się PF i EV. - Wygeneruj 3 wykresy: equity w R, rozkład R, drawdown w R. Zapisz do
reports/.
Kryteria zaliczenia:
N > 100
EV_R po kosztach 0.05 > 0
MaxDD_R wyliczony i zapisany
Kod ma shift(1) i entry na open
Sprawdź się
- Po co
shift(1)na sygnale w Pythonie i co się stanie jeśli go pominiesz? - Dlaczego entry na
opena nieclosejest bezpieczniejsze anty-lookahead? - Napisz formułę Expectancy w pandas: jak policzyć EV w R z
trades_df["R"]? - Czym różni się
RodR_neti gdzie w kodzie odejmujesz koszty? - Masz 1M wierszy H1 i pętlasz
for i in range(len(df))- dlaczego to może być wolne i jakie jest rozwiązanie?
Częste pytania
Czy muszę znać programowanie aby być traderem systemowym? Nie, ale musisz znać narzędzia. Excel wystarcza na start. Python jest potrzebny gdy Excel jest za wolny lub chcesz testować 10 rynków. Jeśli nie chcesz kodować, zostań przy Pine + Excel, ale zrozum ograniczenia.
Jaka biblioteka backtest jest najlepsza?
Dla nauki: Twój własny silnik (jak w lekcji) + pandas. Dla produkcji: vectorbt lub backtesting.py ale tylko po zrozumieniu lookahead. Unikaj czarnych skrzynek na start.
Czy mogę backtestować na Jupyter czy muszę mieć IDE?
Jupyter jest idealny do tej lekcji. Ale finalny silnik przenieś do .py aby był wersjonowalny w git. Jupyter ma tendencję do ukrywania stanu.
Jak pobrać dane bez survivorship bias? Dla krypto: pobieraj top 50 z danego roku, nie dziś. Dla akcji: kup bazę Norgate lub używaj ETF zamiast pojedynczych akcji. Dla startu testuj indeks, nie koszyk spółek.
Czy muszę używać AI/ML w Pythonie? Nie. 90% trwałych systemów to proste reguły jak w tej lekcji. ML dodaje ogromny overfitting jeśli nie masz Walk-Forward i 10k+ transakcji. Najpierw R i PF, potem ewentualnie ML.
Reklama
Najważniejsze wnioski
- Python nie jest magią która zarabia za Ciebie. Python jest skalowalnym Excelem. Jeśli Twoje laboratorium z lekcji 4 ma 5000 transakcji, 5 rynków i 3 reżimy, Excel zaczyna się dławić. Python pozwala: pobrać dane, policzyć 10 systemów naraz, zrobić Walk-Forward, Monte Carlo 10k...
Źródła i dokumenty
Materiał opieramy na dokumentach źródłowych. Wartości zmienne — opłaty, limity, wskaźniki regulacyjne — sprawdź u źródła przed podjęciem decyzji.
- TradingView — Pine Script reference
- Python for Finance — Yves Hilpisch
Zastrzeżenie
- KursTradera.pl to serwis edukacyjny o charakterze informacyjnym. Nie prowadzimy działalności maklerskiej, nie przyjmujemy i nie przekazujemy zleceń, nie zarządzamy portfelem, nie oferujemy instrumentów finansowych i nie świadczymy usług doradztwa inwestycyjnego w rozumieniu ustawy o obrocie instrumentami finansowymi. Materiały opisują mechanizmy rynkowe i proces decyzyjny — nie są instrukcją kupna lub sprzedaży jakiegokolwiek instrumentu.
- Żadna z publikowanych treści nie stanowi rekomendacji inwestycyjnej, informacji rekomendującej ani rekomendacji w rozumieniu Rozporządzenia Parlamentu Europejskiego i Rady (UE) nr 596/2014 (MAR) oraz aktów wykonawczych. Nie jest też ofertą nabycia lub zbycia instrumentów finansowych ani zaproszeniem do składania ofert. Wszelkie przykłady, wykresy, zrzuty ekranu i liczby mają charakter ilustracyjny i odnoszą się do sytuacji hipotetycznych lub historycznych.
- Inwestowanie i trading wiążą się z ryzykiem utraty części lub całości zainwestowanego kapitału. Wyniki osiągnięte w przeszłości — w tym wyniki przedstawione w materiałach edukacyjnych, backtestach i przykładach — nie gwarantują wyników przyszłych i nie są ich wiarygodnym wskaźnikiem. Żadna opisana metoda ani strategia nie eliminuje ryzyka. Decyzje inwestycyjne podejmujesz samodzielnie, na własną odpowiedzialność i po zapoznaniu się z aktualnymi dokumentami swojego brokera.
- Instrumenty z dźwignią (CFD, kontrakty terminowe, opcje, transakcje typu margin) mogą generować straty przewyższające wartość wpłaconych środków — możesz stracić więcej, niż zainwestowałeś, i zostać wezwanym do uzupełnienia depozytu (margin call). W Unii Europejskiej dźwignia dla klientów detalicznych handlujących CFD na głównych parach walutowych jest ograniczona decyzją ESMA (interwencja produktowa w sprawie CFD i opcji binarnych), ale ograniczenie dźwigni nie usuwa ryzyka. Odsetek rachunków detalicznych przynoszących straty różni się między brokerami i zmienia się w czasie — aktualną wartość odczytaj z ostrzeżenia ryzyka u swojego brokera i zapisz razem z datą.
- Backtesty, symulacje i przykładowe wyniki nie są rzeczywistymi wynikami transakcyjnymi. Powstają w warunkach odtwarzanych z danych historycznych, nie uwzględniają w pełni poślizgu cenowego, płynności, spreadu, prowizji, swapu, wpływu własnych zleceń na rynek ani czynnika psychologicznego — a wszystkie te elementy pogarszają wynik w praktyce. Wyniki historyczne nie chronią przed przyszłymi stratami.
Kto to pisze i jak to sprawdzamy
Materiały pisze i weryfikuje Redakcja Kurs Tradera — zasady opisuje o serwisie: każda liczba ma źródło, a braki pokazujemy jawnie. Błąd merytoryczny zgłosisz przez kontakt — poprawki opisujemy w treści.
Postęp zapisujemy wyłącznie w tej przeglądarce (localStorage) — nie ma konta ani kopii na serwerze. Wyczyszczenie danych witryny go usunie. Zarządzaj swoimi danymi