Przejdź do treści
Moduł 2 z 2
  1. Python dla Tradera - Od Backtestu do Raportu
  2. Automatyzacja i Boty - Kiedy Ma Sens
  3. Walk-Forward i Monte Carlo - Koniec Iluzji
Algo Systematic Lekcja 05

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...

Ilustracja do lekcji: Python dla Tradera - Od Backtestu do Raportu
5 / 7
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 PythonInterpretacja tak samo jak w Excelu
EV_RCzy >0.15R po kosztach?
PF>1.3 to filtr
MaxDD_RIle 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

  1. Gdy nie masz shift(1) na sygnale: Twój backtest ma lookahead. W Pythonie to jedna linijka która niszczy iluzję.
  2. 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.
  3. Gdy testujesz tylko na jednym pliku CSV z hossy: Podziel na IS/OOS czasowo, jak w lekcji 3.
  4. Gdy nie masz costs_R w metrykach: Prowizja i poślizg muszą być w R_net. Bez tego raport jest fikcją.
  5. 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

  1. 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.
  2. Pobieranie danych z yfinance bez sprawdzenia dziur. yfinance ma dziury, split adjustment, brak delistowanych. Waliduj isna() i wolumen.
  3. Ignorowanie open vs close. Wejście na close w kodzie a w realu na next open = inny wynik. Zawsze entry = open[i+1].
  4. 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.
  5. Ufanie bibliotece backtest która ukrywa egzekucję. Biblioteki typu backtrader mają domyślny cheat_on_close. Musisz wiedzieć co robi silnik.

Ćwiczenie mierzalne

Cel: Zbudować minimalny silnik backtestu i raport dla 1 rynku.

Zadanie 2h:

  1. Pobierz BTCUSDT H1 z Binance (CSV) - min 2 lata.
  2. Skopiuj kod silnika z lekcji, uruchom.
  3. Wyeksportuj trades_df do CSV i porównaj metryki z Excelem z lekcji 4 - muszą się zgadzać +/- 5%.
  4. Dodaj 2 koszty: costs_R = 0.05 i costs_R = 0.10. Policz jak zmienia się PF i EV.
  5. 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ę

  1. Po co shift(1) na sygnale w Pythonie i co się stanie jeśli go pominiesz?
  2. Dlaczego entry na open a nie close jest bezpieczniejsze anty-lookahead?
  3. Napisz formułę Expectancy w pandas: jak policzyć EV w R z trades_df["R"]?
  4. Czym różni się R od R_net i gdzie w kodzie odejmujesz koszty?
  5. 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 kursu 0 / 7
Dalej →

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