Итоговый проект: проверяемая стратегия
Соберите настраиваемую стратегию и изучите её сигналы, сделки и результаты.
Итоговый проект объединяет изученное: средние, inputs, условия, функцию, приказы и отображение. Цель в том, чтобы каждое решение можно было связать с данными и написанными правилами.
Опишите программу без кода
Стратегия следит за пересечением двух EMA. Третья, длинная SMA проверяет закрытие выше среднего.
Длинный вход требует перехода быстрой EMA выше медленной снизу и закрытия выше трендовой SMA. Порядок длин: быстрая < медленная < трендовая. При нарушении программа не подаёт вход.
Пересечение EMA вниз даёт рыночное закрытие. К входу привязаны стоп и цель в тиках. Используются одна единица, комиссия 0,1 процента и проскальзывание два тика.
Это правила задания, а не основание ожидать прибыль на рынке. Проверяем их на фиксированных учебных данных.
Полная программа
Создайте 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)Сохраните и запустите. Появятся три средние и зелёные треугольники условий входа. Фактические входы и выходы отмечены сделками стратегии.

Что считается на каждой свече?
Три средние и два пересечения выполняются до условий, даже когда фильтр затем запрещает вход. Состояния расчётов проходят весь ряд.
lengths_are_ordered проверяет три числа и возвращает логическое значение. Собственного свечного состояния нет. valid_lengths хранит текущий результат для следующих выражений.
trend_passes истинно при выключенном фильтре или закрытии выше SMA. input.bool даёт переключатель. При выключении not use_trend_filter истинно и вторая часть не нужна.
Средняя всё равно вычисляется и рисуется. Переключатель меняет проверку входа, а не видимость линии.
Недопустимое соотношение длин
Отдельные границы требуют лишь длины от 1 до 200. Можно ввести быструю 30, медленную 10, нарушив заданный порядок.
Проверочная функция отдельно оценивает связь. При неверном порядке линии остаются, но entry_signal не станет истинным. Программа не начнёт входить с отношением длин вне задания.
Проверьте напрямую valid_lengths в отладчике. При неверных сохранённых inputs она ложна весь запуск.
Сигнал не всегда даёт новую сделку
Зелёный треугольник обозначает условие входа. Приказ отправляется симулятору. Для исполнения могут требоваться следующая свеча, достаточный капитал и допустимое количество.
При pyramiding=1 существующая длинная позиция может запрещать ещё один вход. Не считайте число сигналов и сделок автоматически равным.
В начале отсутствует трендовая SMA длиной пятьдесят. При включённом фильтре нельзя признать цену выше неё, и условие входа не выполнено.
Проверьте поведение
Начните со значений по умолчанию и проверьте количество, расходы, исполнение в Properties. Ранее сохранённая настройка может переопределять исходник.
Найдите зелёный сигнал. В отладчике или по графику изучите EMA текущей и прошлой свечей. Проверьте пересечение, SMA-фильтр и порядок длин. Все три части должны быть истинными.
Затем найдите связанный вход в Trades. Проверьте свечу и цену исполнения. У выхода определите причину: пересечение, стоп или цель.
Попробуйте предсказуемые случаи
Для проверки меняйте то, чей результат можете предсказать:
| Изменение | Что проверить? |
|---|---|
| Равные быстрая и медленная длины | Проверка запрещает новые сигналы входа |
| Выключенный фильтр | Пересечение EMA не требует цены выше SMA |
| Увеличенные стоп и цель | Другое расстояние может изменить свечу выхода |
| Другое количество | Ценовой сигнал тот же, денежный размер может меняться |
Делайте одно изменение за запуск. Сначала записывайте ожидание, потом итог. При расхождении найдите первое отличающееся решение в отладчике.
Сравнение результата
Сравнивая фильтр включённым и выключенным, сохраняйте данные, количество, расходы и длины. Смотрите Performance вместе с Trades. Мало сделок или открытая последняя позиция влияют и на итоги.
Для реальных исторических данных задайте период настройки и отдельной проверки ещё до просмотра результатов. Изменив параметры после просмотра проверочного периода, вы включаете и его в настройку.
В начале данных нужно достаточно свечей прогрева. Запуск вырезанного периода заново строит средние и позиции. Поэтому его начало может отличаться от того же участка внутри длинного ряда.
Попробуйте самостоятельно
Добавьте Use crossings exit. При включении сохраняйте выход по пересечению вниз, при выключении закрывают только стоп и цель. Пересечение вниз считайте в обоих случаях.
Решение
В параметры main():
use_crossing_exit: bool = input.bool(True, title="Use crossings exit"),Ветвь закрытия:
if use_crossing_exit and crosses_down:
strategy.close("Long")crosses_down = ta.crossunder(...) оставьте до условий. strategy.exit сохраняется в обоих положениях.
Без выхода по пересечению позиция может остаться после пересечения EMA вниз. Новый сигнал возможен, но существующая позиция помешает новой сделке.
Создайте собственный вариант
Скопируйте под новым именем и опишите одно новое правило. Укажите данные и свечу решения, ручную проверку и управляющий input.
Сначала вычислите правило отдельной переменной. Отобразите или проверьте её в отладчике, прежде чем делать условием входа или выхода. Это помогает искать ошибку расчёта до объяснения результата всей стратегии.
Код, сохранённые настройки и данные вместе описывают эксперимент. Сохранив их, можно повторить и проверить запуск.