English 中文 Español Deutsch 日本語 Português
preview
Построение модели для ограничения диапазона сигналов по тренду (Часть 8): Разработка советника (I)

Построение модели для ограничения диапазона сигналов по тренду (Часть 8): Разработка советника (I)

MetaTrader 5Тестер | 18 февраля 2025, 16:27
589 2
Clemence Benjamin
Clemence Benjamin

Содержание:


Введение

MetaEditor включает в себя компилятор, который эффективно обрабатывает ошибки, обнаруженные во время попыток профилирования. Этот инструмент помог мне понять, почему предыдущая версия не отображала прямоугольники риска и прибыли так, как надо. Хотя программа успешно компилировалась, проблема была не в самом коде. В диапазоне исторических свечей ничего не отображалось, в первую очередь из-за технических особенностей.
  1. Значение исторической свечи (look back candle) по умолчанию было установлено слишком высоким — 5000 баров.
  2. Наличие нескольких буферов в одной программе увеличивает сложность вычислений, что может замедлить отображение окна графика индикатора.

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

    Соотношение риска и прибыли, рассчитанное для пересечения скользящих средних

    Автоматически отрисованные прямоугольники риска и вознаграждения


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

    Для решения проблем индикатора:

    1. Мы сократили период ретроспективного анализа (look-back period) с 5000 до 1000 баров, тем самым значительно уменьшив объем данных для расчета.
    2. Мы стремились снизить нагрузку на программу, создав автономный скрипт как часть набора инструментов. Этот скрипт специально проверяет условия, обрабатываемые буферами 6 и 7 в индикаторе. После выполнения этих условий скрипт рисует необходимые прямоугольники риска-прибыли и размещает линии с ценовыми метками для цены входа, стоп-лосса и тейк-профита. Однако важно отметить, что скрипт выполняет одноразовую задачу и не работает непрерывно. Пользователь должен вручную добавить скрипт на график, чтобы визуализировать торговые уровни, представленные нарисованными объектами и ценовыми отметками.

    Ниже представлено изображение, демонстрирующее запуск скрипта:

    Запуск скрипта Trend Constraint R-R

    Скрипт Trend Constraint R-R: Построение прямоугольников риска и прибыли при пересечении скользящей средней

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

    //+------------------------------------------------------------------+
    //|                                        Trend Constraint R-R.mq5  |
    //|                                                  Script program  |
    //+------------------------------------------------------------------+
    #property strict
    #property script_show_inputs
    #property copyright "2024 Clemence Benjamin"
    #property version "1.00"
    #property link "https://www.mql5.com/en/users/billionaire2024/seller"
    #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover."
    
    
    
    //--- input parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points
    input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points
    input color RiskColor = clrIndianRed; // Default risk color
    input color RewardColor = clrSpringGreen; // Default reward color
    input int MaxBars = 500; // Maximum bars to process
    input int RectangleWidth = 10; // Width of the rectangle in bars
    input bool FillRectangles = true; // Option to fill rectangles
    input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    
      ArraySetAsSeries(FastMA, true);
      ArraySetAsSeries(SlowMA, true);
    
      CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA);
      CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA);
    
      limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA));
    
      for (i = 1; i < limit - 1; i++)
      {
        //--- check for crossover
        if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1])
        {
          //--- long position entry point (bullish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice + RiskHeightPoints * Point();
          rewardLevel = closePrice - RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
        else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1])
        {
          //--- short position entry point (bearish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice - RiskHeightPoints * Point();
          rewardLevel = closePrice + RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to delete existing rectangles and lines                 |
    //+------------------------------------------------------------------+
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw rectangles                                      |
    //+------------------------------------------------------------------+
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      // Set the color with transparency (alpha value)
      int alphaValue = FillTransparency; // Adjust transparency level (0-255)
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw price lines                                     |
    //+------------------------------------------------------------------+
    void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
    {
      datetime time = iTime(NULL, 0, barIndex);
      datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right
    
      if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
        Print("Failed to create price line: ", name);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      // Create text label
      string labelName = name + "_Label";
      if (ObjectFind(0, labelName) >= 0)
        ObjectDelete(0, labelName);
    
      if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
        Print("Failed to create label: ", labelName);
    
      ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT);
      ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
      ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
      ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5);
      ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0);
    }

    Давайте подробно обсудим работу скрипта, прежде чем приступить к разработке советника:

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

    //---- Input Parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0;
    input double RewardHeightPoints = 15000.0;
    input color RiskColor = clrIndianRed;
    input color RewardColor = clrSpringGreen;
    input int MaxBars = 500;
    input int RectangleWidth = 10;
    input bool FillRectangles = true;
    input int FillTransparency = 128;

    • В функции (OnStart) мы позаботились о том, чтобы график оставался чистым, запрограммировав скрипт на предварительное удаление любых существующих прямоугольников риска/прибыли и ценовых линий. Затем мы рассчитали быстрые и медленные скользящие средние с помощью функции (iMA) и сохранили эти значения в массивах для дальнейшей обработки. По мере того, как скрипт проходит по барам на графике, мы установили условия для обнаружения бычьих пересечений, когда быстрая скользящая средняя пересекает медленную снизу вверх. При выполнении этих условий скрипт рассчитывает цену входа, уровень риска (стоп-лосс) и уровень прибыли (тейк-профит). Затем он рисует прямоугольники и ценовые линии на графике, эффективно отмечая эти критические торговые уровни, как мы увидим далее во фрагментах внутреннего кода:

    //----Onstart Function
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    

    • Для сохранения ясности на графике мы разработали функцию Delete Existing Objects (удалить существующие объекты), которую скрипт использует для удаления любых ранее нарисованных объектов, связанных с торговыми сигналами. Проверяя названия всех объектов на графике, скрипт гарантировал, что будет отображаться только самая последняя и актуальная информация, защищая график от захламления:

    //---- DeleteAllExistingObjects Function
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }

    • В функции Draw Rectangle (нарисовать прямоугольник) мы гарантировали, что скрипт сможет визуально отображать уровни риска и прибыли, предварительно удалив все существующие прямоугольники с одинаковыми именами, чтобы избежать дублирования. Затем мы заставили скрипт вычислить время начала и окончания для прямоугольников на основе индексов баров, а также установили цвета и уровни прозрачности. Это позволило выделить прямоугольники на графике, не заслоняя другие важные детали:
    ///---Draw rectangle function
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      int alphaValue = FillTransparency;
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor);
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF);
      }
    }

      • Наконец, мы реализовали функцию Draw Price Line (нарисовать линию цены), чтобы дать скрипту команду добавлять горизонтальные линии на уровнях входа, стоп-лосса и тейк-профита. Скрипт продлил эти линии по всему графику и добавил текстовые метки, отображающие соответствующие уровни цен. Это обеспечило визуальный справочник, позволяющий пользователям быстро определять и управлять ключевыми точками для сделок на основе сигналов, генерируемых скользящими средними:

      ///---- Draw Price Lines Function
      void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
      {
        datetime time = iTime(NULL, 0, barIndex);
        datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth);
      
        if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
          Print("Failed to create price line: ", name);
      
        ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
        ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
        ObjectSetInteger(0, name, OBJPROP_BACK, true);
      
        string labelName = name + "_Label";
        if (ObjectFind(0, labelName) >= 0)
          ObjectDelete(0, labelName);
      
        if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
          Print("Failed to create label: ", labelName);
      
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
        ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
        ObjectSetInteger(0, labelName, OBJPROP_BACK, true);
      }
      

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


      Создание советника на основе индикатора

      Чтобы создать советника на MQL5 с использованием пользовательского индикатора, нам необходимо убедиться, что файл пользовательского индикатора (.ex5) доступен в папке Indicators платформы MetaTrader 5, в данном случае это Trend Constraint V1.09. Используя MetaEditor, мы можем написать наш советник, включив функции MQL5 для доступа к значениям буфера индикатора. Мы используем функцию (iCustom()) для вызова пользовательского индикатора в советнике, указав необходимые параметры, такие как символ и таймфрейм.

      Для извлечения данных из буферов индикатора мы используем функцию (CopyBuffer()), которая извлекает значения буфера, которые мы собираемся анализировать на предмет торговых сигналов. Затем мы реализуем нашу торговую логику на основе этих значений буфера, определяя условия открытия, закрытия или изменения ордеров в соответствии с нашей стратегией. Интегрируем функции управления рисками, такие как стоп-лосс и тейк-профит, для разумного управления торговлей. Проведем тестирование советника на истории с использованием тестера стратегий MetaTrader 5 для оценки его производительности и точной настройки его параметров. Наконец, мы проверим функциональность советника на демо-счете, чтобы убедиться в его эффективной работе в реальных рыночных условиях, перед выходом на реальный рынок.

      Мы можем начать с запуска шаблона советника в MetaEditor, а затем разработать или изменить его на основе примера, показанного скриншоте ниже:

      Запуск шаблона советника в MetaEditor

      Запуск шаблона советника в MetaEditor

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

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

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

      //You can replace the author details with yours.
      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"

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

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

      ///----------- EA inputs parameters for customizations
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders

      3. Функция инициализации (OnInit)

      В функции OnInit мы подготавливаем почву для работы советника, инициализируя необходимые компоненты. Начнем с попытки получить хэндл для нашего индикатора Trend Constraint V1.09. Он позволит нам взаимодействовать с индикатором программно. Если хэндл успешно получен, приступаем к последовательной настройке массивов буферов (Buffer6 и Buffer7), что позволяет нам хранить и обрабатывать значения индикаторов. Если же хэндл не может быть получен, советник вернет ошибку инициализации с описанием, которое поможет нам найти проблему:

      ////-------Initialization Function
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      

      4. Функция деинициализации (OnDeinit)

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

      ///------Deinitialization Function(OnDeinit)
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      

      5. Основная функция исполнения (OnTick)

      Функция (OnTick) — это место, где происходят реальные торговые действия. Эта функция вызывается каждый раз, когда поступает новый рыночный тик. Сначала мы проверяем, нет ли уже открытой позиции с таким же магическим числом, чтобы не дублировать сделки. Далее мы копируем последние значения из буферов индикаторов (Buffer6 и Buffer7) для принятия торговых решений. Если наши условия для сигнала на покупку или продажу выполнены, мы формируем и отправляем соответствующий торговый запрос. Необходимо указать все необходимые параметры, такие как тип ордера, цена, стоп-лосс, тейк-профит и проскальзывание, для эффективной реализации нашей торговой стратегии:

      ///---Main Execution Function(OnTick)
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      

      6. Прочие функции

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

      • (OnTrade) - здесь мы можем обрабатывать любые конкретные действия, которые необходимо выполнить при возникновении торгового события. Хотя в настоящее время эта функция пуста, она предоставляет нам пространство для добавления логики при необходимости.
      • (OnTester) - функция используется для пользовательских расчетов во время тестирования на истории. Возвращая значение, мы можем оптимизировать нашу стратегию на основе определенных показателей.
      • (OnTesterInit, OnTesterPass, OnTesterDeinit) - эти функции участвуют в оптимизации в тестере стратегий. Они позволяют нам инициализировать настройки, выполнять действия после каждого прохода оптимизации и впоследствии очищать ресурсы.
      • (OnChartEvent) - эта функция позволяет нам обрабатывать различные события графика, такие как щелчки мыши или нажатия клавиш. Хотя в настоящее время она пустует, мы можем использовать это пространство для добавления интерактивных функций в наш советник:
      ///----Other Template functions available
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      

      В окончательном виде программа будет выглядеть так:

      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
      
      //--- Input parameters for the EA
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders
      
      //--- Indicator handle
      int indicator_handle;
      double Buffer6[];
      double Buffer7[];
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      
      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      
      //+------------------------------------------------------------------+
      //| Trade function                                                   |
      //+------------------------------------------------------------------+
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      //+------------------------------------------------------------------+
      //| Tester function                                                  |
      //+------------------------------------------------------------------+
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      //+------------------------------------------------------------------+
      //| TesterInit function                                              |
      //+------------------------------------------------------------------+
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      //+------------------------------------------------------------------+
      //| TesterPass function                                              |
      //+------------------------------------------------------------------+
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      //+------------------------------------------------------------------+
      //| TesterDeinit function                                            |
      //+------------------------------------------------------------------+
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      //+------------------------------------------------------------------+
      

      После успешной компиляции программу пора протестировать.


      Тест

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

      Запуск тестера стратегий

      Запуск тестера из навигатора

      Откроется панель тестера стратегий, позволяющая вам настроить некоторые параметры. Как только всё будет готово, нажмите "Старт" в правом нижнем углу. Например, размер лота по умолчанию в советнике установлен на уровне 0,1 лота, но для индекса Boom 500 мне пришлось увеличить его до минимума в 0,2 лота.

      Тестер стратегий

      Панель тестера стратегий

      Удивительно, но наша система хорошо показала себя в тестере стратегий, как видно из скриншота ниже:

      Воспроизведение тестирования

      Визуализация тестера стратегий: советник Trend Constraint


      Заключение

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

      Успешное взаимодействие индикатора (Trend Constraint V1.09) и советника подчеркивает важность синергии между инструментами в торговой стратегии. Индикатор определяет потенциальные тренды и развороты, а советник совершает сделки и управляет рисками на основе этой информации. Такой комплексный подход приводит к более целостной и эффективной торговой стратегии.

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

      Приложенные файлы Описание
      Trend_Constraint V1.09.mq5 Исходный код индикатора для работы с советником.
      Trend Constraint R-R.mq5 Исходный код скрипта для отображения прямоугольников риска и прибыли.
      Trend Constraint Expert.mq5 Исходный код советника, работающего строго с Trend Constraint V1.09

      Вернуться к содержанию

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

      Прикрепленные файлы |
      Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
      Helga Gustana Argita
      Helga Gustana Argita | 14 авг. 2024 в 19:50
      Здравствуйте, у меня есть ошибка, как ее исправить?

      2024.08.15 00:47:15.123 2024.08.01 00:00:00 Не удается загрузить пользовательский индикатор 'Trend Constraint V1.09' [4802]
      2024.08.15 00:47:15.123 2024.08.01 00:00:00 ошибка создания индикатора в 'Trend_Constraint_Expert.mq5' (1,1)

      Clemence Benjamin
      Clemence Benjamin | 15 авг. 2024 в 22:43
      argatafx28 пользовательский индикатор 'Trend Constraint V1.09' [4802]
      2024.08.15 00:47:15.123 2024.08.01 00:00:00 ошибка создания индикатора в 'Trend_Constraint_Expert.mq5' (1,1)

      Привет @argatafx28

      1. Убедитесь, что для работы советника установлен индикатор Trend Constraint V1.09.
      2. Trend Constraint V1.09 использует ShellExecute для интеграции телеграммы, не могли бы вы убедиться, что при запуске индикатора разрешены DLL-зависимости?


      Включить Разрешить DLL

      Кстати, какую операционную систему вы используете?
      Разработка системы репликации (Часть 68): Настройка времени (I) Разработка системы репликации (Часть 68): Настройка времени (I)
      Сегодня мы продолжим работу над тем, чтобы заставить указатель мыши сообщать нам об оставшемся времени бара в периоды низкой ликвидности. Хотя на первый взгляд кажется, что всё просто, на самом деле эта задача гораздо сложнее. Это связано с некоторыми препятствиями, которые нам придется преодолеть. Поэтому важно, чтобы вы хорошо усвоили материал из первой части данной серии, чтобы понять следующие части.
      Нейросети в трейдинге: Двухмерные модели пространства связей (Окончание) Нейросети в трейдинге: Двухмерные модели пространства связей (Окончание)
      Продолжаем знакомство с инновационным фреймворком Chimera — двухмерной моделью пространства состояний, использующей нейросетевые технологии для анализа многомерных временных рядов. Этот метод обеспечивает высокую точность прогнозирования при низких вычислительных затратах.
      Интеграция MQL5: Python Интеграция MQL5: Python
      Python — известный и популярный язык программирования со множеством функций, особенно в областях финансов, науки о данных, искусственного интеллекта и машинного обучения. Python — мощный инструмент, который может быть полезен и в трейдинге. MQL5 позволяет нам использовать этот мощный язык для эффективного достижения наших целей. В этой статье мы рассмотрим некоторые базовые сведения о Python и расскажем, как его можно интегрировать в MQL5.
      От начального до среднего уровня: Массивы и строки (II) От начального до среднего уровня: Массивы и строки (II)
      В этой статье я покажу, что хотя мы всё еще находимся на очень базовой стадии программирования, мы уже можем реализовать несколько интересных приложений. В данном случае мы создадим довольно простой генератор паролей. Таким образом мы сможем применить некоторые концепции, которые объяснялись до этого. Кроме того, мы рассмотрим, как можно разработать решения для некоторых конкретных проблем.