Projekt końcowy: strategia do sprawdzenia
Złożysz własną regulowaną strategię i prześledzisz jej sygnały, transakcje i wyniki.
Projekt łączy w pliku poznane części: średnie, wejścia, warunki, własną funkcję, zlecenia i wyświetlanie. Celem jest program, którego każdą decyzję potrafisz powiązać z danymi i opisanymi regułami.
Opisz program bez kodu
Strategia obserwuje przecięcia dwóch EMA. Trzecia, dłuższa SMA sprawdza, czy zamknięcie znajduje się nad średnią.
Warunkiem longa jest przejście szybkiej EMA nad wolną i zamknięcie nad SMA trendu. Długości mają zachować kolejność szybka < wolna < trend. Inaczej program nie składa wejścia.
Przecięcie EMA w dół daje zamknięcie rynkowe. Do wejścia należą też stop i cel w tickach. Symulacja używa stałej jednej jednostki, prowizji 0,1 procent i dwóch ticków poślizgu.
To reguły zadania. Nie wnioskujemy z nich o zyskowności na jakimś rynku. Do kontroli używamy stałych danych demonstracyjnych książki.
Pełny program
Utwórz final_strategy.py:
"""
@pyne edge
"""
from pynecore.lib import close, color, input, location, plot, plotshape, script, shape, strategy, ta
def lengths_are_ordered(fast: int, slow: int, trend: int) -> bool:
return fast < slow and slow < trend
@script.strategy(
"Filtered Crossing Strategy",
overlay=True,
initial_capital=10000,
default_qty_type=strategy.fixed,
default_qty_value=1,
pyramiding=1,
commission_type=strategy.commission.percent,
commission_value=0.1,
slippage=2,
)
def main(
fast_length: int = input.int(9, title="Fast EMA length", minval=1, maxval=200),
slow_length: int = input.int(21, title="Slow EMA length", minval=1, maxval=200),
trend_length: int = input.int(50, title="Trend SMA length", minval=1, maxval=200),
use_trend_filter: bool = input.bool(True, title="Use trend filter"),
stop_ticks: int = input.int(100, title="Stop distance (ticks)", minval=1),
target_ticks: int = input.int(200, title="Target distance (ticks)", minval=1),
):
fast_average = ta.ema(close, fast_length)
slow_average = ta.ema(close, slow_length)
trend_average = ta.sma(close, trend_length)
crosses_up = ta.crossover(fast_average, slow_average)
crosses_down = ta.crossunder(fast_average, slow_average)
valid_lengths = lengths_are_ordered(fast_length, slow_length, trend_length)
trend_passes = not use_trend_filter or close > trend_average
entry_signal = valid_lengths and crosses_up and trend_passes
if entry_signal:
strategy.entry("Long", strategy.long)
if crosses_down:
strategy.close("Long")
strategy.exit("Bracket", "Long", loss=stop_ticks, profit=target_ticks)
plot(fast_average, "Fast EMA", color=color.blue, linewidth=2)
plot(slow_average, "Slow EMA", color=color.orange, linewidth=2)
plot(trend_average, "Trend SMA", color=color.gray, linewidth=2)
plotshape(entry_signal, "Entry signal", style=shape.triangleup,
location=location.belowbar, color=color.green)Zapisz i uruchom na demonstracji. Pojawią się trzy średnie i zielone trójkąty warunku wejścia. Rzeczywiste wejścia i wyjścia pokazują znaczniki transakcji strategii.

Co liczymy na każdej świecy?
Trzy średnie i dwa przecięcia wykonują się przed warunkami. Liczymy je nawet wtedy, gdy filtr nie pozwala wejść, by stan przechodził przez cały szereg.
Własne lengths_are_ordered bada trzy liczby i zwraca logikę. Nie ma stanu świec. valid_lengths zachowuje obecny wynik dla dalszych wyrażeń.
trend_passes jest prawdziwe przy wyłączonym filtrze albo zamknięciu nad SMA. input.bool daje przełączane wejście logiczne. not use_trend_filter jest prawdą przy wyłączonym filtrze, więc drugi człon nie jest wtedy potrzebny.
Wyłączając filtr, nadal liczymy i rysujemy średnią. Przełącznik zmienia sprawdzenie wejścia, nie widoczność linii.
Nieprawidłowa relacja długości
Osobne granice mówią tylko, że każda długość ma leżeć między 1 a 200. Nadal można podać szybką 30 i wolną 10, naruszając kolejność zadania.
Funkcja osobno sprawdza związek. Przy złej kolejności linie są rysowane, lecz entry_signal nie może być prawdziwe. Program nie otwiera więc pozycji z niezgodną relacją długości.
W debuggerze sprawdzisz to przez valid_lengths. Przy niepoprawnych zapisanych wejściach pozostanie fałszywe przez cały przebieg.
Sygnał nie zawsze tworzy transakcję
Trójkąt oznacza warunek wejścia. Strategia przekazuje zlecenie symulatorowi. Realizacja może wymagać kolejnej świecy, wystarczającego kapitału i dozwolonej ilości.
Przy pyramiding=1 symulator może nie dopuścić następnego longa, gdy pozycja już istnieje. Nie zakładaj więc równej liczby sygnałów i transakcji.
Na początku brakuje SMA filtra o długości pięćdziesiąt. Przy włączonym filtrze nie można wtedy uznać ceny za leżącą nad nią, więc warunek wejścia nie przechodzi.
Sprawdź działanie
Zacznij od domyślnych wartości i sprawdź w Properties ilość, koszty i realizację. Starsze nadpisanie może zmienić ustawienia źródła.
Znajdź zielony sygnał. W debuggerze lub danych wykresu obejrzyj obie EMA na obecnej i poprzedniej świecy. Sprawdź przecięcie, filtr SMA i kolejność długości. Wszystkie trzy części muszą być prawdziwe.
Następnie znajdź powiązane wejście w Trades. Sprawdź świecę i cenę realizacji. Przy wyjściu ustal, czy wyjaśnia je przecięcie, stop czy cel.
Wypróbuj przewidywalne przypadki
Do kontroli wybieraj zmiany o przewidywalnym skutku:
| Zmiana | Co sprawdzić? |
|---|---|
| Równa szybka i wolna długość | Kontrola długości blokuje nowe sygnały wejścia |
| Filtr trendu wyłączony | Przecięcie EMA nie wymaga ceny nad SMA |
| Większy stop i cel | Inna odległość może zmienić świecę wyjścia |
| Zmieniona ilość | Sygnał cenowy ten sam, wynik pieniężny może się różnić |
W jednym przebiegu zmieniaj tylko jeden punkt. Zapisuj najpierw przewidywanie, potem wynik. Przy rozbieżności szukaj w debuggerze pierwszej odmiennej decyzji.
Porównanie wyników
Porównując włączony i wyłączony filtr, zachowaj te same dane, ilość, koszty i długości. Oglądaj Performance razem z Trades. Mała liczba transakcji lub otwarta ostatnia pozycja wpływają na podsumowanie.
Na prawdziwej historii określ okres strojenia i osobnej kontroli jeszcze przed obejrzeniem wyników. Zmieniając parametry po obejrzeniu okresu kontrolnego, włączasz go do strojenia.
Początek danych musi zapewnić dostateczną rozgrzewkę. Oddzielne uruchomienie wyciętego okresu odbudowuje średnie i pozycję od nowa. Jego początek może więc różnić się od tego samego okresu w środku dłuższej serii.
Spróbuj samodzielnie
Dodaj przełącznik Use crossings exit. Włączony zachowuje zamknięcie na przecięciu w dół, wyłączony pozostawia tylko stop i cel. Przecięcie obliczaj w obu trybach.
Rozwiązanie zadania
Do parametrów main():
use_crossing_exit: bool = input.bool(True, title="Use crossings exit"),Gałąź zamknięcia:
if use_crossing_exit and crosses_down:
strategy.close("Long")crosses_down = ta.crossunder(...) pozostaje przed warunkami, a strategy.exit w obu położeniach przełącznika.
Przy wyłączonym zamknięciu przez przecięcie pozycja może pozostać otwarta po przecięciu EMA w dół. Nowy sygnał wejścia może się pojawić, choć istniejąca pozycja nie pozwoli otworzyć transakcji.
Stwórz własną wersję
Skopiuj program pod nową nazwą i opisz jedną nową regułę. Określ dane i świecę decyzji, sposób ręcznego sprawdzenia i wejście sterujące regułą.
Najpierw oblicz ją w osobnej zmiennej. Narysuj lub obejrzyj w debuggerze, zanim użyjesz jako warunku wejścia czy wyjścia. Błąd można wtedy znaleźć w obliczeniu, zanim zaczniesz tłumaczyć wynik całej strategii.
Źródło, zapisane ustawienia i dane wspólnie opisują eksperyment. Zachowując je, możesz odtworzyć i sprawdzić przebieg.