Алгоритмический трейдинг: Создание, тестирование и запуск роботов на рынке Форекс

Размер шрифта:   13
Алгоритмический трейдинг: Создание, тестирование и запуск роботов на рынке Форекс

Часть 1: Основы автоматической торговли.

Глава 1: Введение в алгоритмический трейдинг на Форексе. Эра машин

Представьте себе идеального трейдера. Он никогда не спит, не ест, не отвлекается на новости или эмоции. Он молниеносно анализирует терабайты данных, неукоснительно соблюдает риск-менеджмент и совершает сделки в точно рассчитанный момент, 24 часа в сутки, 7 дней в неделю. Этот трейдер – не сверхчеловек. Это торговый робот, или советник (Expert Advisor), воплощающий в себе суть алгоритмической торговли.

Алгоритмический трейдинг (алготрейдинг) – это использование компьютерных программ, которые следуют четко заданному набору правил (алгоритму) для автоматического открытия, управления и закрытия торговых позиций на финансовых рынках. На Форексе, где ликвидность огромна, а движения часто вызваны математическими закономерностями, алготрейдинг нашел свою идеальную среду.

Переход от субъективного человеческого решения к объективному машинному исполнению – это революция. Но как у любой революции, у нее есть две стороны: мощные преимущества и скрытые опасности.

Плюсы: Почему мир переходит на алгоритмы

1. Скорость и точность исполнения. Человеческая реакция измеряется долями секунды, машинная – микро- и наносекундами. Робот может отследить сигнал на десятке пар одновременно, мгновенно рассчитать объем позиции согласно риск-менеджменту и выставить ордер без задержек. Это критично для стратегий скальпинга и торговли на новостях, где цена может пройти нужное расстояние быстрее, чем вы моргнете.

2. Железная дисциплина. Главный враг трейдера – эмоции: жадность, заставляющая держать убыточную сделку, и страх, заставляющий рано закрыть прибыльную. Робот лишен психологии. Он будет снова и again открывать сделки по системе, даже если предыдущие 5 раз закрылись в убыток. Он строго соблюдает правила мани-менеджмента, никогда не «доливается» в убыток на эмоциях.

3. Возможность глубокого тестирования (Backtesting). Это самый мощный аргумент. Прежде чем рисковать реальными деньгами, вы можете «прогнать» стратегию робота на многолетних исторических данных. Как система вела себя в кризис 2008 года? Как на выборах в США? Ответы даст backtesting. Он позволяет количественно оценить ключевые метрики: общую прибыль, максимальную просадку, риск-ривард, процент прибыльных сделок. Вы можете оптимизировать параметры, подбирая наилучшие для конкретного инструмента и периода.

4. Масштабируемость и многозадачность. Один алгоритм может торговать на сотнях символов, одновременно отслеживая корреляции и глобальные рыночные условия. Человек физически не способен на такой охват.

5. Устранение рутины. Алгоритм берет на себя монотонную работу: постоянный мониторинг графиков, расчеты, выставление ордеров. Вы же сосредотачиваетесь на более важном – анализе эффективности, поиске новых идей и управлении капиталом.

Минусы: Подводные камни автоматизации

1. Риск переоптимизации (Кривая подгонка). Это бич алготрейдинга. Используя возможности тестера, можно так «настроить» параметры робота (периоды индикаторов, уровни стоп-лосса) под конкретный исторический период, что он покажет на графике прибыли невероятную красоту. Но такая стратегия «подогнана» под шум прошлых данных и абсолютно беспомощна в реальных, будущих условиях. Она работает идеально только на истории. Это как ключ, идеально отточенный под один замок, который больше не встретится.

2. Технические сбои и риски. Ваш идеальный трейдер полностью зависит от технологий. Разрыв интернет-соединения, отказ сервера брокера (VPS), сбой в подаче электричества, ошибка в коде при нестандартных рыночных условиях (например, «шпилька» на графике) – все это может привести к незакрытой позиции, убыткам или пропущенной прибыли. Ответственность за техническую инфраструктуру (резервный канал связи, надежный VPS) лежит на вас.

3. Отсутствие гибкости и интуиции. Робот слепо следует алгоритму. Он не понимает контекста. Он не отличит панику на рынке из-за геополитического кризиса от обычной технической коррекции. Он не прочитает между строк выступление главы ЦБ и не учтет «настроение рынка». В периоды фундаментальных потрясений, когда логика рушится, жесткая алгоритмическая система может нанести убытки, тогда как живой трейдер смог бы вовремя выйти из рынка.

4. Дрейф рынка (Изменение волатильности и закономерностей). Рынки эволюционируют. Закономерности, работавшие в условиях низкой волатильности и QE (количественного смягчения), могут перестать работать при росте ставок и высокой волатильности. Робот, настроенный на старые условия, будет продолжать торговать, пока не «сольет» счет. Алгоритм требует периодического мониторинга и адаптации.

5. Ложное чувство безопасности и «перекладывание ответственности». Начинающие алготрейдеры часто впадают в заблуждение: «Робот все сделает за меня, можно не следить». Это прямая дорога к потере депозита. Автоматизация не отменяет необходимости понимать логику стратегии, следить за ее работой и управлять рисками. Робот – это ваш инструмент, а не черный ящик, приносящий деньги.

Вывод главы:

Алгоритмический трейдинг – это не магия, а передовая методология. Он смещает фокус трейдера с эмоционального исполнения на интеллектуальное создание, тестирование и управление торговыми системами. Он дает беспрецедентное преимущество в виде дисциплины и скорости, но взамен требует глубокого понимания, технической грамотности и постоянной бдительности.

Робот – идеальный солдат, но генералом стратегии должны оставаться вы. Осознание как силы, так и ограничений автоматизации – это первый и самый важный шаг на пути к созданию устойчивой алгоритмической торговли. В следующих главах мы разберемся, как создаются эти «солдаты» и как превратить сырую идею в отлаженную торговую машину.

Глава 2: Техническая база: Инструменты алготрейдера

Если торговый робот – это «идеальный солдат», то платформа для его создания и развертывания – это целый арсенал, полигон и командный пункт. Выбор технологического стека определяет, насколько мощным, гибким и надежным будет ваш автоматический трейдинг. Давайте разберемся с фундаментом.

Торговые платформы: Где живут роботы

Это программное обеспечение, которое обеспечивает доступ к рынку, отображение котировок, анализ и исполнение ордеров. Для алготрейдинга ключевая функция – возможность создавать и запускать автоматические стратегии.

1. MetaTrader 4 / MetaTrader 5: Индустриальный стандарт

Подавляющее большинство розничных трейдеров начинают и часто остаются в экосистеме MetaTrader от компании MetaQuotes.

· MetaTrader 4 (MT4): Легенда рынка. Создан в 2005 году, ориентирован прежде всего на Форекс и CFD. Его главные преимущества – невероятная распространенность, стабильность, огромное сообщество и тонны готовых индикаторов, скриптов и советников. Однако его архитектура начинает устаревать: нет мультитрединга, ограниченные возможности тестирования (только на одном ядре), устаревший язык MQL4.

· MetaTrader 5 (MT5): Эволюционное, но не полное, замещение MT4. Выпущен в 2010 году. Это более мощная и современная платформа:

· Больше рынков: Форекс, фьючерсы, акции, криптовалюты.

· Улучшенный тестер стратегий: Многопоточное тестирование и оптимизация, использование облачных ресурсов брокера.

· Больше типов ордеров: Неттинговая система учета позиций (как на биржах), помимо хеджирования (как в MT4).

· Встроенный экономический календарь и чат.

· Современный язык MQL5.

Почему начинают с MT4/5? Готовая экосистема, минимальные требования к знаниям программирования для запуска готовых роботов, поддержка практически у любого форекс-брокера.

2. Python + Библиотеки: Царство гибкости и мощи

Если MetaTrader – это удобный заводской автомобиль, то Python – это мастерская, где вы можете собрать свой гоночный болид с нуля.

· Python: Универсальный язык программирования, ставший де-факто стандартом для анализа данных, машинного обучения и, как следствие, для профессионального алготрейдинга.

· Библиотеки:

· backtrader, zipline – фреймворки для бэктестинга стратегий.

· pandas, numpy – для молниеносной обработки и анализа данных (котировок).

· scikit-learn, TensorFlow, PyTorch – для внедрения машинного обучения и нейросетей.

· ccxt – единый API для подключения к сотням криптобирж и некоторым форекс-брокерам.

· MetaTrader5 (пакет для Python) – для подключения к терминалу MT5 и управления им прямо из кода на Python.

Преимущества Python:

· Неограниченная гибкость: Вы можете реализовать любую, самую сложную логику, интегрировать внешние данные (например, сеты новостей через API), использовать передовые методы ИИ.

· Лучшие возможности тестирования: Глубокий анализ результатов, кастомные метрики, Walk-Forward анализ.

· Контроль над всем: От подключения к брокеру до управления рисками.

Недостатки:

· Высокий порог входа: нужно уверенно знать Python.

· Нужно самостоятельно решать вопросы надежности исполнения, логирования, развертывания на сервере (VPS).

· Прямое подключение к брокерам может быть сложнее, чем через MT.

Советник (Expert Advisor, EA): Сердце системы

Советник (Expert Advisor, EA) – это и есть торговый робот в экосистеме MetaTrader. Это программа, написанная на языке MQL4/MQL5, которая работает внутри терминала MT4/MT5.

Как это работает?

1. Вы устанавливаете файл советника (*.ex4 для MT4, *.ex5 для MT5) в терминал.

2. Прикрепляете его к графику конкретной валютной пары (например, EURUSD).

3. Советник начинает свою работу в реальном времени. Он:

· Слушает поток котировок.

· Анализирует данные на основе заложенного алгоритма (проверяет условия индикаторов, цену и т.д.).

· Принимает решение: если условия для входа/выхода выполнены, он автоматически отправляет торговый приказ (ордер) на сервер брокера через терминал.

4. Он работает непрерывно, пока вы не отключите его или не закроете терминал.

Важно: Советник работает на вашем компьютере или VPS. Он зависит от вашего подключения к интернету и работы терминала.

Языки MQL4 и MQL5: На чем говорят советники

Чтобы создать своего робота для MetaTrader, нужно выучить его родной язык.

MQL4: Язык для MT4

· Проще для изучения. Меньше встроенных функций, более прямой синтаксис. Много учебных материалов на русском.

· Ориентирован на процедурное программирование (хотя ОПП тоже возможно).

· Ограниченная скорость тестирования.

· Устаревающий, но еще живой стандарт. Огромное наследие в виде тысяч бесплатных и платных советников.

MQL5: Современный язык для MT5

· Мощнее и современнее. Полноценная поддержка объектно-ориентированного программирования (ООП), что позволяет создавать более сложные, модульные и поддерживаемые советники.

· Гораздо больше встроенных функций для работы с графикой, файлами, сетевыми запросами (WebRequest), что позволяет, например, отправлять уведомления в Telegram.

· Прямой доступ к историческим данным и инструментам технического анализа без необходимости вручную рассчитывать индикаторы.

· Высокая скорость выполнения и тестирования благодаря компиляции в native-код и многопоточности.

Простой пример для иллюстрации различий:

Задача: Открыть рыночный ордер на покупку 0.1 лота по текущей цене.

На MQL4 (упрощенно):

```cpp

int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "My EA Comment", 12345, 0, clrGreen);

```

На MQL5 (используя торговые классы):

```cpp

CTrade trade;

trade.Buy(0.1, _Symbol, 0, 0, 0, "My EA Comment");

```

Код MQL5 часто более структурированный и читаемый для сложных проектов.

-–

Вывод главы:

Выбор технической базы – стратегическое решение.

· Новичкам, которые хотят быстро начать и тестировать классические стратегии, стоит выбрать MetaTrader 5 (или MT4, если брокер не поддерживает MT5) и изучать MQL5 (он перспективнее).

· Профессионалам и энтузиастам данных, стремящимся к максимальной гибкости, сложным стратегиям с ИИ и полному контролю, – прямая дорога к Python и соответствующим библиотекам.

· Советник (EA) – это исполнительный механизм в терминале MetaTrader, преобразующий ваш алгоритм в конкретные торговые приказы.

Помните: ни одна платформа не отменяет необходимости понимать логику торговли. Дальше мы погрузимся в этапы создания и, что критически важно, проверки жизнеспособности вашего будущего робота.

Глава 3: Жизненный цикл торгового робота: От идеи до машины

Создание прибыльного торгового робота – это не спринт, а марафон с четкими этапами, каждый из которых критически важен. Пропуск или халтурное выполнение любого шага неминуемо ведет к потере капитала. Представьте этот процесс как создание и запуск космического спутника: сначала идея и расчеты (теория), затем постройка и испытания в вакуумной камере (тестирование), и только потом – запуск на орбиту с постоянным контролем.

Давайте пройдем весь путь.

Этап 1: ИДЕЯ – Зарождение гипотезы

Все начинается не с кода, а с рыночной гипотезы. Это четкое, проверяемое утверждение о поведении цены.

· Что это такое? "Когда 20-периодная SMA пересекает 50-периодную SMA снизу вверх на дневном графике, а RSI > 50, то цена с вероятностью выше 55% продолжит рост в ближайшие 5 дней".

· Источники идей:

· Классические книги по техническому анализу (Элдер, Мерфи, Нисон).

· Академические исследования (статьи о сезонности, корреляциях).

· Наблюдение за графиками и выявление повторяющихся паттернов.

· Адаптация известных стратегий (например, "Черепахи") под Форекс.

· Критерий хорошей идеи: Она должна быть конкретной, формализуемой и статистически проверяемой. Идея "покупать, когда цена низкая" – плохая. Идея "покупать, когда цена опускается на 1.5 стандартных отклонения ниже 100-дневной скользящей средней" – хорошая.

Этап 2: РАЗРАБОТКА – Воплощение в код

Здесь гипотеза превращается в алгоритм. Это самый технический этап.

1. Формализация правил: Распишите идею до мельчайших деталей, как инструкцию для самого примитивного робота.

· Условие ВХОДА (лонг): (SMA(20) > SMA(50)) && (RSI(14) > 50) && (Цена закрытия > SMA(20)).

· Условие ВЫХОДА (тейк-профит/стоп-лосс): Фиксированный в пунктах? Динамический (например, на уровне ATR)? По обратному сигналу?

· Правила управления капиталом: Риск на сделку (например, 1% от депозита). Фиксированный лот или переменный?

· Фильтры: Время торговли (исключить азиатскую сессию? выходные?), макрособытия (выход Non-Farm Payrolls).

2. Написание кода: Используя выбранную платформу (MQL5/Python), вы программируете все правила. На этом этапе важна чистота кода и логирование (запись всех действий и решений робота в файл для последующего разбора).

Этап 3: ТЕСТИРОВАНИЕ – Суровая проверка реальностью

Это самый важный и многоступенчатый этап, цель которого – не подтвердить гениальность идеи, а обнаружить ее слабые места.

1. Бэктестинг (Backtest) – Испытание историей.

· Что это? Прогонка алгоритма на исторических данных за несколько лет. Платформа эмулирует, как робот торговал бы в прошлом.

· Цель: Получить первичную статистику и отсеять заведомо убыточные идеи. 80% идей погибают здесь.

· Ключевые метрики:

· Общая прибыль и кривая баланса. Ровный рост предпочтительнее резких взлетов и падений.

· Максимальная просадка (Max Drawdown): Максимальный пиковый убыток от предыдущего пика капитала. Это ваш главный стресс-тест. Если просадка 40%, а вы к ней психологически не готовы – система вам не подходит.

· Profit Factor (Прибыльность): (Сумма прибылей) / (Сумма убытков). Значение > 1.2 – минимально приемлемое, > 1.5 – хорошее.

· Количество сделок: Для статистической значимости нужно несколько сотен сделок (не 10-20).

2. Оптимизация – Тонкая настройка (ОПАСНАЯ ЗОНА).

· Что это? Подбор наилучших параметров (периоды индикаторов, уровни стоп-лосса) на исторических данных.

· ЛОВУШКА ПЕРЕОПТИМИЗАЦИИ (Кривая подгонка): Когда вы создаете идеальную систему для прошлого, которая бесполезна в будущем. Признаки: десятки параметров, чрезмерно высокая прибыль в бэктесте (>50% в месяц), резкие пики на кривой баланса.

· Как избежать?

· Принцип «Бритвы Оккама»: Чем проще система и меньше параметров, тем лучше.

· Тест на "незнакомых" данных: Оптимизируйте на периоде 2015-2020 гг., а проверьте на 2021-2023 гг.

· Walk-Forward Optimization (WFO): «Скользящая» оптимизация. Параметры подбираются на одном историческом окне, а тестируются на последующем, затем окно сдвигается вперед. Это золотой стандарт проверки устойчивости.

3. Форвард-тестирование (Forward Test / Paper Trading) – Репетиция в прямом эфире.

· Что это? Запуск робота на ДЕМО-СЧЕТЕ в режиме реального времени, но на виртуальные деньги. Он торгует по текущим, а не историческим, котировкам.

· Цель: Убедиться, что робот работает в реальных рыночных условиях с учетом спредов, проскальзываний, задержек исполнения. Проверить корректность кода и логики на "живом" потоке данных.

· Правило: Тестируйте не менее 2-3 месяцев и наберите не менее 100 сделок. Только после этого можно думать о реальных деньгах.

Этап 4: ЗАПУСК – Переход в боевой режим

Самый волнительный и ответственный момент.

1. Выделение капитала: Используйте только рисковый капитал, потеря которого не повлияет на вашу жизнь.

2. Начало с минимального объема: Запустите робота с лотом в 5-10 раз меньше расчетного. Например, если по вашим правилам лот для депозита $10,000 = 0.1, начните с 0.01. Цель – проверить исполнение и психологическую устойчивость.

3. Техническая инфраструктура: Обязательно используйте VPS (Виртуальный частный сервер) с хорошим аптаймом, расположенный рядом с серверами брокера. Это гарантирует, что робот не отключится из-за вашего сбоя интернета или выключенного компьютера.

Этап 5: МОНИТОРИНГ И УПРАВЛЕНИЕ – Не усыпляйте бдительность

Запуск робота – не финал, а начало нового этапа. Вы – пилот, а робот – автопилот. Пилот должен контролировать приборы.

· Что мониторить ежедневно (10-15 минут):

1. Исполнение ордеров: Не было ли критических проскальзываний, реквот (отказов брокера)?

2. Соответствие стратегии: Сделки открываются по правилам? Проверьте логи.

3. Состояние счета: Глубина текущей просадки. Не приближается ли к максимальной расчетной?

4. Рыночный контекст: Не начался ли период экстремальной волатильности (войны, решения ЦБ), для которого робот не предназначен?

· Когда ВМЕШАТЬСЯ (важнейший навык):

· Технический сбой (потеря связи, ошибка в логах).

· "Черный лебедь" – форс-мажорное рыночное событие.

· Достигнут лимит убытков: Например, просадка превысила 80% от расчетной Max Drawdown. Нужно остановить робота и разбираться.

· Декларативное изменение: Вы решили модернизировать стратегию.

· Постоянное улучшение: Анализируйте убыточные сделки. Можно ли добавить фильтр? Периодически проводите ре-оптимизацию на новых данных (но снова через все этапы тестирования!).

-–

Вывод главы:

Жизненный цикл робота – это строгий протокол, призванный минимизировать роль удачи и максимизировать роль проверенного алгоритма. Самые дорогие ошибки совершаются из-за спешки: пропуск форвард-теста, запуск на большой объем из-за жадности, невнимательный мониторинг.

Уважайте этот процесс. Дисциплинированное следование ему – это то, что отделяет мечтателя от системного алготрейдера. В следующей части мы перейдем к самому интересному – галерее конкретных идей для ваших будущих торговых роботов.

Часть 2: Галерея стратегий: 10 роботов для Форекса

Робот 1: «Следователь за трендом» (Trend Follower) – Python версия для Backtesting

Вот полная реализация стратегии Trend Follower на Python с использованием библиотеки backtrader для бэктестинга и анализа:

```python

# trend_follower_backtest.py

import backtrader as bt

import backtrader.analyzers as btanalyzers

import pandas as pd

import yfinance as yf

from datetime import datetime, timedelta

import matplotlib.pyplot as plt

# =====================================================================

# 1. СОЗДАНИЕ СТРАТЕГИИ

# =====================================================================

class TrendFollowerStrategy(bt.Strategy):

"""

Стратегия следования за трендом

Правила:

1. Покупка: когда быстрая MA (50) пересекает медленную (200) снизу вверх И ADX > 25

2. Закрытие: при обратном пересечении MA или по стоп-лоссу/тейк-профиту

"""

params = (

('fast_ma', 50), # Период быстрой скользящей средней

('slow_ma', 200), # Период медленной скользящей средней

('adx_period', 14), # Период ADX

('adx_threshold', 25),# Пороговое значение ADX для силы тренда

('stop_loss', 0.02), # Стоп-лосс 2%

('take_profit', 0.04),# Тейк-профит 4%

('risk_per_trade', 0.01), # Риск на сделку 1% от капитала

('use_atr_filter', True), # Использовать фильтр ATR

('atr_period', 14), # Период ATR

('atr_multiplier', 2),# Множитель ATR для стоп-лосса

('printlog', False), # Вывод логов

)

def __init__(self):

"""Инициализация индикаторов"""

# Создаем индикаторы

self.fast_ma = bt.indicators.SimpleMovingAverage(

self.data.close, period=self.params.fast_ma

)

self.slow_ma = bt.indicators.SimpleMovingAverage(

self.data.close, period=self.params.slow_ma

)

# ADX индикатор (направленность тренда)

self.adx = bt.indicators.AverageDirectionalMovementIndex(

self.data, period=self.params.adx_period

)

# ATR для фильтра волатильности и расчета стоп-лосса

self.atr = bt.indicators.AverageTrueRange(

self.data, period=self.params.atr_period

)

# Переменные для отслеживания состояния

self.order = None

self.buyprice = None

self.buycomm = None

# Для логирования

self.trades = []

self.current_trade = {}

def log(self, txt, dt=None, doprint=False):

"""Метод для логирования"""

if self.params.printlog or doprint:

dt = dt or self.datas[0].datetime.date(0)

print(f'{dt.isoformat()} {txt}')

def notify_order(self, order):

"""Обработка изменения статуса ордера"""

if order.status in [order.Submitted, order.Accepted]:

# Ордер отправлен/принят – ничего не делаем

return

if order.status in [order.Completed]:

if order.isbuy():

self.log(

f'ПОКУПКА ИСПОЛНЕНА, Цена: {order.executed.price:.2f}, '

f'Стоимость: {order.executed.value:.2f}, '

f'Комиссия: {order.executed.comm:.2f}'

)

self.buyprice = order.executed.price

self.buycomm = order.executed.comm

# Устанавливаем стоп-лосс и тейк-профит

if self.params.use_atr_filter:

sl = self.buyprice – self.atr[0] * self.params.atr_multiplier

tp = self.buyprice + self.atr[0] * self.params.atr_multiplier * 2

else:

sl = self.buyprice * (1 – self.params.stop_loss)

tp = self.buyprice * (1 + self.params.take_profit)

self.sell(exectype=bt.Order.StopTrail, price=sl)

self.sell(exectype=bt.Order.Limit, price=tp)

# Сохраняем информацию о сделке

self.current_trade = {

'entry_date': self.datas[0].datetime.date(0),

'entry_price': order.executed.price,

'stop_loss': sl,

'take_profit': tp,

'size': order.executed.size

}

elif order.issell():

self.log(

f'ПРОДАЖА ИСПОЛНЕНА, Цена: {order.executed.price:.2f}, '

f'Стоимость: {order.executed.value:.2f}, '

f'Комиссия: {order.executed.comm:.2f}'

)

# Фиксируем завершение сделки

if self.current_trade:

self.current_trade['exit_date'] = self.datas[0].datetime.date(0)

self.current_trade['exit_price'] = order.executed.price

self.current_trade['pnl'] = (

(order.executed.price – self.current_trade['entry_price']) *

self.current_trade['size']

)

self.current_trade['pnl_percent'] = (

(order.executed.price / self.current_trade['entry_price'] – 1) * 100

)

self.trades.append(self.current_trade.copy())

self.current_trade = {}

elif order.status in [order.Canceled, order.Margin, order.Rejected]:

self.log(f'Ордер отменен/отклонен: {order.getstatusname()}')

self.order = None

def notify_trade(self, trade):

"""Обработка изменения статуса сделки"""

if not trade.isclosed:

return

self.log(f'СДЕЛКА ЗАКРЫТА, Прибыль: {trade.pnl:.2f}, Чистая прибыль: {trade.pnlcomm:.2f}')

def next(self):

"""Основная логика на каждом новом баре"""

# Пропускаем если ордер уже открыт

if self.order:

return

# Проверяем, есть ли открытая позиция

if not self.position:

# УСЛОВИЕ ДЛЯ ПОКУПКИ:

# 1. Быстрая MA пересекает медленную снизу вверх

# 2. ADX > порогового значения (сила тренда)

crossover_up = (self.fast_ma[-1] < self.slow_ma[-1] and

self.fast_ma[0] > self.slow_ma[0])

strong_trend = self.adx[0] > self.params.adx_threshold

if crossover_up and strong_trend:

# Рассчитываем размер позиции на основе риска

capital = self.broker.getvalue()

risk_amount = capital * self.params.risk_per_trade

if self.params.use_atr_filter:

stop_distance = self.atr[0] * self.params.atr_multiplier

else:

stop_distance = self.data.close[0] * self.params.stop_loss

if stop_distance > 0:

size = risk_amount / stop_distance

size = min(size, capital * 0.1 / self.data.close[0]) # Не более 10% капитала

self.log(f'СИГНАЛ НА ПОКУПКУ: цена={self.data.close[0]:.2f}, '

f'ADX={self.adx[0]:.2f}, размер={size:.2f}')

self.order = self.buy(size=size)

else:

# УСЛОВИЕ ДЛЯ ЗАКРЫТИЯ ПОЗИЦИИ:

# Быстрая MA пересекает медленную сверху вниз

crossover_down = (self.fast_ma[-1] > self.slow_ma[-1] and

self.fast_ma[0] < self.slow_ma[0])

if crossover_down:

self.log(f'СИГНАЛ НА ЗАКРЫТИЕ: цена={self.data.close[0]:.2f}')

self.order = self.close()

# =====================================================================

# 2. КЛАСС ДЛЯ ОПТИМИЗАЦИИ ПАРАМЕТРОВ

# =====================================================================

class TrendFollowerOptimizer(bt.Strategy):

"""Версия стратегии для оптимизации параметров"""

params = (

('fast_ma', 50),

('slow_ma', 200),

('adx_threshold', 25),

('risk_per_trade', 0.01),

)

def __init__(self):

self.fast_ma = bt.indicators.SMA(self.data.close, period=self.p.fast_ma)

self.slow_ma = bt.indicators.SMA(self.data.close, period=self.p.slow_ma)

self.adx = bt.indicators.ADX(self.data, period=14)

self.order = None

def next(self):

if self.order:

return

if not self.position:

if (self.fast_ma[-1] < self.slow_ma[-1] and

self.fast_ma[0] > self.slow_ma[0] and

self.adx[0] > self.p.adx_threshold):

size = self.broker.getvalue() * self.p.risk_per_trade / self.data.close[0]

self.order = self.buy(size=size)

else:

if self.fast_ma[-1] > self.slow_ma[-1] and self.fast_ma[0] < self.slow_ma[0]:

self.order = self.close()

# =====================================================================

# 3. ФУНКЦИИ ДЛЯ ТЕСТИРОВАНИЯ И АНАЛИЗА

# =====================================================================

def download_data(symbol='EURUSD=X', start_date='2020-01-01', end_date='2023-12-31'):

"""Загрузка данных с Yahoo Finance"""

print(f"Загрузка данных для {symbol}…")

data = yf.download(symbol, start=start_date, end=end_date, progress=False)

# Преобразуем индексы для backtrader

data.index = pd.to_datetime(data.index)

data = data[['Open', 'High', 'Low', 'Close', 'Volume']]

return data

def run_backtest(data, initial_cash=10000, commission=0.001, **strategy_params):

"""Запуск бэктеста"""

cerebro = bt.Cerebro()

# Добавляем данные

data_feed = bt.feeds.PandasData(dataname=data)

cerebro.adddata(data_feed)

# Добавляем стратегию

cerebro.addstrategy(TrendFollowerStrategy, **strategy_params)

# Настройки брокера

cerebro.broker.setcash(initial_cash)

cerebro.broker.setcommission(commission=commission)

# Добавляем анализаторы

cerebro.addanalyzer(btanalyzers.Returns, _name='returns')

cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='sharpe', riskfreerate=0.0)

cerebro.addanalyzer(btanalyzers.DrawDown, _name='drawdown')

cerebro.addanalyzer(btanalyzers.TradeAnalyzer, _name='trades')

cerebro.addanalyzer(btanalyzers.SQN, _name='sqn')

print(f'Начальный капитал: {initial_cash:.2f}')

print(f'Комиссия: {commission*100:.2f}%')

# Запуск

results = cerebro.run()

strat = results[0]

# Вывод результатов

print('\n' + '='*50)

print('РЕЗУЛЬТАТЫ БЭКТЕСТА')

print('='*50)

final_value = cerebro.broker.getvalue()

total_return = (final_value / initial_cash – 1) * 100

print(f'Конечный капитал: {final_value:.2f}')

print(f'Общая доходность: {total_return:.2f}%')

# Анализ результатов

if hasattr(strat, 'analyzers'):

returns = strat.analyzers.returns.get_analysis()

sharpe = strat.analyzers.sharpe.get_analysis()

drawdown = strat.analyzers.drawdown.get_analysis()

trades = strat.analyzers.trades.get_analysis()

sqn = strat.analyzers.sqn.get_analysis()

print(f'\nАНАЛИТИКА:')

print(f'Sharpe Ratio: {sharpe.get("sharperatio", 0):.3f}')

print(f'Максимальная просадка: {drawdown.max.drawdown:.2f}%')

print(f'Продолжительность просадки: {drawdown.max.len} дней')

if trades.total.total:

print(f'\nСТАТИСТИКА СДЕЛОК:')

print(f'Всего сделок: {trades.total.total}')

print(f'Прибыльных: {trades.won.total} ({trades.won.total/trades.total.total*100:.1f}%)')

print(f'Убыточных: {trades.lost.total} ({trades.lost.total/trades.total.total*100:.1f}%)')

print(f'Profit Factor: {trades.won.pnl.total/abs(trades.lost.pnl.total):.2f}')

print(f'Средняя прибыль: {trades.won.pnl.average:.2f}')

print(f'Средний убыток: {trades.lost.pnl.average:.2f}')

print(f'SQN: {sqn.sqn:.2f}')

# Возвращаем детали сделок для дальнейшего анализа

trades_details = getattr(strat, 'trades', [])

return cerebro, strat, trades_details

def optimize_parameters(data, parameter_ranges):

"""Оптимизация параметров стратегии"""

cerebro = bt.Cerebro(optreturn=False)

# Добавляем данные

data_feed = bt.feeds.PandasData(dataname=data)

cerebro.adddata(data_feed)

# Добавляем стратегию с параметрами для оптимизации

cerebro.optstrategy(

TrendFollowerOptimizer,

fast_ma=parameter_ranges.get('fast_ma', range(10, 101, 10)),

slow_ma=parameter_ranges.get('slow_ma', range(50, 301, 50)),

adx_threshold=parameter_ranges.get('adx_threshold', range(20, 41, 5)),

risk_per_trade=parameter_ranges.get('risk_per_trade', [0.005, 0.01, 0.02])

)

# Настройки

cerebro.broker.setcash(10000)

cerebro.broker.setcommission(commission=0.001)

# Оптимизация

print("Запуск оптимизации параметров…")

opt_results = cerebro.run(maxcpus=1)

# Анализ результатов оптимизации

results = []

for run in opt_results:

for strat in run:

# Собираем статистику для каждого набора параметров

value = cerebro.broker.getvalue()

returns = (value / 10000 – 1) * 100

results.append({

'fast_ma': strat.params.fast_ma,

'slow_ma': strat.params.slow_ma,

'adx_threshold': strat.params.adx_threshold,

'risk_per_trade': strat.params.risk_per_trade,

'final_value': value,

'returns': returns

})

# Создаем DataFrame с результатами

results_df = pd.DataFrame(results)

results_df = results_df.sort_values('final_value', ascending=False)

return results_df

def plot_results(cerebro, save_path='trend_follower_results.png'):

"""Визуализация результатов"""

# График 1: Кривая баланса

fig = cerebro.plot(style='candlestick', iplot=False)[0][0]

fig.set_size_inches(14, 8)

fig.suph2('Trend Follower Strategy – Equity Curve', fontsize=16)

# Сохраняем график

plt.tight_layout()

plt.savefig(save_path, dpi=100, bbox_inches='tight')

plt.show()

return fig

def generate_report(strategy_params, trades_details, filename='trend_follower_report.txt'):

"""Генерация детального отчета"""

with open(filename, 'w', encoding='utf-8') as f:

f.write("="*60 + "\n")

f.write("ОТЧЕТ ПО СТРАТЕГИИ TREND FOLLOWER\n")

f.write("="*60 + "\n\n")

f.write("ПАРАМЕТРЫ СТРАТЕГИИ:\n")

f.write("-"*40 + "\n")

for key, value in strategy_params.items():

f.write(f"{key}: {value}\n")

f.write("\n" + "="*60 + "\n\n")

if trades_details:

f.write("ДЕТАЛИ СДЕЛОК:\n")

f.write("-"*40 + "\n")

total_pnl = 0

winning_trades = 0

for i, trade in enumerate(trades_details, 1):

pnl = trade.get('pnl', 0)

pnl_percent = trade.get('pnl_percent', 0)

f.write(f"\nСделка #{i}:\n")

f.write(f" Дата входа: {trade.get('entry_date')}\n")

f.write(f" Цена входа: {trade.get('entry_price', 0):.4f}\n")

f.write(f" Дата выхода: {trade.get('exit_date')}\n")

f.write(f" Цена выхода: {trade.get('exit_price', 0):.4f}\n")

f.write(f" P&L: {pnl:.2f} ({pnl_percent:.2f}%)\n")

total_pnl += pnl

if pnl > 0:

winning_trades += 1

f.write("\n" + "="*60 + "\n")

f.write(f"ИТОГИ:\n")

f.write(f"Всего сделок: {len(trades_details)}\n")

f.write(f"Прибыльных сделок: {winning_trades} ({winning_trades/len(trades_details)*100:.1f}%)\n")

f.write(f"Общий P&L: {total_pnl:.2f}\n")

print(f"Отчет сохранен в файл: {filename}")

# =====================================================================

# 4. ОСНОВНОЙ СКРИПТ ДЛЯ ЗАПУСКА

# =====================================================================

def main():

"""Основная функция для запуска бэктеста"""

print("="*60)

print("TREND FOLLOWER STRATEGY BACKTEST")

print("="*60)

# 1. Загружаем данные

symbol = 'EURUSD=X' # EUR/USD

data = download_data(

symbol=symbol,

start_date='2020-01-01',

end_date='2023-12-31'

)

if data.empty:

print("Ошибка загрузки данных!")

Продолжить чтение