English
preview
Рыночные секреты Ларри Уильямса (Часть 15): Торговля разворотами по паттерну Hidden Smash Day с учетом рыночного контекста

Рыночные секреты Ларри Уильямса (Часть 15): Торговля разворотами по паттерну Hidden Smash Day с учетом рыночного контекста

MetaTrader 5Торговые системы |
47 0
Chacha Ian Maroa
Chacha Ian Maroa

Введение

В предыдущей статье этой серии был разработан пользовательский индикатор для выявления разворотных паттернов Hidden Smash Day, описанных Ларри Уильямсом. Индикатор находит бары Hidden Smash на графике и визуально отмечает их, позволяя распознавать паттерн объективно, без ручной интерпретации.

Однако обнаружение паттерна — это только первый шаг. Полноценный торговый подход требует дополнительных правил, определяющих, когда обнаруженный сетап следует торговать, а когда его нужно игнорировать. Эти правила могут учитывать направление сделок, преобладающий тренд или ограничения по дням недели. Без четко заданных условий систематически оценивать паттерн становится сложно.

В этой статье мы формализуем эти дополнительные условия в виде структурированной торговой системы. Материал предназначен для алгоритмических трейдеров, которые хотят автоматизировать сетапы Hidden Smash Day с явными, проверяемыми правилами.

Опираясь на ранее созданный индикатор, мы реализуем советник, который считывает сигналы Hidden Smash и применяет настраиваемые фильтры рыночного контекста перед открытием сделок. Эти фильтры включают проверку направления тренда по индикатору Supertrend, необязательные ограничения по торговым дням и дисциплинированное управление риском через правила стоп-лосса и расчета размера позиции.

В результате получается объективная основа для изучения и торговли сетапами Hidden Smash Day, учитывающая рыночные условия, а не только факт обнаружения паттерна.


Паттерн Hidden Smash Day и подтверждение сигнала

Перед реализацией торговой системы полезно кратко вернуться к структуре паттерна Hidden Smash Day, описанного Ларри Уильямсом, и уточнить, как формируется корректный торговый сигнал.

Бар Hidden Smash Day определяется по положению цены закрытия внутри собственного диапазона бара. Для бычьего сетапа Hidden Smash бар закрывается в нижней части своего диапазона, но при этом выше закрытия предыдущего бара. Такая конфигурация показывает, что хотя сессия завершилась выше предыдущей, закрытие произошло ближе к нижней границе дневного диапазона, что указывает на потенциальную слабость.

Для медвежьего сетапа Hidden Smash логика обратная. Бар закрывается в верхней части своего диапазона, но при этом ниже закрытия предыдущего бара. В этом случае рынок выглядит слабым относительно предыдущей сессии, однако закрытие рядом с верхней частью диапазона бара может указывать на внутреннюю силу.

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

Корректный сигнал на покупку формируется в такой последовательности:

  1. Бычий бар Hidden Smash обнаружен на индексе бара  2.
  2. Следующий бар закрывается выше максимума этого бара Hidden Smash.
  3. На открытии нового бара система разрешает вход в длинную позицию.

Buy Setup

Корректный сигнал на продажу зеркально повторяет эту структуру:

  1. Медвежий бар Hidden Smash обнаружен на индексе бара 2.
  2. Следующий бар закрывается ниже минимума этого бара Hidden Smash.
  3. На открытии нового бара система разрешает вход в короткую позицию.

Sell Setup

Чтобы упростить реализацию, советник не выявляет паттерн напрямую. Вместо этого он считывает сигналы Hidden Smash из пользовательского индикатора, разработанного в предыдущей статье серии. Этот индикатор отмечает обнаруженные паттерны в специальных буферах. Когда бар Hidden Smash находится на индексе 2, буфер индикатора содержит значение, отличное от EMPTY_VALUE, и советник использует его как надежную опорную точку для проверки подтверждения и формирования торговых сигналов.


Проектирование советника и правила исполнения сделок

Советник, разработанный в этой статье, напрямую опирается на индикатор Hidden Smash Day, представленный ранее в серии. Вместо внутреннего поиска паттерна система считывает сигналы из индикатора, а затем оценивает дополнительные правила перед открытием сделки. Такой дизайн четко разделяет обязанности обнаружения паттерна и управления сделками. Индикатор выявляет сетапы Hidden Smash, а советник занимается фильтрацией, контролем риска и исполнением ордеров.

Все торговые решения оцениваются только в начале каждого нового бара. Это гарантирует, что и бар Hidden Smash, и бар подтверждения полностью завершены до обработки сигнала. Ожидание завершенных баров позволяет системе избежать внутрибарных колебаний и обеспечивает стабильное поведение при историческом тестировании.

Обработка сигналов

Советник считывает значения буферов индикатора, чтобы определить, был ли обнаружен паттерн Hidden Smash. Индикатор отмечает найденные паттерны с помощью стрелочных буферов. Если буфер содержит значение, отличное от EMPTY_VALUE, на индексе бара 2, система интерпретирует это как обнаруженный сетап Hidden Smash.

После формирования сетапа на индексе 2 советник оценивает, был ли подтвержден корректный сигнал. Бычий сигнал допускается к исполнению, если бар подтверждения закрылся выше максимума бара Hidden Smash. Медвежий сигнал следует противоположной логике и становится действительным, когда бар подтверждения закрывается ниже минимума бара Hidden Smash. Только после этого шага подтверждения система разрешает открыть сделку.

Контроль направления сделок

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

Контекстный трендовый фильтр

Через индикатор Supertrend доступен необязательный трендовый фильтр. Когда этот фильтр включен, длинные сделки разрешены только тогда, когда рынок находится в бычьем состоянии Supertrend. Аналогично, короткие сделки разрешены только тогда, когда индикатор Supertrend показывает медвежье состояние тренда. Если фильтр отключен, сделки оцениваются без ограничений по тренду.

Фильтр торговых дней

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

Модели стоп-лосса

Реализованы две модели стоп-лосса. Первая модель использует структуру бара Hidden Smash. В этом режиме стоп-лосс для длинной сделки размещается на минимуме бара Hidden Smash, а стоп-лосс для короткой сделки — на максимуме этого бара.

Вторая модель использует волатильность, измеряемую индикатором Average True Range. При выборе этой опции расстояние до стоп-лосса рассчитывается умножением значения ATR на настраиваемый множитель. Такой подход позволяет защитному стопу адаптироваться к изменяющейся рыночной волатильности.

Расчет тейк-профита

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

Размер позиции

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

Автоматический метод рассчитывает потенциальный убыток для одного лота с помощью функции OrderCalcProfit . Затем размер позиции корректируется в соответствии с заданным процентом риска. Ограничения брокера, такие как минимальный объем, максимальный объем и шаг объема, учитываются, чтобы рассчитанный размер позиции оставался допустимым.

Управление позициями

Для поддержания последовательного управления сделками советник допускает только одну активную позицию за раз. Перед открытием новой сделки система проверяет, существует ли уже активная позиция на покупку или продажу с тем же magic number. Если позиция есть, новый сигнал игнорируется.

Вместе эти компоненты формируют структурированную торговую систему, объединяющую обнаружение паттерна, фильтры рыночного контекста и дисциплинированное управление риском. Получившийся советник предоставляет гибкую среду для изучения поведения сетапов Hidden Smash Day при торговле в разных рыночных условиях.


Поток исполнения стратегии Hidden Smash Day с учетом рыночного контекста

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

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

если открылся новый бар

 Обновить буферы индикатора Hidden Smash
 Обновить значения индикатора Supertrend
 Обновить значения ATR

 если нет открытой позиции

  Проверить наличие сетапа Hidden Smash Buy на индексе два

  если сетап существует

   Проверить, разрешены ли сделки на покупку настройкой направления торговли

   если фильтр Supertrend включен

    Убедиться, что рынок сейчас находится в бычьем состоянии тренда

   если активны ограничения по торговым дням

    Убедиться, что в текущий день торговля разрешена

   если выполнены все условия

    Рассчитать уровень стоп-лосса
    Рассчитать уровень тейк-профита
    Определить размер позиции
    Открыть ордер на покупку

  Проверить наличие сетапа Hidden Smash Sell на индексе два

  если сетап существует

   Проверить, разрешены ли сделки на продажу настройкой направления торговли

   если фильтр Supertrend включен

    Убедиться, что рынок сейчас находится в медвежьем состоянии тренда

   если активны ограничения по торговым дням

    Убедиться, что в текущий день торговля разрешена

   если выполнены все условия

    Рассчитать уровень стоп-лосса
    Рассчитать уровень тейк-профита
    Определить размер позиции
    Открыть ордер на продажу

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


Подготовка среды разработки и основы проекта

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

Первый компонент — это индикатор Supertrend. Этот индикатор является необязательным трендовым фильтром, который позволяет советнику открывать сделки только тогда, когда направление рынка соответствует выбранному трендовому состоянию. Построение этого индикатора было объяснено в предыдущей статье из отдельной серии. Читатели, желающие понять, как был разработан сам индикатор, могут обратиться к этой статье за полным объяснением его внутренней логики.

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

Первый способ — скачать предоставленный исходный файл и поместить его в каталог Indicators основной папки MQL5. Это можно сделать следующим образом.

  1. Откройте платформу MetaTrader 5.
  2. В главном меню выберите пункт, открывающий папку данных.
  3. Перейдите в каталог MQL5.
  4. Откройте папку Indicators.
  5. Скопируйте файл supertrend.mq5 в этот каталог.
  6. Вернитесь в среду разработки MetaEditor и скомпилируйте индикатор.

После компиляции индикатор становится доступным для других программ, включая советники.

Второй способ — создать исходный файл индикатора вручную. При таком подходе новый файл индикатора с именем supertrend.mq5 создается внутри каталога Indicators. Затем исходный код из прикрепленного файла можно скопировать и вставить в новый файл перед компиляцией в MetaEditor.

Тот же процесс подготовки применяется к индикатору Hidden Smash Day разработанному в предыдущей статье этой серии. Исходный файл, lwHiddenSmashDayIndicator.mq5, также прикреплен к этой статье. Его следует поместить в каталог Indicators и скомпилировать таким же образом.

В этой статье предполагается, что оба индикатора находятся в каталоге Indicators внутри корневой папки MQL5. После компиляции советник сможет обращаться к ним напрямую через хэндлы индикаторов.

Перед дальнейшей работой необходимо выполнить несколько предварительных условий, чтобы эффективно следовать процессу реализации. Во-первых, требуется знакомство с синтаксисом и базовыми конструкциями языка программирования MQL5. Такие понятия, как переменные, функции, условные операторы, циклы, перечисления и стандартные библиотеки, уже должны быть понятны. Если эти темы незнакомы, официальная документация MQL5 служит надежной отправной точкой для изучения языка.

Во-вторых, важен предыдущий опыт работы с торговой платформой MetaTrader 5. Нужно уверенно ориентироваться в интерфейсе платформы, включая открытие графиков, подключение программ и запуск тестера стратегий. Эти операции являются частью обычного рабочего процесса при разработке алгоритмических торговых систем.

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

По мере продвижения реализации настоятельно рекомендуется писать код параллельно с учебным материалом. Программные концепции становятся понятнее, когда применяются непосредственно на практике, а не только наблюдаются со стороны. Для поддержки этого процесса завершенный исходный код советника прикреплен к статье как lwHiddenSmashDayContextExpert.mq5. Открытый в отдельной вкладке редактора файл может служить полезным ориентиром при пошаговой сборке системы.

Когда среда подготовлена, можно начинать разработку. Откройте MetaEditor и создайте новый исходный файл советника. Имя файла можно выбрать свободно, хотя версия, разрабатываемая в этой статье, называется lwHiddenSmashDayContextExpert.mq5. После создания пустого файла следующий код закладывает основу для остальной части системы.

//+------------------------------------------------------------------+
//|                                lwHiddenSmashDayContextExpert.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"
#resource "\\Indicators\\lwHiddenSmashDayIndicator.ex5"
#resource "\\Indicators\\supertrend.ex5"

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+
enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_SMASH_TRADE_MODE
{
   SMASH_TRADE_BUY_ONLY,
   SMASH_TRADE_SELL_ONLY,
   SMASH_TRADE_BOTH
};

enum ENUM_STOP_LOSS_MODE
{
   SL_ATR_BASED,            
   SL_SMASH_BAR_STRUCTURE
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber          = 254700680002;                 
input ENUM_TIMEFRAMES timeframe  = PERIOD_CURRENT;

input group "Supertrend configuration parameters"
input bool useSupertrendFilter            = false;
input ENUM_TIMEFRAMES supertrendTimeframe = PERIOD_CURRENT;
input int32_t supertrendAtrPeriod         = 10;
input double  supertrendAtrMultiplier     = 1.5;

input group "TDW filters"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE smashStopLossMode = SL_SMASH_BAR_STRUCTURE;
input int32_t atrPeriod                     = 14;
input double  atrMultiplier                 = 2.0;
input ENUM_SMASH_TRADE_MODE smashTradeMode  = SMASH_TRADE_BOTH;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;
input double riskRewardRatio                = 3.0;

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- To help track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

//--- To help track new bar open
datetime lastBarOpenTime;

//--- Hidden Smash bar indicator handle and values
int hiddenSmashDayIndicatorHandle;
double buySmashArrowBuffer[];
double sellSmashArrowBuffer[];

//--- Supertrend indicator handle and values 
int    supertrendIndicatorHandle;
double upperBandValues[];
double lowerBandValues[];

//--- ATR Values
int atrHandle;
double atrValues [];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Initialize global variables
   lastBarOpenTime = 0;
   
   //--- Initialize the hidden smash day indicator
   hiddenSmashDayIndicatorHandle    = iCustom(_Symbol, timeframe, "::Indicators\\lwHiddenSmashDayIndicator.ex5");
   if(hiddenSmashDayIndicatorHandle == INVALID_HANDLE){
      Print("Error while initializing The Hidden Smash Day Indicator: ", GetLastError());
      return(INIT_FAILED);
   }
   
   // Initialize the Supertrend Indicator
   supertrendIndicatorHandle = iCustom(_Symbol, supertrendTimeframe, "::Indicators\\supertrend.ex5", supertrendAtrPeriod, supertrendAtrMultiplier);
   if(supertrendIndicatorHandle == INVALID_HANDLE){
      Print("Error while initializing the Supertrend indicator: ", GetLastError());
      return(INIT_FAILED);
   }
   
   //--- Initialize the ATR indicator
   atrHandle = iATR(_Symbol, timeframe, atrPeriod);
   if(atrHandle == INVALID_HANDLE){
      Print("Error while initializing the ATR indicator ", GetLastError());
      return(INIT_FAILED);
   }
   
   //--- Set arrays as series
   ArraySetAsSeries(buySmashArrowBuffer, true);
   ArraySetAsSeries(sellSmashArrowBuffer, true);
   ArraySetAsSeries(upperBandValues, true);
   ArraySetAsSeries(lowerBandValues, true);
   ArraySetAsSeries(atrValues, true);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Release Supertrend
   if(hiddenSmashDayIndicatorHandle != INVALID_HANDLE){
      IndicatorRelease(hiddenSmashDayIndicatorHandle);
   }

   //--- Release Supertrend
   if(supertrendIndicatorHandle != INVALID_HANDLE){
      IndicatorRelease(supertrendIndicatorHandle);
   }
   
   //--- Release ATR
   if(atrHandle != INVALID_HANDLE){
      if(IndicatorRelease(atrHandle)){
         Print("The computer memory tracking ATR has been freed.");
      }
   }

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();   
}
 
//+------------------------------------------------------------------+

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

Объявления свойств в начале файла определяют метаданные программы.

//+------------------------------------------------------------------+
//|                                lwHiddenSmashDayContextExpert.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"
#resource "\\Indicators\\lwHiddenSmashDayIndicator.ex5"
#resource "\\Indicators\\supertrend.ex5"

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

После определения свойств объявляются несколько пользовательских перечислений.

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+
enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_SMASH_TRADE_MODE
{
   SMASH_TRADE_BUY_ONLY,
   SMASH_TRADE_SELL_ONLY,
   SMASH_TRADE_BOTH
};

enum ENUM_STOP_LOSS_MODE
{
   SL_ATR_BASED,            
   SL_SMASH_BAR_STRUCTURE
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

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

Следующий раздел подключает стандартную торговую библиотеку.

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

Эта библиотека предоставляет класс CTrade, который упрощает отправку торговых запросов на торговый сервер. Вместо ручного построения низкоуровневых структур ордеров объект CTrade позволяет открывать ордера простыми вызовами методов.

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

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber          = 254700680002;                 
input ENUM_TIMEFRAMES timeframe  = PERIOD_CURRENT;

input group "Supertrend configuration parameters"
input bool useSupertrendFilter            = false;
input ENUM_TIMEFRAMES supertrendTimeframe = PERIOD_CURRENT;
input int32_t supertrendAtrPeriod         = 10;
input double  supertrendAtrMultiplier     = 1.5;

input group "TDW filters"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE smashStopLossMode = SL_SMASH_BAR_STRUCTURE;
input int32_t atrPeriod                     = 14;
input double  atrMultiplier                 = 2.0;
input ENUM_SMASH_TRADE_MODE smashTradeMode  = SMASH_TRADE_BOTH;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;
input double riskRewardRatio                = 3.0;

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

После входных параметров объявляются глобальные переменные.

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- To hep track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

//--- To help track new bar open
datetime lastBarOpenTime;

//--- Hidden Smash bar indicator handle and values
int hiddenSmashDayIndicatorHandle;
double buySmashArrowBuffer[];
double sellSmashArrowBuffer[];

//--- Supertrend indicator handle and values 
int    supertrendIndicatorHandle;
double upperBandValues[];
double lowerBandValues[];

//--- ATR Values
int atrHandle;
double atrValues [];

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

Далее появляется функция инициализации .

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Initialize global variables
   lastBarOpenTime = 0;
   
   //--- Initialize the hidden smash day indicator
   hiddenSmashDayIndicatorHandle    = iCustom(_Symbol, timeframe, "::Indicators\\lwHiddenSmashDayIndicator.ex5");
   if(hiddenSmashDayIndicatorHandle == INVALID_HANDLE){
      Print("Error while initializing The Hidden Smash Day Indicator: ", GetLastError());
      return(INIT_FAILED);
   }
   
   // Initialize the Supertrend Indicator
   supertrendIndicatorHandle = iCustom(_Symbol, supertrendTimeframe, "::Indicators\\supertrend.ex5", supertrendAtrPeriod, supertrendAtrMultiplier);
   if(supertrendIndicatorHandle == INVALID_HANDLE){
      Print("Error while initializing the Supertrend indicator", GetLastError());
      return(INIT_FAILED);
   }
   
   //--- Initialize the ATR indicator
   atrHandle = iATR(_Symbol, timeframe, atrPeriod);
   if(atrHandle == INVALID_HANDLE){
      Print("Error while initializing the ATR indicator ", GetLastError());
      return(INIT_FAILED);
   }
   
   //--- Set arrays as series
   ArraySetAsSeries(buySmashArrowBuffer, true);
   ArraySetAsSeries(sellSmashArrowBuffer, true);
   ArraySetAsSeries(upperBandValues, true);
   ArraySetAsSeries(lowerBandValues, true);
   ArraySetAsSeries(atrValues, true);

   return(INIT_SUCCEEDED);
}

Эта функция подготавливает программу при первом подключении к графику. Во время инициализации советник назначает уникальный magic number, создает хэндлы индикаторов и подготавливает массивы, в которых будут храниться значения индикаторов. Если любой из этих шагов инициализации завершается ошибкой, программа прекращает работу заранее, чтобы не допустить некорректного исполнения.

За освобождение ресурсов при остановке программы отвечает функция деинициализации .

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Release Supertrend
   if(hiddenSmashDayIndicatorHandle != INVALID_HANDLE){
      IndicatorRelease(hiddenSmashDayIndicatorHandle);
   }

   //--- Release Supertrend
   if(supertrendIndicatorHandle != INVALID_HANDLE){
      IndicatorRelease(supertrendIndicatorHandle);
   }
   
   //--- Release ATR
   if(atrHandle != INVALID_HANDLE){
      if(IndicatorRelease(atrHandle)){
         Print("The computer memory tracking ATR has been freed.");
      }
   }

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

Созданные во время инициализации хэндлы индикаторов корректно освобождаются, высвобождая системную память.

Наконец, вводится функция обработки тиков .

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();   
}

Эта функция выполняется каждый раз, когда рынок получает новое обновление цены. На этом раннем этапе она получает текущие цены Ask и Bid и записывает текущее время. Эти значения позднее будут использоваться при реализации торговой логики.

На этом этапе структурная основа советника завершена. Теперь программа имеет необходимый каркас для поддержки торговой логики, которая будет разработана в следующих разделах.


Реализация обнаружения сигналов, фильтров рыночного контекста и исполнения сделок

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

Каждая функция предназначена для одной конкретной задачи. Такой модульный подход повышает ясность и делает каждый компонент стратегии простым для понимания и сопровождения. Начнем с обнаружения новых баров.

Обнаружение открытия нового бара

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

//+------------------------------------------------------------------+
//| Function to check if there's a new bar on a given chart timeframe|
//+------------------------------------------------------------------+
bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm){

   datetime currentTm = iTime(symbol, tf, 0);
   if(currentTm != lastTm){
      lastTm       = currentTm;
      return true;
   }  
   return false;   
}

Эта функция получает время открытия самого последнего бара. Если это время отличается от ранее сохраненного значения, значит сформировался новый бар. Затем функция обновляет сохраненное время и возвращает true. Такой простой механизм гарантирует, что программа оценивает сигнал ровно один раз на бар.

Обновление данных индикатора Hidden Smash

Индикатор Hidden Smash, разработанный ранее в этой серии, выявляет базовый ценовой паттерн. Советник считывает буферы индикатора, чтобы определить, существует ли паттерн. Для хранения этих значений используются два массива: один — для бычьих сигналов, другой — для медвежьих. Следующие функции обновляют эти массивы последними значениями индикатора.

//+------------------------------------------------------------------+
//| Updates the buySmashArrowBuffer with latest buy arrow values     |
//+------------------------------------------------------------------+
void UpdateBuySmashArrowBuffer()
{
   int copied = CopyBuffer(hiddenSmashDayIndicatorHandle,
                           0,          // Buy buffer index
                           0,          // Start from most recent bar
                           5,          // Number of values to copy
                           buySmashArrowBuffer);

   if(copied <= 0)
   {
      Print("Failed to copy buy smash arrow buffer. Error: ", GetLastError());
   }
}

Эта функция копирует недавние значения из буфера индикатора, который отмечает бычьи паттерны Smash.

//+------------------------------------------------------------------+
//| Updates the sellSmashArrowBuffer with latest sell arrow values   |
//+------------------------------------------------------------------+
void UpdateSellSmashArrowBuffer()
{
   int copied = CopyBuffer(hiddenSmashDayIndicatorHandle,
                           1,          // Sell buffer index
                           0,          // Start from most recent bar
                           5,          // Number of values to copy
                           sellSmashArrowBuffer);

   if(copied <= 0)
   {
      Print("Failed to copy sell smash arrow buffer. Error: ", GetLastError());
   }
}

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
      UpdateBuySmashArrowBuffer ();
      UpdateSellSmashArrowBuffer();
   }
}

Это гарантирует, что массивы всегда содержат свежие данные до начала оценки сигналов.

Обнаружение корректных сигналов Hidden Smash

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

//+------------------------------------------------------------------+
//| Returns true if a bullish Hidden Smash signal exists at index    |
//+------------------------------------------------------------------+
bool IsBullishHiddenSmashSignal(int index)
{
   if(index < 0)
      return false;

   if(buySmashArrowBuffer[index] != EMPTY_VALUE)
      return true;

   return false;
}

Эта функция проверяет буфер покупки по указанному индексу. Если значение не пустое, индикатор определил бычий паттерн Smash. Затем

//+------------------------------------------------------------------+
//| Returns true if a bearish Hidden Smash signal exists at index    |
//+------------------------------------------------------------------+
bool IsBearishHiddenSmashSignal(int index)
{
   if(index < 0)
      return false;

   if(sellSmashArrowBuffer[index] != EMPTY_VALUE)
      return true;

   return false;
}

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

Обновление данных Supertrend для оценки рыночных условий

Стратегия может дополнительно фильтровать сделки по направлению тренда, предоставляемому индикатором Supertrend. Чтобы использовать эту информацию, программа сначала должна получить значения индикатора.

//+------------------------------------------------------------------+
//| Fetches recent Supertrend upper and lower band values            |
//+------------------------------------------------------------------+
void UpdateSupertrendBandValues(){

   //--- Get a few Supertrend upper band values
   int copiedUpper = CopyBuffer(supertrendIndicatorHandle, 5, 0, 5, upperBandValues);
   if(copiedUpper == -1)
   {
      Print("Error while copying Supertrend upper band values: ", GetLastError());
      return;
   }

   //--- Get a few Supertrend lower band values
   int copiedLower = CopyBuffer(supertrendIndicatorHandle, 6, 0, 5, lowerBandValues);
   if(copiedLower == -1)
   {
      Print("Error while copying Supertrend lower band values: ", GetLastError());
      return;
   }
   
   if(copiedUpper < 5 || copiedLower < 5){
      Print("Insufficient Supertrend indicator data!");
      return;
   }   
}

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

Определение текущего состояния тренда

Индикатор Supertrend может находиться только в двух состояниях: бычьем или медвежьем. Две небольшие функции определяют текущее направление тренда.

//+------------------------------------------------------------------+
//| Returns true if Supertrend is currently in a bullish trend state |
//+------------------------------------------------------------------+
bool IsSupertrendCurrentlyBullish(){

   if(lowerBandValues[1] != EMPTY_VALUE){
      return true;
   }

   return false;
}

Эта функция возвращает true, когда индикатор показывает бычьи условия.

//+------------------------------------------------------------------+
//| Returns true if Supertrend is currently in a bearish trend state |
//+------------------------------------------------------------------+
bool IsSupertrendCurrentlyBearish(){

   if(upperBandValues[1] != EMPTY_VALUE){
      return true;
   }

   return false;
}

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

Обновление значений ATR для стопов на основе волатильности

Стратегия поддерживает режим стоп-лосса на основе волатильности, использующий индикатор Average True Range. Для получения значений ATR мы определяем следующую функцию.

//+------------------------------------------------------------------+
//| To update ATR values                                             |
//+------------------------------------------------------------------+
void UpdateATRValues(){
   
   //--- Get some ATR Values
   int numberOfCopiedATRValues = CopyBuffer(atrHandle, 0, 0, 5, atrValues);
   if(numberOfCopiedATRValues == -1){
      Print("Error while copying ATR values: ", GetLastError());
   }
}

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

Реализация фильтра торгового дня

Некоторые трейдеры предпочитают ограничивать торговлю определенными днями недели. Для этого советник включает настраиваемый фильтр. Следующая вспомогательная функция определяет день недели.

//+------------------------------------------------------------------+
//| Returns the day of the week (0 = Sunday, 6 = Saturday) for the given datetime value|                               
//+------------------------------------------------------------------+
int TimeDayOfWeek(datetime time){
   MqlDateTime timeStruct = {};
   if(!TimeToStruct(time, timeStruct)){
      Print("TimeDayOfWeek: TimeToStruct failed");
      return -1;
   }      
   return timeStruct.day_of_week;
}

Используя это значение, программа может проверить, разрешена ли торговля.

//+------------------------------------------------------------------+
//| Determines whether trading is permitted for the given datetime based on the selected trade-day mode |                               
//+------------------------------------------------------------------+
bool IsTradingDayAllowed(datetime time)
{
   // Baseline mode: no filtering
   if(tradeDayMode == TDW_ALL_DAYS){
      return true;
   }

   int day = TimeDayOfWeek(time);

   switch(day)
   {
      case 0: return tradeSunday;
      case 1: return tradeMonday;
      case 2: return tradeTuesday;
      case 3: return tradeWednesday;
      case 4: return tradeThursday;
      case 5: return tradeFriday;
      case 6: return tradeSaturday;
   }

   return false;
}

Эта функция считывает пользовательскую конфигурацию и определяет, разрешена ли торговля в текущий день.

Проверка существующих позиций

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

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active buy position.  |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveBuyPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
            return true;
         }
      }
   }
   
   return false;
}

Следующая функция выполняет такую же проверку для позиций на продажу.

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active sell position. |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveSellPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
            return true;
         }
      }
   }
   
   return false;
}

Эти функции обеспечивают правило одной сделки в стратегии.

Расчет размера позиции по риску

Управление риском может быть реализовано через динамический расчет размера позиции.

//+------------------------------------------------------------------+
//| Calculates position size using OrderCalcProfit for accuracy      |
//+------------------------------------------------------------------+
double CalculatePositionSizeByRisk(ENUM_ORDER_TYPE orderType, double entryPrice, double stopLossPrice){

   //--- Amount willing to risk
   double amountAtRisk = (riskPerTradePercent / 100.0) *
                         AccountInfoDouble(ACCOUNT_BALANCE);

   //--- Calculate loss for 1 lot
   double lossPerLot = 0.0;

   if(!OrderCalcProfit(orderType,
                       _Symbol,
                       1.0,              // 1 lot
                       entryPrice,
                       stopLossPrice,
                       lossPerLot))
   {
      Print("OrderCalcProfit failed: ", GetLastError());
      return 0.0;
   }

   // Loss will be negative for losing scenario
   lossPerLot = MathAbs(lossPerLot);

   if(lossPerLot <= 0.0)
      return 0.0;

   //--- Raw volume
   double volume = amountAtRisk / lossPerLot;

   //--- Apply broker constraints
   double minLot   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   // Normalize to step
   volume = MathFloor(volume / lotStep) * lotStep;

   if(volume < minLot)
      volume = minLot;

   if(volume > maxLot)
      volume = maxLot;

   return NormalizeDouble(volume, 2);
}

Эта функция определяет максимальный объем, которым можно торговать, рискуя только заданным процентом баланса счета. Она использует функцию OrderCalcProfit для оценки потенциального убытка на лот, затем масштабирует объем соответствующим образом. Результат корректируется с учетом минимальных и максимальных ограничений брокера по лоту.

Расчет уровней стоп-лосса и тейк-профита

Две функции рассчитывают уровни стоп-лосса на основе выбранного режима стоп-лосса. Один режим размещает стоп на экстремуме бара Smash. Другой режим размещает стоп с учетом волатильности ATR.

//+--------------------------------------------------------------------------+
//| Computes the bullish stop loss level based on the index of the smash bar |
//+--------------------------------------------------------------------------+
double GetBuyStopLoss(int index){

   if(smashStopLossMode == SL_ATR_BASED){
      double atrValue = atrValues[1] * atrMultiplier;
      double slLevel  = askPrice - atrValue;
      return NormalizeDouble(slLevel, Digits());
   }else{
      return NormalizeDouble(iLow(_Symbol, timeframe, index), Digits());
   }   
}

Эта функция возвращает уровень стоп-лосса для позиций на покупку.

//+--------------------------------------------------------------------------+
//| Computes the bearish stop loss level based on the index of the smash bar |
//+--------------------------------------------------------------------------+
double GetSellStopLoss(int index){

   if(smashStopLossMode == SL_ATR_BASED){
      double atrValue = atrValues[1] * atrMultiplier;
      double slLevel  = bidPrice + atrValue;
      return NormalizeDouble(slLevel, Digits());
   }else{
      return NormalizeDouble(iHigh(_Symbol, timeframe, index), Digits());
   }   
}

Эта функция выполняет такой же расчет для сделок на продажу.

Уровни тейк-профита проецируются от заданного расстояния риска.

//+--------------------------------------------------------------------------------------------------+
//| Computes the bullish take profit level based on entry price, stop loss, and risk to reward ratio |
//+--------------------------------------------------------------------------------------------------+
double GetBuyTakeProfit(double entryPrice, double stopLoss){
   double riskDistance = entryPrice - stopLoss;
   double rewardDistance = riskDistance * riskRewardRatio;
   rewardDistance = MathAbs(rewardDistance);
   return NormalizeDouble((entryPrice + rewardDistance), Digits());
}

и

//+--------------------------------------------------------------------------------------------------+
//| Computes the bearish take profit level based on entry price, stop loss, and risk to reward ratio |
//+--------------------------------------------------------------------------------------------------+
double GetSellTakeProfit(double entryPrice, double stopLoss){
   double riskDistance = stopLoss - entryPrice;
   double rewardDistance = riskDistance * riskRewardRatio;
   rewardDistance = MathAbs(rewardDistance);
   return NormalizeDouble((entryPrice - rewardDistance), Digits());
}

Эти функции применяют настроенное соотношение риска и прибыли.

Отправка торговых ордеров

Когда все условия выполнены, советник отправляет ордер на торговый сервер.

//+------------------------------------------------------------------+
//| Function to open a market buy position                           |
//+------------------------------------------------------------------+
bool OpenBuy(double entryPrice, double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(ORDER_TYPE_BUY, entryPrice, stopLoss);
   }
   
   if(!Trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   return true;
}

Эта функция открывает рыночную позицию на покупку.

//+------------------------------------------------------------------+
//| Function to open a market sell position                          |
//+------------------------------------------------------------------+
bool OpenSell(double entryPrice, double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(ORDER_TYPE_SELL, entryPrice, stopLoss);
   }
   
   if(!Trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){
      Print("Error while executing a market sell order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   return true;
}

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

Объединение торговой логики

После реализации всех вспомогательных функций финальный шаг — собрать полную торговую логику внутри функции OnTick.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
      
      ...
      
      double stopLossLevel   = 0.000000;
      double takeProfitLevel = 0.000000;
      
      //--- Act on a bullish signal
      if(IsBullishHiddenSmashSignal(2)){
      
         stopLossLevel   = GetBuyStopLoss(2);
         takeProfitLevel = GetBuyTakeProfit(askPrice, stopLossLevel);
         if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
            if(smashTradeMode == SMASH_TRADE_BUY_ONLY || smashTradeMode == SMASH_TRADE_BOTH){
               if(useSupertrendFilter){
                  if(IsSupertrendCurrentlyBullish()){
                     //---
                     if(tradeDayMode == TDW_SELECTED_DAYS){
                        if(IsTradingDayAllowed(currentTime)){
                           OpenBuy(askPrice, stopLossLevel, takeProfitLevel, positionSize);
                        }
                     }else{
                        OpenBuy(askPrice, stopLossLevel, takeProfitLevel, positionSize);
                     }
                  }
               }else{
                  //---
                  if(tradeDayMode == TDW_SELECTED_DAYS){
                     if(IsTradingDayAllowed(currentTime)){
                        OpenBuy(askPrice, stopLossLevel, takeProfitLevel, positionSize);
                     }
                  }else{
                     OpenBuy(askPrice, stopLossLevel, takeProfitLevel, positionSize);
                  }
               }
            }
         }
      }
      
      //--- Act on a bearish signal
      if(IsBearishHiddenSmashSignal(2)){
         stopLossLevel   = GetSellStopLoss(2);
         takeProfitLevel = GetSellTakeProfit(bidPrice, stopLossLevel);
         if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
            if(smashTradeMode == SMASH_TRADE_SELL_ONLY || smashTradeMode == SMASH_TRADE_BOTH){
               if(useSupertrendFilter){
                  if(IsSupertrendCurrentlyBearish()){
                     //---
                     if(tradeDayMode == TDW_SELECTED_DAYS){
                        if(IsTradingDayAllowed(currentTime)){
                           OpenSell(bidPrice, stopLossLevel, takeProfitLevel, positionSize);
                        }
                     }else{
                        OpenSell(bidPrice, stopLossLevel, takeProfitLevel, positionSize);
                     }
                  }
               }else{
                  //---
                  if(tradeDayMode == TDW_SELECTED_DAYS){
                     if(IsTradingDayAllowed(currentTime)){
                        OpenSell(bidPrice, stopLossLevel, takeProfitLevel, positionSize);
                     }
                  }else{
                     OpenSell(bidPrice, stopLossLevel, takeProfitLevel, positionSize);
                  }
               }
            }
         }
      }
   }
}

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

Получившаяся структура гарантирует, что сигналы проверяются, риск рассчитывается, а сделки исполняются последовательно и контролируемо.

Улучшение видимости графика во время тестирования

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

//+------------------------------------------------------------------+
//| This function configures the chart's appearance.                 |
//+------------------------------------------------------------------+
bool ConfigureChartAppearance()
{
   if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){
      Print("Error while setting chart background, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){
      Print("Error while setting chart grid, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){
      Print("Error while setting chart mode, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){
      Print("Error while setting chart foreground, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){
      Print("Error while setting bullish candles color, ", GetLastError());
      return false;
   }
      
   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   return true;
}

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

Функция вызывается на этапе инициализации.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...   
   
   //--- To configure the chart's appearance
   if(!ConfigureChartAppearance()){
      Print("Error while configuring chart appearance", GetLastError());
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

Компиляция советника

На этом этапе основная реализация советника завершена. Исходный код теперь можно скомпилировать в MetaEditor. Если шаги разработки были выполнены правильно, программа должна скомпилироваться без ошибок.

Если при компиляции возникнут проблемы, прикрепленный исходный файл lwHiddenSmashDayContextExpert.mq5 можно использовать как эталон для проверки реализации.

Следующий этап статьи переходит от разработки к тестированию стратегии и оценке торговой системы. 


Тестирование стратегии Hidden Smash Day с учетом рыночного контекста

Перед оценкой долгосрочной эффективности убедимся, что советник исполняет сделки в соответствии с паттерном Hidden Smash Day, описанным ранее в статье.

Поэтому сначала представлены два скриншота. На одном изображении показана активная длинная позиция.

Long Position

На другом скриншоте показана активная короткая позиция.

Short Position

Эти примеры показывают, что система корректно обнаруживает сетапы Hidden Smash Day и исполняет сделки только после появления корректного бара подтверждения.

В примере длинной сделки бар Smash закрывается в нижней части своего диапазона и одновременно выше закрытия предыдущего бара. Следующий бар закрывается выше максимума бара Smash, что активирует корректный сигнал на покупку. На открытии следующего бара советник открывает длинную позицию в соответствии с заданными правилами.

В примере короткой сделки бар Smash закрывается в верхней части своего диапазона и одновременно ниже закрытия предыдущего бара. Следующий бар закрывается ниже минимума бара Smash, что формирует подтвержденный сигнал на продажу. Затем советник исполняет короткую позицию на открытии следующего бара.

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

Эксперимент 1: торговля всеми сигналами Hidden Smash

Первый эксперимент оценивает стратегию без какой-либо трендовой фильтрации. В этой конфигурации советнику разрешено торговать каждый корректный паттерн Hidden Smash Day. Использовались следующие настройки среды.

Таймфрейм:

Daily D1

Период тестирования: 

1 января 2025 года — 28 февраля 2026 года

Были включены как сетапы Smash на покупку, так и сетапы на продажу, чтобы система могла отрабатывать все корректные сигналы. Размер позиции автоматически определялся с использованием конфигурации AUTO_MODE, а риск на сделку был ограничен 1% баланса счета.

Чтобы результаты можно было воспроизвести, к статье прикреплены два файла.

  • configurations_exp1.ini, который содержит настройки среды тестера стратегий
  • parameters_exp1.set, который содержит точные входные параметры советника, использованные в этом тесте

Эксперимент начался с баланса счета 10 000 долларов. К концу периода тестирования стратегия получила общую чистую прибыль 1058,18 доллара, что соответствует ROI чуть выше 10%.

Tester Report

Доля прибыльных сделок в этом тесте составила 100%. Сопровождающая кривая средств показывает плавное движение вверх без существенной просадки.

Equity Curve exp1

Это говорит о том, что в выбранный период тестирования паттерн Hidden Smash Day сформировал серию чистых сигналов по золоту.

Эксперимент 2: добавление рыночного контекста Supertrend

Второй эксперимент оценивает ту же стратегию с добавлением фильтра Supertrend для учета направления рынка.

В этой конфигурации советник берет сделки только тогда, когда сигнал совпадает с направлением индикатора Supertrend, рассчитанного на недельном таймфрейме. Это добавляет в процесс принятия решений трендовое условие старшего таймфрейма.

Среда тестирования осталась идентичной первому эксперименту.

Таймфрейм

Daily D1

Период тестирования

1 января 2025 года — 28 февраля 2026 года

Сетапы Smash на покупку и продажу оставались включенными, а расчет размера позиции по-прежнему использовал AUTO_MODE с риском 1% на сделку. Файлы, необходимые для воспроизведения этого эксперимента, также прикреплены.

  • configurations_exp2.ini, где сохранены настройки среды тестера стратегий
  • parameters_exp2.set, где сохранена конфигурация параметров советника

Снова начав с баланса счета в десять тысяч долларов, стратегия за период тестирования получила общую чистую прибыль 1862,62 доллара.

Tester Report for Exp2

Это дало доходность на капитал чуть выше 18%. Доля прибыльных сделок в этом эксперименте также достигла 100%.

Наблюдения по экспериментам

Из этих двух тестов можно сделать несколько наблюдений.

Первый эксперимент показывает, что развороты Hidden Smash Day могут генерировать прибыльные сделки при механическом исполнении с контролируемым управлением риском.

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

Важно помнить, что эти результаты отражают только конкретный рынок и период тестирования, использованные в этом исследовании. Другие инструменты, потоки данных или временные периоды могут дать другие результаты.

Ограничения системы

Хотя советник четко реализует концепцию Hidden Smash Day, он намеренно оставлен простым. В текущий дизайн не включены несколько функций.

  • Нет фильтра торговли по времени суток
  • Нет контроля спреда или фильтров исполнения
  • Нет защиты от новостных событий

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

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

Дальнейшие эксперименты на разных рынках, таймфреймах и сочетаниях параметров могут дать дополнительные выводы о концепции Hidden Smash Day.

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



Заключение

Эта статья была посвящена преобразованию концепции разворотов Hidden Smash Day в структурированную торговую систему, которую можно исполнять автоматически.

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

В ходе разработки были реализованы несколько практических компонентов.

  • Обнаружение подтвержденных сигналов Hidden Smash Day
  • Необязательная трендовая фильтрация с использованием индикатора Supertrend
  • Фильтр торговых дней для контроля того, когда торговля разрешена
  • Два режима стоп-лосса на основе структуры или волатильности
  • Автоматический расчет размера позиции на основе риска счета
  • Логика исполнения, предотвращающая перекрывающиеся сделки

Следуя шагам, представленным в этой статье, читатель получает полностью функциональный советник, который можно скомпилировать и протестировать непосредственно в MetaTrader.

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

Главное, эта работа показывает, как торговую идею можно перевести из графической концепции в воспроизводимый алгоритм. Когда правила четко выражены в коде, их можно объективно тестировать на исторических данных и улучшать через эксперименты.

Ниже приведено описание всех файлов, прикрепленных к этой статье:

Имя файла Описание
lwHiddenSmashDayContextExpert.mq5 
Завершенный исходный файл советника, разработанного в этой статье
configurations_exp1.ini
Файл конфигурации тестера стратегий с настройками среды, использованными в эксперименте 1
parameters_exp1.set 
Входные параметры, использованные в эксперименте 1
configurations_exp2.ini
Файл конфигурации тестера стратегий с настройками среды, использованными в эксперименте 2
parameters_exp2.set
Входные параметры, использованные в эксперименте 2

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21393

Прикрепленные файлы |
Разработка инструментария для анализа Price Action (Часть 69): Обнаружение паттерна "флаг" в MQL5 Разработка инструментария для анализа Price Action (Часть 69): Обнаружение паттерна "флаг" в MQL5
В этой статье показано, как преобразовать субъективное распознавание паттерна "флаг" в воспроизводимую логику на языке MQL5 для графиков в реальном времени. Она объединяет нормализованную по ATR силу флагштока, ограничения на откат, проверку структуры консолидации, подтверждение пробоя и контроль перекрытия. В результате читатель получает практический подход, который строит адаптивные каналы и зоны, эффективно обновляет активные сетапы и при необходимости выдает алерты о новых подтвержденных паттернах.
Торговые инструменты на MQL5 (Часть 31): Создание интерактивной палитры инструментов в MQL5 Торговые инструменты на MQL5 (Часть 31): Создание интерактивной палитры инструментов в MQL5
Мы превращаем боковую панель "Палитра инструментов" из статической оболочки в интерактивную систему MQL5. В статье реализованы выдвижные панели для каждой категории, обработчик событий графика, механизм рисования с несколькими щелчками мыши (инструменты с одним, двумя и тремя щелчками), а также взаимодействие с мышью, включая перетаскивание, изменение размера нижнего края, прокрутку, состояния при наведении курсора и переключение тем в реальном времени. Вы сможете выбирать инструмент и размещать объекты графика непосредственно из палитры для анализа.
Управление позициями: Безопасный пирамидинг с единым стопом в MQL5 Управление позициями: Безопасный пирамидинг с единым стопом в MQL5
В этой статье представлен CPyramidEngine – переиспользуемый класс на языке MQL5, который добавляет в любой советник дисциплинированное пирамидирование и требует для интеграции всего около шести изменений в коде. Движок обеспечивает соблюдение трех ограничений: размеры лотов должны строго уменьшаться, единый стоп – сдвигаться после каждого добавления, а каждая модификация – проходить проверку на уровне брокера. В статье разбираются типичные сценарии отказа наивных реализаций и показывается, как по мере добавления позиций сохранять общий риск по счету измеримым и контролируемым.
Разработка инструментария для анализа Price Action (Часть 68): Панель RSI с привязкой к цене на языке MQL5 Разработка инструментария для анализа Price Action (Часть 68): Панель RSI с привязкой к цене на языке MQL5
Мы представляем встроенную в график панель RSI, которая устраняет необходимость в отдельном окне, привязывая данные о моментуме непосредственно к текущей цене. В статье рассматриваются концепция решения и код на MQL5: получение значений RSI в реальном времени, классификация сигналов по наклону и адаптивное позиционирование. Трейдеры получают значение RSI, состояние и силу сигнала прямо в точке принятия решения, что повышает ясность анализа на разных таймфреймах.