English 中文 Español Deutsch 日本語
preview
Разработка инструментария для анализа движения цен (Часть 7): Советник Signal Pulse

Разработка инструментария для анализа движения цен (Часть 7): Советник Signal Pulse

MetaTrader 5Примеры |
617 0
Christian Benjamin
Christian Benjamin

Содержание



Введение

В этой статье мы рассмотрим разработку MQL5-советника Signal Pulse на языке MQL5. Советник будет использовать комбинацию индикаторов Bollinger Bands и Stochastic Oscillator на трех различных таймфреймах для обнаружения сигналов покупки и продажи. Цель советника — помочь трейдерам принимать обоснованные решения, подтверждая сигналы с нескольких таймфреймов перед входом в сделку. Мы включили несколько таймфреймов, полосы Боллинджера (BB) и стохастический осциллятор для генерации сигналов. Наша цель — свести к минимуму ложные сигналы, которые могут доставить неприятности трейдерам. Объединяя эти элементы, мы стремимся повысить точность наших торговых сигналов. Ниже я составил таблицу важности каждого компонента в нашем инструменте генерации сигналов.

  • Несколько таймфреймов
Польза Описание
Управление рисками Использование нескольких таймфреймов позволяет советнику анализировать рынок с разных точек зрения, снижая влияние ложных сигналов и улучшая управление рисками.
Уверенность в сигналах Подтверждая сигналы на нескольких таймфреймах, советник получает уверенность в направлении торговли, снижая вероятность входа в сделку со слабым или ложным сигналом.
Диверсификация Анализ нескольких таймфреймов обеспечивает более диверсифицированное представление о рынке, позволяя советнику адаптироваться к изменяющимся рыночным условиям и принимать более обоснованные торговые решения.
  • Стохастик
Польза Описание
Состояния перекупленности/перепроданности
Стохастик помогает определить состояния перекупленности и перепроданности, указывая на потенциальные точки разворота на рынке.
Инструмент подтверждения
Стохастик служит инструментом подтверждения для полос Боллинджера, гарантируя, что советник не войдет в сделку, основываясь исключительно на сигналах Боллинджера.
Сортировка ложных сигналов
Используя стохастик, советник может отфильтровывать ложные сигналы, генерируемые полосами Боллинджера, особенно в периоды высокой волатильности.

  • Полосы Боллинджера
Польза Описание
Индикация волатильности
Полосы Боллинджера показывают уровень волатильности на рынке, помогая советнику понять настроение рынка и потенциальные торговые возможности.
Уровни поддержки/сопротивления
Полосы Боллинджера действуют как динамические уровни поддержки и сопротивления, предоставляя советнику потенциальные точки входа и выхода.
Подтверждение тренда 
Ширина полос Боллинджера может подтвердить или опровергнуть наличие тренда, помогая советнику принимать обоснованные торговые решения.

Взаимодействие между несколькими таймфреймами, стохастиком и полосами Боллинджера (BB) значительно повышает эффективность советника в генерации надежных торговых сигналов и управлении рисками. Анализируя сигналы на различных таймфреймах, советник гарантирует, что торговые возможности подтверждаются сильным сочетанием индикаторов Stochastic и BB. Такой многомерный подход снижает вероятность ложных сигналов, поскольку советник выявляет потенциальные сделки только при наличии убедительных доказательств в поддержку решения. В результате сочетание этих элементов повышает надежность генерируемых сигналов и улучшает общее управление рисками, позволяя трейдерам принимать более обоснованные решения. 


Стратегия

Скрипт Signal Pulse генерирует торговые сигналы на основе выравнивания полос Боллинджера и стохастического осциллятора на трех таймфреймах (M15, M30 и H1). Условия для каждого сигнала следующие:

Сигнал на покупку

  1. Состояние полос Боллинджера: Цена касается нижней полосы Боллинджера на всех трех таймфреймах.
  2. Состояние стохастического осциллятора: Стохастический осциллятор указывает на состояние перепроданности на всех трех таймфреймах, обычно ниже 20.
  3. Требование подтверждения: Для формирования сигнала на покупку оба условия должны быть выполнены одновременно на таймфреймах M15, M30 и H1.

Условия на покупку

Рис 1. Условия на покупку

Сигнал на продажу

  1. Состояние полос Боллинджера: Цена касается верхней полосы Боллинджера на всех трех таймфреймах.
  2. Состояние стохастического осциллятора: Стохастический осциллятор указывает на состояние перекупленности на всех трех таймфреймах, обычно выше 80.
  3. Требование подтверждения: Для формирования сигнала на продажу оба условия должны быть выполнены одновременно на таймфреймах M15, M30 и H1.

Условие на продажу

Рис 2. Условия продажи

Визуализируем этот процесс с помощью диаграммы ниже.  

Схема генерации сигналов

Рис. 3. Генерация сигнала


Код MQL5

//+------------------------------------------------------------------+
//|                                              Signal Pulse EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Input parameters
input ENUM_TIMEFRAMES Timeframe1 = PERIOD_M15; // M15 timeframe
input ENUM_TIMEFRAMES Timeframe2 = PERIOD_M30; // M30 timeframe
input ENUM_TIMEFRAMES Timeframe3 = PERIOD_H1;  // H1 timeframe
input int BB_Period = 20;                      // Bollinger Bands period
input double BB_Deviation = 2.0;               // Bollinger Bands deviation
input int K_Period = 14;                       // Stochastic %K period
input int D_Period = 3;                        // Stochastic %D period
input int Slowing = 3;                         // Stochastic slowing
input double SignalOffset = 10.0;              // Offset in points for signal arrow
input int TestBars = 10;                       // Number of bars after signal to test win condition
input double MinArrowDistance = 5.0;          // Minimum distance in points between arrows to avoid overlapping

// Signal tracking structure
struct SignalInfo
  {
   datetime          time;
   double            price;
   bool              isBuySignal;
  };

// Arrays to store signal information
datetime signalTimes[];
double signalPrices[];
bool signalBuySignals[];

//+------------------------------------------------------------------+
//| Retrieve Bollinger Band Levels                                   |
//+------------------------------------------------------------------+
bool GetBollingerBands(ENUM_TIMEFRAMES timeframe, double &upper, double &lower, double &middle)
  {
   int handle = iBands(Symbol(), timeframe, BB_Period, 0, BB_Deviation, PRICE_CLOSE);

   if(handle == INVALID_HANDLE)
     {
      Print("Error creating iBands for timeframe: ", timeframe);
      return false;
     }

   double upperBand[], middleBand[], lowerBand[];

   if(!CopyBuffer(handle, 1, 0, 1, upperBand) ||
      !CopyBuffer(handle, 0, 0, 1, middleBand) ||
      !CopyBuffer(handle, 2, 0, 1, lowerBand))
     {
      Print("Error copying iBands buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
     }

   upper = upperBand[0];
   middle = middleBand[0];
   lower = lowerBand[0];

   IndicatorRelease(handle);
   return true;
  }

//+------------------------------------------------------------------+
//| Retrieve Stochastic Levels                                       |
//+------------------------------------------------------------------+
bool GetStochastic(ENUM_TIMEFRAMES timeframe, double &k_value, double &d_value)
  {
   int handle = iStochastic(Symbol(), timeframe, K_Period, D_Period, Slowing, MODE_SMA, STO_CLOSECLOSE);

   if(handle == INVALID_HANDLE)
     {
      Print("Error creating iStochastic for timeframe: ", timeframe);
      return false;
     }

   double kBuffer[], dBuffer[];

   if(!CopyBuffer(handle, 0, 0, 1, kBuffer) ||  // %K line
      !CopyBuffer(handle, 1, 0, 1, dBuffer))   // %D line
     {
      Print("Error copying iStochastic buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
     }

   k_value = kBuffer[0];
   d_value = dBuffer[0];

   IndicatorRelease(handle);
   return true;
  }

//+------------------------------------------------------------------+
//| Check and Generate Signal                                        |
//+------------------------------------------------------------------+
void CheckAndGenerateSignal()
  {
   double upper1, lower1, middle1, close1;
   double upper2, lower2, middle2, close2;
   double upper3, lower3, middle3, close3;
   double k1, d1, k2, d2, k3, d3;

   if(!GetBollingerBands(Timeframe1, upper1, lower1, middle1) ||
      !GetBollingerBands(Timeframe2, upper2, lower2, middle2) ||
      !GetBollingerBands(Timeframe3, upper3, lower3, middle3))
     {
      Print("Error retrieving Bollinger Bands data.");
      return;
     }

   if(!GetStochastic(Timeframe1, k1, d1) ||
      !GetStochastic(Timeframe2, k2, d2) ||
      !GetStochastic(Timeframe3, k3, d3))
     {
      Print("Error retrieving Stochastic data.");
      return;
     }

// Retrieve the close prices
   close1 = iClose(Symbol(), Timeframe1, 0);
   close2 = iClose(Symbol(), Timeframe2, 0);
   close3 = iClose(Symbol(), Timeframe3, 0);

   bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                    (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition
   bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                     (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

// Check if an arrow already exists in the same region before placing a new one
   if(buySignal && !ArrowExists(close1))
     {
      Print("Buy signal detected on all timeframes with Stochastic confirmation!");

      string arrowName = "BuySignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 241);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrGreen);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = true;
     }

   if(sellSignal && !ArrowExists(close1))
     {
      Print("Sell signal detected on all timeframes with Stochastic confirmation!");

      string arrowName = "SellSignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 242);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrRed);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = false;
     }
  }

//+------------------------------------------------------------------+
//| Check if an arrow already exists within the MinArrowDistance     |
//+------------------------------------------------------------------+
bool ArrowExists(double price)
  {
   for(int i = 0; i < ArraySize(signalPrices); i++)
     {
      if(MathAbs(signalPrices[i] - price) <= MinArrowDistance)
        {
         return true; // Arrow exists in the same price region
        }
     }
   return false; // No arrow exists in the same region
  }

//+------------------------------------------------------------------+
//| OnTick Event                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
   CheckAndGenerateSignal();
  }

//+------------------------------------------------------------------+
//| OnDeinit Function                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Clean up the objects
   long chart_id = 0;
   for(int i = ObjectsTotal(chart_id) - 1; i >= 0; i--)
     {
      string name = ObjectName(chart_id, i);
      if(StringFind(name, "Signal") != -1)
        {
         ObjectDelete(chart_id, name);
        }
     }

   Print("Multitimeframe Bollinger-Stochastic Analyzer deinitialized.");
  }
//+------------------------------------------------------------------+



Разбор кода

В этом разборе советника Signal Pulse шаг за шагом будут рассмотрены различные компоненты советника, механизмы его работы и базовая логика функционала.
  • Заголовок, свойства и входные параметры

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

//+------------------------------------------------------------------+
//|                                              Signal Pulse EA.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.00"

// Input parameters
input ENUM_TIMEFRAMES Timeframe1 = PERIOD_M15; // M15 timeframe
input ENUM_TIMEFRAMES Timeframe2 = PERIOD_M30; // M30 timeframe
input ENUM_TIMEFRAMES Timeframe3 = PERIOD_H1;  // H1 timeframe
input int BB_Period = 20;                      // Bollinger Bands period
input double BB_Deviation = 2.0;               // Bollinger Bands deviation
input int K_Period = 14;                       // Stochastic %K period
input int D_Period = 3;                        // Stochastic %D period
input int Slowing = 3;                         // Stochastic slowing
input double SignalOffset = 10.0;              // Offset in points for signal arrow
input int TestBars = 10;                       // Number of bars after signal to test win condition
input double MinArrowDistance = 5.0;          // Minimum distance in points between arrows to avoid overlapping
Здесь мы указываем различные таймфреймы (M15, M30 и H1), что позволяет анализировать динамику цен за несколько периодов. Мы также определяем параметры полос Боллинджера, такие как период и отклонение, а также настройки стохастического осциллятора, включая его периоды %K и %D. Мы даже включили визуальные настройки для стрелок, которые будут отмечать сигналы покупки и продажи на графике, а также параметры, предназначенные для минимизации визуального беспорядка путем предотвращения перекрытия стрелок.
  •  Отслеживание сигналов и структуры

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

// Signal tracking structure
struct SignalInfo {
   datetime time;
   double price;
   bool isBuySignal;
};

// Arrays to store signal information
datetime signalTimes[];
double signalPrices[];
bool signalBuySignals[];
Благодаря массивам signalTimes, signalPrices и signalBuySignals мы фиксируем торговые сигналы, которые советник генерирует с течением времени, что значительно упрощает обработку множественных сигналов и позволяет избежать путаницы.
  •  Функции поиска индикаторов

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

//+------------------------------------------------------------------+
//| Retrieve Bollinger Band Levels                                   |
//+------------------------------------------------------------------+
bool GetBollingerBands(ENUM_TIMEFRAMES timeframe, double &upper, double &lower, double &middle) {
   int handle = iBands(Symbol(), timeframe, BB_Period, 0, BB_Deviation, PRICE_CLOSE);

   if(handle == INVALID_HANDLE) {
      Print("Error creating iBands for timeframe: ", timeframe);
      return false;
   }

   double upperBand[], middleBand[], lowerBand[];

   if(!CopyBuffer(handle, 1, 0, 1, upperBand) ||
      !CopyBuffer(handle, 0, 0, 1, middleBand) ||
      !CopyBuffer(handle, 2, 0, 1, lowerBand)) {
      Print("Error copying iBands buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
   }

   upper = upperBand[0];
   middle = middleBand[0];
   lower = lowerBand[0];

   IndicatorRelease(handle);
   return true;
}

//+------------------------------------------------------------------+
//| Retrieve Stochastic Levels                                       |
//+------------------------------------------------------------------+
bool GetStochastic(ENUM_TIMEFRAMES timeframe, double &k_value, double &d_value) {
   int handle = iStochastic(Symbol(), timeframe, K_Period, D_Period, Slowing, MODE_SMA, STO_CLOSECLOSE);

   if(handle == INVALID_HANDLE) {
      Print("Error creating iStochastic for timeframe: ", timeframe);
      return false;
   }

   double kBuffer[], dBuffer[];

   if(!CopyBuffer(handle, 0, 0, 1, kBuffer) ||  // %K line
      !CopyBuffer(handle, 1, 0, 1, dBuffer)) { // %D line
      Print("Error copying iStochastic buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
   }

   k_value = kBuffer[0];
   d_value = dBuffer[0];

   IndicatorRelease(handle);
   return true;
}
Первая функция, GetBollingerBands(), извлекает верхний, средний и нижний уровни полос Боллинджера для заданного таймфрейма. Он создает хэндл для индикатора и проверяет, был ли он успешно создан. Если все в порядке, он копирует значения полос в массивы, которые мы позже можем использовать в нашей торговой логике. Аналогично, функция GetStochastic() извлекает значения %K и %D стохастического осциллятора. Он использует те же процедуры проверки ошибок и копирования данных, гарантируя, что мы всегда получаем точные данные для принятия решений.

  • Проверка и генерация сигналов

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

Функция проверяет сигналы на покупку и продажу на основе текущих рыночных условий. Сигнал на покупку срабатывает, когда цены закрытия опускаются ниже нижней полосы Боллинджера, а значения стохастика указывают на состояние перепроданности (менее 5). И наоборот, сигналы на продажу возникают, когда цены превышают верхнюю полосу Боллинджера, а показания стохастика превышают 95, что указывает на состояние перекупленности.

//+------------------------------------------------------------------+
//| Check and Generate Signal                                        |
//+------------------------------------------------------------------+
void CheckAndGenerateSignal() {
   double upper1, lower1, middle1, close1;
   double upper2, lower2, middle2, close2;
   double upper3, lower3, middle3, close3;
   double k1, d1, k2, d2, k3, d3;

   if(!GetBollingerBands(Timeframe1, upper1, lower1, middle1) ||
      !GetBollingerBands(Timeframe2, upper2, lower2, middle2) ||
      !GetBollingerBands(Timeframe3, upper3, lower3, middle3)) {
      Print("Error retrieving Bollinger Bands data.");
      return;
   }

   if(!GetStochastic(Timeframe1, k1, d1) ||
      !GetStochastic(Timeframe2, k2, d2) ||
      !GetStochastic(Timeframe3, k3, d3)) {
      Print("Error retrieving Stochastic data.");
      return;
   }

   // Retrieve the close prices
   close1 = iClose(Symbol(), Timeframe1, 0);
   close2 = iClose(Symbol(), Timeframe2, 0);
   close3 = iClose(Symbol(), Timeframe3, 0);

   bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                    (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition
   bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                     (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

   // Check if an arrow already exists in the same region before placing a new one
   if(buySignal && !ArrowExists(close1)) {
      Print("Buy signal detected on all timeframes with Stochastic confirmation!");
      string arrowName = "BuySignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 241);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrGreen);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = true;
   }

   if(sellSignal && !ArrowExists(close1)) {
      Print("Sell signal detected on all timeframes with Stochastic confirmation!");
      string arrowName = "SellSignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 242);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrRed);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = false;
   }
}

Более того, перед тем, как разместить стрелку для обозначения сигнала на графике, функция проверяет, что существующая стрелка не перекрывается, путем вызова функции ArrowExists(). Если все верно, она создает соответствующую стрелку и сохраняет информацию о сигнале в наших ранее определенных массивах.

  • Проверка существования стрелки

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

//+------------------------------------------------------------------+
//| Check if an arrow already exists within the MinArrowDistance     |
//+------------------------------------------------------------------+
bool ArrowExists(double price) {
   for(int i = 0; i < ArraySize(signalPrices); i++) {
      if(MathAbs(signalPrices[i] - price) <= MinArrowDistance) {
         return true; // Arrow exists in the same price region
      }
   }
   return false; // No arrow exists in the same region
}

  • Функции OnTick и OnDeinit

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

Напротив, функция OnDeinit() вызывается при удалении советника с графика или закрытии терминала. Его задача — очищать все графические объекты, созданные советником, в частности стрелки, обозначающие сигналы покупки и продажи, тем самым поддерживая порядок на графике.

//+------------------------------------------------------------------+
//| OnTick Event                                                     |
//+------------------------------------------------------------------+
void OnTick() {
   CheckAndGenerateSignal();
}

//+------------------------------------------------------------------+
//| OnDeinit Function                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // Clean up the objects
   long chart_id = 0;
   for(int i = ObjectsTotal(chart_id) - 1; i >= 0; i--) {
      string name = ObjectName(chart_id, i);
      if(StringFind(name, "Signal") != -1) {
         ObjectDelete(chart_id, name);
      }
   }

   Print("Multitimeframe Bollinger-Stochastic Analyzer deinitialized.");
}

Кроме того, в советнике Signal Pulse мы скорректировали пороговые уровни для стохастического осциллятора, чтобы гарантировать генерацию только самых надежных сигналов. Эти корректировки направлены на подтверждение того, что рынок находится в экстремальных условиях — либо перепродан для сигналов на покупку, либо перекуплен для сигналов на продажу, — прежде чем предпринимать какие-либо действия.

  • Состояние перепроданности: сигнал на покупку
bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                 (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition

Для сигнала на покупку цена должна быть на уровне или ниже нижней полосы Боллинджера на всех трех таймфреймах (M15, M30, H1), что указывает на сильное давление вниз. Кроме того, значение %K стохастического осциллятора должно быть меньше 5 на всех трех таймфреймах. Это значение указывает на состояние крайней перепроданности, при котором рынок с большой вероятностью развернется вверх. Более строгий порог < 5 гарантирует, что советник будет рассматривать только те сигналы, где вероятность разворота достаточно высока.

  • Состояние перекупленности: сигнал на продажу

bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                  (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

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


Тестирование и результаты

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

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

2. Установка параметров тестирования

После загрузки данных пришло время настроить тестер стратегий в MetaTrader, задав необходимые параметры. Они включают в себя:
  • Символ - выбрать валютную пару или актив для торговли.
  • Период - использовать те же таймфреймы, что и в настройках советника (например, M15, M30, H1).
  • Спред - установить реалистичный или фиксированный спред, чтобы имитировать торговые издержки и убедиться, что результаты соответствуют реальным торговым условиям.
  • Оптимизация - протестировать входные параметры (например, период полос Боллинджера, пороговые значения стохастика) для достижения оптимальной производительности.
3. Оценка результатов
После настройки параметров тестирования настало время проанализировать выходные показатели:
  • Прибыльность: оценить чистую прибыль советника и фактор прибыли, чтобы определить его общую рентабельность.
  • Риск: оценить максимальную просадку, чтобы оценить устойчивость советника к риску.
  • Коэффициент прибыльных сделок и частота сделок: проанализировать количество прибыльных сделок и частоту сделок, чтобы понять эффективность советника в различных рыночных условиях.

Давайте рассмотрим результаты тестирования, представленные ниже.

Результаты теста 1

Рис 4. Результаты теста 1

Результат 2

Рис. 5. Результаты теста 2

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


Заключение

В этой статье мы разработали советник Signal Pulse, объединяющий полосы Боллинджера и стохастический осциллятор для генерации торговых сигналов на таймфреймах M15, M30 и H1. Система определяет состояния перекупленности/перепроданности только при совпадении нескольких таймфреймов, что увеличивает вероятность успеха. Крайне важно тщательное тестирование в различных рыночных условиях. Будущие усовершенствования могут включать добавление фильтров направления тренда, расширенное управление рисками и уточнение логики сигналов для различных типов рынка. Мы призываем трейдеров использовать Signal Pulse вместе с их собственными стратегиями и методиками для комплексного подхода к торговле.

Дата Название инструмента  Описание Версия  Обновления  Примечания
01/10/24 Chart Projector Скрипт для наложения эффекта призрака на движение цены за предыдущий день. 1.0 Первоначальная версия Первый инструмент в Lynnchris Tools Chest
18/11/24 Analytical Comment Предоставляет информацию за предыдущий день в табличном формате, а также прогнозирует будущее направление рынка. 1.0 Первоначальная версия Второй инструмент в Lynnchris Tools Chest
27/11/24 Analytics Master Регулярное обновление рыночных показателей каждые два часа  1.01 Вторая версия Третий инструмент в Lynnchris Tools Chest
02/12/24 Analytics Forecaster  Регулярное обновление рыночных показателей каждые два часа с интеграцией с Telegram 1.1 Третья версия Инструмент номер 4
09/12/24 Volatility Navigator Советник анализирует рыночные условия с помощью полос Боллинджера, RSI и ATR. 1.0 Первоначальная версия Инструмент номер 5
19/12/24 Mean Reversion Signal Reaper  Анализирует рынок и генерирует сигналы, используя стратегию возврата к среднему  1.0  Первоначальная версия  Инструмент номер 6 
9/01/2025  Signal Pulse  Анализирует несколько таймфреймов 1.0  Первоначальная версия  Инструмент номер 7 

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

Прикрепленные файлы |
Переходим на MQL5 Algo Forge (Часть 3): Использование чужих репозиториев в собственном проекте Переходим на MQL5 Algo Forge (Часть 3): Использование чужих репозиториев в собственном проекте
Рассмотрим, как можно уже сейчас подключить чужой код из любого репозитория в хранилище MQL5 Algo Forge к своему проекту. В этой статье мы наконец обратимся к этой многообещающей, но и более сложной задаче: как на практике подключить и использовать в своём проекте библиотеки из чужих репозиториев хранилища MQL5 Algo Forge.
Многопоточный торговый робот с машинным обучением: От концепции до реализации Многопоточный торговый робот с машинным обучением: От концепции до реализации
Статья представляет пошаговую разработку многопоточного торгового робота с машинным обучением на Python и MetaTrader 5. Рассматривается архитектура системы — от сбора данных и создания технических индикаторов до обучения XGBoost-моделей с портфельным риск-менеджментом. Детально описана реализация аугментации данных, кластеризации признаков через Gaussian Mixture Models и координации потоков для параллельной торговли несколькими валютными парами.
Нейросети в трейдинге: Устойчивые торговые сигналы в любых режимах рынка (Модули внимания) Нейросети в трейдинге: Устойчивые торговые сигналы в любых режимах рынка (Модули внимания)
В данной статье мы продолжаем реализацию подходов фреймворка ST-Expert, сосредотачиваясь на практических аспектах его применения средствами MQL5. Ранее мы рассмотрели теоретические основы и ключевые компоненты модели, а теперь переходим к непосредственной работе с алгоритмами графового внимания, локального и глобального распределения внимания. Основная цель текущей работы — показать, как концептуальные идеи ST-Expert превращаются в работоспособные решения для анализа и прогнозирования финансовых рядов.
От новичка до эксперта: Создание анимированного советника для новостей в MQL5 (IX) — Управление несколькими символами на одном графике для торговли на новостях От новичка до эксперта: Создание анимированного советника для новостей в MQL5 (IX) — Управление несколькими символами на одном графике для торговли на новостях
Торговля на новостях часто требует управления несколькими позициями и символами в течение очень короткого времени из-за повышенной волатильности. В сегодняшнем обсуждении мы рассмотрим проблемы торговли несколькими символами, интегрировав эту функцию в наш советник «Заголовки новостей». Присоединяйтесь к нам, и мы узнаем, как алгоритмическая торговля с помощью MQL5 делает торговлю несколькими символами более эффективной и действенной.