Ниже математически правильная ТС с проверкой, что это так.
Логика ТС следующая: переворачивается вовнутрь, когда цена с запасом пересекает EMA-шку от цены.
// Пример математически правильной ТС с возможностью ее проверки в MT5-Тестере. // https://www.mql5.com/ru/forum/333746 input group "EA" input int inPeriod = 100; // Период EMA-шки input double inFilter = 0.0005; // Фильтр сделок input group "Symbol" input bool inReverse = false; // Переворот символа input double inKoef = 1; // На столько умножаем символ #include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/ru/code/22577 int OnInit() { return(!MQLInfoInteger(MQL_TESTER) || !VIRTUAL::SelectByHandle(VIRTUAL::Create())); // Создали виртуальное торговое окружение и вошли в него. } // Обычная EMA-шка. struct EMA { private: double Value; // Текущее значение EMA. public: const double Alpha; // Коэффициент EMA (обратен периоду). EMA( const int period ) : Alpha(1.0 / period), Value(0) { } // Возвращает значение EMA после добавления нового члена в нее. double Get( const double NewValue ) { if (this.Value) // Если не первый запуск, вычисляем EMA через классическую итерацию. this.Value += (NewValue - this.Value) * this.Alpha; else this.Value = NewValue; // Если первый запуск, берем пришедшее значение в качестве EMA. return(this.Value); } }; // Логарифмирование тика. const MqlTick LogTick( const MqlTick &Tick ) { MqlTick NewTick = Tick; // Логарифмируем обе цены. NewTick.bid = MathLog(NewTick.bid); NewTick.ask = MathLog(NewTick.ask); return(NewTick); } // Торговая система void EA() { static const double Filter = MathLog(1 + inFilter); // С таким запасом нужно будет пересечь EMA для переворота. static EMA Ema(inPeriod); // Инициализировали EMA с соответствующим периодом. MqlTick Tick; if (!SymbolInfoTick(_Symbol, Tick)) // Взяли текущий тик. return; const MqlTick LogTick = LogTick(Tick); // Логарифмировали его. const double Price = Ema.Get((LogTick.bid + LogTick.ask) / 2); // Применили EMA к средней цене. const int Type = OrderSelect(0, SELECT_BY_POS) ? OrderType() : -1; // Направление текущей открытой позиции. if (LogTick.bid > Price + Filter) // Если bid выше EMA-шки с запасом, переворачиваемся в SELL. { if (Type != OP_SELL) // Если SELL не открыта. { if (Type == OP_BUY) // Если BUY открыта, OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); // закроем ее. OrderSend(_Symbol, OP_SELL, 1, Tick.bid, 0, 0, 0); // Откроем SELL-позицию. } } else if ((LogTick.ask < Price - Filter) && (Type != OP_BUY)) // Если ask ниже EMA-шки с запасом, переворачиваемся в BUY. { if (Type == OP_SELL) // Если SELL открыта, OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); // закроем ее. OrderSend(_Symbol, OP_BUY, 1, Tick.ask, 0, 0, 0); // Откроем BUY-позицию. } } // Изменения символа, которые должны быть инвариантны для математически правильной ТС. void ChangeTick( MqlTick &Tick ) { if (inReverse) // Обращаем символ. { const double Tmp = Tick.bid; Tick.bid = 1 / Tick.ask; Tick.ask = 1 / Tmp; } // Домножаем на константу Tick.bid *= inKoef; Tick.ask *= inKoef; } void OnTick() { MqlTick Tick; if (_V(0, SymbolInfoTick(_Symbol, Tick)) && Tick.bid && Tick.ask) // _V(0, - берем тик из реального торгового окружения (Тестер). { ChangeTick(Tick); // Изменяем тик для проверки инвариант-условия математически правильной ТС. VIRTUAL::NewTick(Tick, EA); // Пробросили измененный тик в виртуальное окружение с выполнением ТС. } } input group "HTML" #define REPORT_TESTER // В тестере будут автоматически записываться отчеты #define REPORT_TESTER_INPUTS // В отчете одиночного прохода будут видны входные параметры советника - требует разрешения DLL. #define REPORT_BROWSER // Создание отчета с запуском браузера - требует разрешения DLL. #include <Report.mqh> // https://www.mql5.com/ru/code/18801 #include <fxsaber\OnTesterCustom\OnTesterCustom.mqh> // https://www.mql5.com/ru/code/27714 input group "OnTester" sinput double inMarkup = 20; // Комиссия на миллион (одна сторона) sinput double inRisk = 0.5; // Фиксированный риск double OnTester() { const ONTESTERCUSTOM OnTesterCustom(_Symbol); // Считали историю торговли. return(OnTesterCustom.TesterStatistics(ONTESTERCUSTOM_GAIN, inRisk, inMarkup)); // Посчитали прибыльность при заданном риске и размере комиссии. }
Вся система - это лаконичная EA-функция. Остальной функционал - проверка правильности ТС. Специально привел полный листинг, чтобы было понятно, о чем речь. Код простой.
Запускается советник в MT5-Тестере, но совершает он сделки в виртуальном торговом окружении, т.к. MT5-Тестер не умеет работать с произвольными ценами.
Отчет торговли автоматически появляется в браузере в виде HTML (нужно разрешить использование DLL - WinAPI).
Проверка.
Итак, запускаем на EURUSD по реальным тикам с такими настройками.
Исходный символ не меняется (не домножения и/или переворота).
Получаем в браузере торговый отчет с таким графиком баланса.
И смотрим значение прибыльности ТС.
Собственно, именно это значение и должно быть инвариантом для всех изменений исходного символа. Т.е. как бы я его не менял (согласно ранее озвученным правилам), это значение должно оставаться постоянным.
Замена символа.
Попробуем изменить исходный символ.
Выделенное в настройках показыет, что символ теперь равен 7/EURUSD. Т.е. перевернули EURUSD и потом домножили на семь. После запуска Вы увидите, что график торговли и значение OnTester остались неизменны.
На всякий случай сравним каждый вход/выход до изменения и после. Ниже на скрине оба HTML-отчета.
Видим, что входы идентичны (с точностью до миллисекунды).
Зачем это нужно?
По коду видно, что в торговле нигде не используются размеры пунктов, лотов, маржинальных требований и т.д. Имеем просто голый ненормализованный ценовой ряд и торгуем на нем, соблюдая инвариантность к некоторым изменениям этого ряда. Удовлетворение логики ТС таким простым правилам позволяет с помощью нее легко делать масштабные исследования различных синтетических символов. Избавляет от математической ущербности в построении торговых сигналов. А значит уменьшает вероятность самообмана
Чтобы проверить любую ТС, замените просто EA-функцию на другую.