English Deutsch 日本語
preview
Построение модели для ограничения диапазона сигналов по тренду (Часть 10): Золотой крест и крест смерти

Построение модели для ограничения диапазона сигналов по тренду (Часть 10): Золотой крест и крест смерти

MetaTrader 5Примеры |
88 1
Clemence Benjamin
Clemence Benjamin

Введение

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

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

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

Содержание:

  1. Стратегия золотого креста и креста смерти
  2. Анализ актуальности стратегии в более широком контексте следования тренду
  3. Адаптация к условиям, ограничивающим тренд
  4. Реализация стратегии с использованием MQL5
  5. Первоначальное тестирование стратегии
  6. Включение новой стратегии в Trend Constraint
  7. Результаты тестирования и оптимизации
  8. Заключение


Стратегия золотого креста и креста смерти

Стратегии "золотой крест" и "крест смерти" являются ключевыми концепциями технического анализа, используемыми для сигнализации о потенциальных бычьих или медвежьих трендах на основе пересечений скользящих средних. Золотой крест возникает, когда краткосрочная скользящая средняя, обычно 50-дневная, пересекает снизу вверх долгосрочную скользящую среднюю, часто 200-дневную. Это событие считается сильным бычьим сигналом, предполагающим возможность начала долгосрочного восходящего тренда. Инвесторы и трейдеры могут рассматривать его как возможность купить или держать акции, ожидая роста цены. 

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


Анализ актуальности стратегии в более широком контексте следования тренду

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

1. Определение тренда: основная функция этих пересечений — подача четких сигналов о наступлении новых трендов. "Золотой крест" сигнализирует о начале восходящего тренда, побуждая тех, кто следует за трендом, открывать длинные позиции или увеличивать объемы инвестиций. Напротив, "крест смерти" указывает на начало нисходящего тренда, предполагая, что пришло время выходить из длинных позиций или рассматривать возможность коротких продаж.
2. Подтверждение трендов: сигналы выступают в качестве подтверждений в стратегиях следования за трендом. Трейдер, следующий за трендом, может использовать эти пересечения вместе с другими индикаторами, такими как индикаторы ценового движения, объема или импульса, чтобы убедиться, что тренд действительно развивается. Такой многоиндикаторный подход помогает сократить количество ложных сигналов, что имеет решающее значение при следовании за трендом, где цель состоит в том, чтобы следовать тренду как можно дольше.
3. Управление рисками: при следовании за трендами управление рисками имеет решающее значение, поскольку тренды могут неожиданно развернуться или приостановиться. Описываемые стратегии можно использовать для установки уровней стоп-лосса или для принятия решения о том, когда следует уменьшить или увеличить размер позиции. Например, прорыв скользящих средних ниже после формирования "золотого креста" может указывать на необходимость ужесточения стоп-ордеров или сокращения рисков.
4. Долгосрочная перспектива: эти сигналы по своей природе являются долгосрочными из-за использования более длительных периодов скользящей средней, что хорошо согласуется с философией следования за трендом, заключающейся в улавливании крупных движений на рынке, а не краткосрочных колебаний. Такой долгосрочный подход помогает отфильтровывать рыночный шум и концентрироваться на существенных изменениях цен.
5. Адаптивность: Хотя традиционно стратегия основана на 50-дневных и 200-дневных скользящих средних, ее можно адаптировать к различным таймфреймам или рыночным условиям. На быстро меняющихся рынках трейдеры могут использовать более короткие периоды для более быстрых сигналов, тогда как на более стабильных рынках предпочтительны более длинные периоды, чтобы избежать резких колебаний из-за незначительных коррекций.

Однако значимость стратегии можно критически проанализировать следующим образом:

  • Задержка: Одним из основных замечаний является задержка, характерная для скользящих средних. К тому времени, как произойдут эти пересечения, значительная часть тренда уже может проявиться, что потенциально снижает прибыльность сделок, основанных исключительно на этих сигналах.
  • Ложные сигналы: Следование за трендом несет в себе риск ложных прорывов или преждевременных разворотов тренда. Поэтому этот подход должен стать частью более широкой стратегии, которая включает другие формы анализа или подтверждающие сигналы.
  • Рыночная среда: Эффективность стратегии может существенно варьироваться в зависимости от рыночных условий. На трендовых рынках эти сигналы могут быть очень эффективными, но на рынках с ограниченным диапазоном или нестабильных рынках они могут привести к многочисленным ложным стартам.


Адаптация к условиям, ограничивающим тренд

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

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

Такой подход обеспечивает:

  1. Гибкость на разных таймфреймах: советник может обнаруживать краткосрочные развороты (например, золотые кресты) и согласовывать их с более широкими движениями рынка, повышая адаптивность.
  2. Улучшенные точки входа: своевременное выявление изменений в настроениях трендов позволяет советнику занимать стратегические позиции, минимизируя задержку в реагировании на изменения рынка.
  3. Синергия между стратегиями: независимая интеграция стратегий "золотого креста" и "креста смерти" позволяет советнику использовать их сильные стороны, не меняя основные механизмы следования за трендом.

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


Реализация стратегии с использованием MQL5

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

Как обычно, откройте приложение MetaEditor 5 с рабочего стола или нажмите клавишу F4 в терминале MetaTrader 5. Следуйте этапам разработки, описанным ниже. Начинающим рекомендуется вводить код вручную, а не копировать и вставлять, так как это помогает закрепить навык и углубить понимание.

Заголовок и метаданные:

Это самая верхняя часть программы: начинаем с определения метаданных, чтобы обеспечить ясность. Метаданные идентифицируют программу как Strategic Golden & Death Cross EA, включая сведения об авторских правах, описание и номер версии. Этот шаг помогает нам организовать проект и обеспечивает правильное указание авторства нашей работы при развертывании в MetaTrader 5. Настраивая метаданные таким образом, мы создаем профессиональную и хорошо документированную основу для нашего советника.

//+------------------------------------------------------------------+ 
//|                       Strategic Golden & Death Cross EA.mq5      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"

Подключаемые файлы и инициализация торгового объекта:

Для упрощения совершения сделок включаем торговую библиотеку MQL5 с помощью #include<Trade\Trade.mqh> . Эта библиотека обеспечивает доступ к мощным торговым функциям. Создаем экземпляр класса CTrade как торговую платформу, позволяя советнику обрабатывать такие операции, как покупка, продажа и закрытие позиций, без необходимости вручную выполнять эти процедуры. Это снижает сложность и делает код более надежным. При такой настройке мы можем сосредоточиться на стратегии и положиться на библиотеку управления торговлей для исполнения наших ордеров.

#include<Trade\Trade.mqh>;
CTrade trade;

Входные параметры:

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

Например:

  • LotSize позволяет нам управлять объемом торговли.
  • Проскальзывание определяет приемлемые отклонения цены во время исполнения.
  • FastEMAPeriod и SlowEMAPeriod определяют периоды скользящих средних, которые составляют основу стратегий "золотой крест" и "крест смерти".
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 10000;       // Timer interval in seconds
input double TakeProfitPips = 100;     // Take Profit in pips
input double StopLossPips = 50;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

Устанавливая эти параметры, мы позволяем пользователям настраивать советник в соответствии с конкретными торговыми условиями.

Инициализация и настройка таймера:

Убедимся, что советник инициализируется правильно. В функции OnInit() добавим проверку наличия достаточного количества баров для расчета медленной EMA. В противном случае советник регистрирует ошибку и останавливает выполнение. Это гарантирует, что стратегия будет запущена только при наличии достаточного количества данных.

int OnInit()
{
   //--- create timer
   EventSetTimer(TimerInterval);

   //--- Check if there are enough bars for EMA calculation
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
   {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

Дополнительно используем EventSetTimer() для перидоческого вызова функции OnTimer() и исполнения торговой логики. Функция OnDeinit() обеспечивает отключение таймера при удалении советника, освобождая ресурсы.

void OnDeinit(const int reason)
{
   //--- delete timer
   EventKillTimer();
}

Торговая логика в OnTimer():

Теперь давайте перейдем к сути стратегии в рамках функции OnTimer():

Расчет EMA:

Начнем с создания хэндлов для быстрых и медленных EMAs, используя iMA() и извлекая их значения с помощью CopyBuffer(). Эти EMA необходимы для обнаружения сигналов входа.

int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

double fastEMAArray[], slowEMAArray[];
CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray);
CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray);

Извлечение рыночных данных:

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

double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

Сигналы входа:

Здесь мы определяем условия открытия сделок на покупку-продажу. "Золотой крест" (быстрая EMA пересекает медленную EMA сверху вниз) сигнализирует о покупке, а "крест смерти" (быстрая EMA пересекает медленную EMA снизу вверх) - о продаже.

if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
{
   double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
   trade.Sell(LotSize, _Symbol, ask, sl);
}
else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
{
   double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
   trade.Buy(LotSize, _Symbol, bid, sl);
}

Сигналы выхода:

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

if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
   (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
{
   trade.PositionClose(PositionGetInteger(POSITION_TICKET));
}

Обработка ошибок:

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

if(!trade.Sell(LotSize, _Symbol, ask, sl))
{
   Print("Sell order error: ", GetLastError());
}

Вот наш полный код:

//+------------------------------------------------------------------+ 
//|                          Golden & Death Cross Strategy.mq5       |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+

#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"


//+------------------------------------------------------------------+
//| Includes                                                         |
//+------------------------------------------------------------------+
#include<Trade\Trade.mqh>;
CTrade trade;

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 1000;          // Timer interval in seconds
input double StopLossPips = 1500;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- create timer
   EventSetTimer(TimerInterval);
   
   //--- Check if there are enough bars to calculate the EMA
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
     {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- delete timer
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()
{
   bool hasPosition = PositionSelect(_Symbol);
   
   int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(fastEMAHandle < 0 || slowEMAHandle < 0)
   {
      Print("Failed to create EMA handles. Error: ", GetLastError());
      return;
   }

   double fastEMAArray[], slowEMAArray[];
   if(CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
      CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
   {
      Print("Failed to copy EMA data. Error: ", GetLastError());
      return;
   }

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(!hasPosition)
   {
      if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
      {
         
         double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
         if(!trade.Sell(LotSize, _Symbol, ask, sl ))
            Print("Buy order error ", GetLastError());
         else
            Print("Buy order opened with TP ", " and SL ", StopLossPips, " pips");
      }
      else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
      {
         
         double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
         if(!trade.Buy(LotSize, _Symbol, bid, sl ))
            Print("Sell order error ", GetLastError());
         else
            Print("Sell order opened with TP ",  " and SL ", StopLossPips, " pips");
      }
   }
   else
   {
     if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
         (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
    {
         ulong ticket = PositionGetInteger(POSITION_TICKET);
         if(!trade.PositionClose(ticket))
            Print("Failed to close position (Ticket: ", ticket, "). Error: ", GetLastError());
         else  
            Print("Position closed : ", ticket);
      }
   }
}

//+------------------------------------------------------------------+


Первоначальное тестирование стратегии

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

strategy tester settings.PNG

Настройки тестера стратегий: индекс Boom 500

Входные параметры

Входные параметры: индекс Boom 500

Индекс Boom 500 M5: тестирование стратегии "золотого креста" и "креста смерти"

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


Включение новой стратегии в Trend Constraint

Мы создали советника с несколькими стратегиями. Просмотреть все имеющиеся на данный момент стратегии можно в части 9. Вот список стратегий, которые уже интегрированы:

  • Следование за трендом
  • Прорыв канала Дончиана
  • Стратегия дивергенции

Здесь мы добавляем четвертую стратегию, которая, как я объяснил ранее, работает независимо и свободна от ограничивающих условий, что позволяет ей использовать все возможности разворота. Чтобы добиться этого, мы внесем изменения в советник Trend Constraint, добавив логический переключатель для новой стратегии на основе "золотого креста" и "креста смерти". Кроме того, мы проведем рефакторинг других разделов кода в соответствующие функции основного кода.

Чтобы избежать конфликтов с другими терминами, уже присутствующими в основной программе, мы добавили уникальный префикс к переменным, связанным с добавляемой стратегией. Например, мы переименовали LotSize в GDC_LotSize = 1.0; для обеспечения ясности и избежания путаницы

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

//+------------------------------------------------------------------+
//|                                      Trend Constraint Expert.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamini"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.03"

#include <Trade\Trade.mqh>
CTrade trade;

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Following Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = false;       // Enable/Disable Divergence Strategy
input bool UseGoldenDeathCrossStrategy = true;  // Enable/Disable Golden/Death Cross Strategy

// Input parameters for Golden/Death Cross Strategy
input double GDC_LotSize = 1.0;            // Trade volume (lots) for Golden Death Cross
input int GDC_Slippage = 20;               // Slippage in points for Golden Death Cross
input int GDC_TimerInterval = 1000;        // Timer interval in seconds for Golden Death Cross
input double GDC_StopLossPips = 1500;      // Stop Loss in pips for Golden Death Cross
input int GDC_FastEMAPeriod = 50;          // Fast EMA period for Golden Death Cross
input int GDC_SlowEMAPeriod = 200;         // Slow EMA period for Golden Death Cross

int GDC_fastEMAHandle, GDC_slowEMAHandle;  // Handles for EMA indicators in Golden Death Cross

// Global variables
double prevShortMA, prevLongMA;

// Input parameters for Trend Constraint Strategy
input int    RSI_Period = 14;            // RSI period
input double RSI_Overbought = 70.0;     // RSI overbought level
input double RSI_Oversold = 30.0;       // RSI oversold level
input double Lots = 0.1;                // Lot size
input double StopLoss = 100;            // Stop Loss in points
input double TakeProfit = 200;          // Take Profit in points
input double TrailingStop = 50;         // Trailing Stop in points
input int    MagicNumber = 12345678;    // Magic number for the Trend Constraint EA
input int    OrderLifetime = 43200;     // Order lifetime in seconds (12 hours)

// Input parameters for Breakout Strategy
input int InpDonchianPeriod = 20;       // Period for Donchian Channel
input double RiskRewardRatio = 1.5;     // Risk-to-reward ratio
input double LotSize = 0.1;             // Default lot size for trading
input double pipsToStopLoss = 15;       // Stop loss in pips for Breakout
input double pipsToTakeProfit = 30;     // Take profit in pips for Breakout

// Input parameters for Divergence Strategy
input int DivergenceMACDPeriod = 12;    // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;   // MACD Signal period
input double DivergenceLots = 1.0;      // Lot size for Divergence trades
input double DivergenceStopLoss = 300;   // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500; // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;       // Number of periods to look back for divergence
input double profitLockerPoints  = 20;  // Number of profit points to lock

// Indicator handle storage
int rsi_handle;                         
int handle;                             // Handle for Donchian Channel
int macd_handle;

double ExtUpBuffer[];                   // Upper Donchian buffer
double ExtDnBuffer[];                   // Lower Donchian buffer
double ExtMacdBuffer[];                 // MACD buffer
double ExtSignalBuffer[];               // Signal buffer
int globalMagicNumber;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    prevShortMA = 0.0;
    prevLongMA = 0.0;
    // Initialize RSI handle
    rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if (rsi_handle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Create a handle for the Donchian Channel
    handle = iCustom(_Symbol, PERIOD_CURRENT, "Free Indicators\\Donchian Channel", InpDonchianPeriod);
    if (handle == INVALID_HANDLE)
    {
        Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Initialize MACD handle for divergence
    globalMagicNumber = DivergenceMagicNumber;
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to create MACD indicator handle for divergence strategy. Error: ", GetLastError());
        return INIT_FAILED;
    }
    
    
    if(UseGoldenDeathCrossStrategy)
{
    // Check if there are enough bars to calculate the EMA
    if(Bars(_Symbol, PERIOD_CURRENT) < GDC_SlowEMAPeriod)
    {
        Print("Not enough bars for EMA calculation for Golden Death Cross");
        return INIT_FAILED;
    }
    GDC_fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    GDC_slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    if(GDC_fastEMAHandle < 0 || GDC_slowEMAHandle < 0)
    {
        Print("Failed to create EMA handles for Golden Death Cross. Error: ", GetLastError());
        return INIT_FAILED;
    }
}

    // Resize arrays for MACD buffers
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);

    Print("Trend Constraint Expert initialized.");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
    Print("Trend Constraint Expert deinitialized.");
}

//+------------------------------------------------------------------+
//| Check Golden/Death Cross Trading Logic                           |
//+------------------------------------------------------------------+
void CheckGoldenDeathCross()
{
    double fastEMAArray[2], slowEMAArray[2];
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    if(CopyBuffer(GDC_fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
       CopyBuffer(GDC_slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
    {
        Print("Failed to copy EMA data for Golden Death Cross. Error: ", GetLastError());
        return;
    }

    bool hasPosition = PositionSelect(_Symbol);

    if(!hasPosition)
    {
        if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
        {
            double sl = NormalizeDouble(ask + GDC_StopLossPips * point, _Digits);
            if(!trade.Sell(GDC_LotSize, _Symbol, ask, sl ))
                Print("Sell order error for Golden Death Cross ", GetLastError());
            else
                Print("Sell order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
        else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
        {
            double sl = NormalizeDouble(bid - GDC_StopLossPips * point, _Digits);
            if(!trade.Buy(GDC_LotSize, _Symbol, bid, sl ))
                Print("Buy order error for Golden Death Cross ", GetLastError());
            else
                Print("Buy order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
    }
    else
    {
        if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
           (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
        {
            ulong ticket = PositionGetInteger(POSITION_TICKET);
            if(!trade.PositionClose(ticket))
                Print("Failed to close position for Golden Death Cross (Ticket: ", ticket, "). Error: ", GetLastError());
            else  
                Print("Position closed for Golden Death Cross: ", ticket);
        }
    }
}

//+------------------------------------------------------------------+
//| Check Trend Following Strategy                                   |
//+------------------------------------------------------------------+
void CheckTrendFollowing()
{
   if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    double rsi_value;
    double rsi_values[];
    if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0)
    {
        Print("Failed to get RSI value. Error: ", GetLastError());
        return;
    }
    rsi_value = rsi_values[0];

    double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
    double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);

    bool is_uptrend = ma_short > ma_long;
    bool is_downtrend = ma_short < ma_long;

    if (is_uptrend && rsi_value < RSI_Oversold)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double stopLossPrice = currentPrice - StopLoss * _Point;
        double takeProfitPrice = currentPrice + TakeProfit * _Point;

        // Corrected Buy method call with 6 parameters
        if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy"))
        {
            Print("Trend Following Buy order placed.");
        }
    }
    else if (is_downtrend && rsi_value > RSI_Overbought)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double stopLossPrice = currentPrice + StopLoss * _Point;
        double takeProfitPrice = currentPrice - TakeProfit * _Point;

        // Corrected Sell method call with 6 parameters
        if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell"))
        {
            Print("Trend Following Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Breakout Strategy                                          |
//+------------------------------------------------------------------+
void CheckBreakoutTrading()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    ArrayResize(ExtUpBuffer, 2);
    ArrayResize(ExtDnBuffer, 2);

    if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0)
    {
        Print("Error reading Donchian Channel buffer. Error: ", GetLastError());
        return;
    }

    double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0);
    double lastOpen = iOpen(_Symbol, PERIOD_D1, 1);
    double lastClose = iClose(_Symbol, PERIOD_D1, 1);

    bool isBullishDay = lastClose > lastOpen;
    bool isBearishDay = lastClose < lastOpen;

    if (isBullishDay && closePrice > ExtUpBuffer[1])
    {
        double stopLoss = closePrice - pipsToStopLoss * _Point;
        double takeProfit = closePrice + pipsToTakeProfit * _Point;
        if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy") > 0)
        {
            Print("Breakout Buy order placed.");
        }
    }
    else if (isBearishDay && closePrice < ExtDnBuffer[1])
    {
        double stopLoss = closePrice + pipsToStopLoss * _Point;
        double takeProfit = closePrice - pipsToTakeProfit * _Point;
        if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell") > 0)
        {
            Print("Breakout Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Divergence Trading                                         |
//+------------------------------------------------------------------+
bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBullishHiddenDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 > priceLow2 && macdLow1 < macdLow2);
}

bool CheckBearishRegularDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 > priceHigh2 && macdHigh1 < macdHigh2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2]; 
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    // Check if no position is open or if less than 3 positions are open
    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        int barsAvailable = Bars(_Symbol, PERIOD_CURRENT);
        if (barsAvailable < DivergenceLookBack * 2)
        {
            Print("Not enough data bars for MACD calculation.");
            return;
        }

        int attempt = 0;
        while(attempt < 6)
        {
            if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
                CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
                break; 
            
            Print("Failed to copy MACD buffer, retrying...");
            Sleep(1000);
            attempt++;
        }
        if(attempt == 6)
        {
            Print("Failed to copy MACD buffers after ", attempt, " attempts.");
            return;
        }

        if(TimeCurrent() == iTime(_Symbol, PERIOD_CURRENT, 0))
        {
            Print("Skipping trade due to incomplete bar data.");
            return;
        }

        double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
        double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
        double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
        bool isDailyBullish = dailyClose > dailyOpen;
        bool isDailyBearish = dailyClose < dailyOpen;

        // Only proceed with buy orders if D1 is bullish
        if (isDailyBullish)
        {
            if ((CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }
        }

        // Only proceed with sell orders if D1 is bearish
        if (isDailyBearish)
        {
            if ((CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
    else
    {
        Print("Divergence strategy: Maximum number of positions reached.");
    }
}

void ExecuteDivergenceOrder(bool isBuy)
{
    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);
    
    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
        {
            Print("Divergence Buy order placed.");
        }
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
        {
            Print("Divergence Sell order placed.");
        }
    }
}

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}

//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (UseTrendFollowingStrategy)
        CheckTrendFollowing();
    if (UseBreakoutStrategy)
        CheckBreakoutTrading();
    if (UseDivergenceStrategy)
        CheckDivergenceTrading();
    if(UseGoldenDeathCrossStrategy)
        CheckGoldenDeathCross();

}


Результаты тестирования и оптимизации

запуск советника Trend Constraint

Советник Trend Constraint: Добавление в график с настройками по умолчанию

Визуализатор стратегии

Стратегия "золотого креста" и "креста смерти" визуализирована как часть других стратегий в тестере


Заключение

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

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

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

Вернуться к введению

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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Christian Paul Anasco
Christian Paul Anasco | 20 дек. 2024 в 03:15
Это именно то, что мне нравится. Очень чистый код. Легко читать и понимать.
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Нейросети в трейдинге: Обучение метапараметров на основе гетерогенности (Окончание) Нейросети в трейдинге: Обучение метапараметров на основе гетерогенности (Окончание)
В статье описана практическая реализация фреймворка HimNet на базе MQL5, который готов к интеграции в автоматическую торговлю. Мы показываем, как метапараметры, адаптированные под гетерогенность, превращают модель в универсальный инструмент, способный справляться с изменчивой волатильностью.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Интеграция Discord с MetaTrader 5: Создание торгового бота с уведомлениями в реальном времени Интеграция Discord с MetaTrader 5: Создание торгового бота с уведомлениями в реальном времени
В этой статье мы рассмотрим, как интегрировать MetaTrader 5 и сервер Discord, чтобы получать торговые уведомления в реальном времени из любой точки мира. Мы узнаем, как настроить платформу и Discord, чтобы обеспечить отправку оповещений в Discord, а также поговорим о проблемах безопасности, возникающих в связи с использованием WebRequest и вебхуков для таких способов оповещения.