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

Построение модели ограничения тренда свечей (Часть 7): Улучшаем нашу модель для разработки советника

MetaTrader 5Трейдинг | 24 января 2025, 13:28
431 0
Clemence Benjamin
Clemence Benjamin

Оглавление:


        Введение

        На основе имеющихся индикаторов можно разработать советника. В этой статье я упомяну два способа:

        1. Включение условий индикатора в алгоритм советника - достаточно эффективное и очень быстрое решение даже в тестере стратегий. В этом случае советник не требует отдельного индикатора.
        2. Другой подход — подготовка алгоритма советника, учитывающего буферы индикатора. Если буфер имеет значение True или False, советник запрограммирован на определенный ответ. Для работы системы на платформе MetaTrader 5 необходимы два файла: советник и индикатор должны находиться в определенных каталогах в пути платформы. Опубликовать советник, работающий на основе пользовательского индикатора, сложно, поскольку система проверки не обнаружит ваш индикатор при попытке публикации в сообществе MQL5. Я пробовал этот метод и столкнулся с ошибками, поэтому мне удалось запустить робота только на своем компьютере без публикации.

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

        В шестой части серии, наша последняя версия Trend Constraint V1.08 объединила две основные интеграции в одну программу. Теперь мы можем легко получить доступ к сигналам в Telegram и WhatsApp. Однако есть несколько вопросов:

        • Да, теперь мы получаем сигналы, но являются ли они лучшими?
        • Мы можем совершать сделки, но когда нам следует выходить?

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

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

        Мы стремимся разработать индикатор, отображающий концепции входа и выхода, позволяя трейдерам отслеживать их вручную. Если стратегию можно реализовать вручную, ее можно и автоматизировать. Коэффициент риска и прибыли (risk-reward ratio, RRR) помогает трейдерам оценить потенциальную прибыльность сделки по отношению к ее риску. Мы обсудим, как определить потенциальные уровни выхода, включив в наш индикатор новые функции, в том числе и формулу для RRR, которая выглядит следующим образом:

         


        Соотношение риска и прибыли, формула.

        где;

        RRR - соотношение риска и прибыли;
        Potential Loss - сумма, которую вы можете потерять, если сделка или инвестиция пойдет не в вашу пользу;   
        Potential Gain - сумма, которую вы ожидаете получить, если сделка или инвестиция будут развиваться в вашу пользу.

        Рассмотрим пример:

        Предположим, вы собираетесь купить акцию стоимостью USD 50. Вы устанавливаете стоп-лосс на уровне USD 48 (что указывает на потенциальный убыток в размере USD 2 за акцию) и устанавливаете целевую цену в размере USD 56 (что указывает на потенциальный доход в размере USD 6 за акцию). Рассчитаем соотношение риска и прибыли для акции.

        • Potential Loss (потенциальный убыток): USD 50 (цена входа) - USD 48 (стоп-лосс) = USD 2
        • Potential Gain (потенциальная прибыль): USD 56 (целевая цена) - USD 50 (цена входа) = USD 6

        Подставляем значения в формулу: Коэффициент риска-прибыли = 1/3

        Если говорить более подробно, это означает, что за каждый USD 1 риска вы ожидаете получить USD 3. Более низкое соотношение (например, 1:1 или меньше) указывает на больший риск по сравнению с потенциальной прибылью, тогда как более высокое соотношение предполагает более благоприятное соотношение риска и прибыли.

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

        Соотношение риска и прибыли


        Давайте обсудим это подробнее в следующих разделах статьи. Постараемся сосредоточиться на:

        1. Понимании важности управления рисками в алгоритмической торговле.
        2. Внедрении соотношений риска и доходности и их математических основ.
        3. Разработке динамических стратегий выхода для оптимального управления торговлей.
        4. Улучшении визуальных индикаторов для принятия более обоснованных торговых решений.
        5. Тестировании и проверке индикатора для применения на практике.


        Выявление ограничений текущей системы

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

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


        График

        Ниже приведено изображение графика индекса Boom 500. Я обрисую проблемы так, как я их вижу, на графике:


        Boom  500 Index M1


        • На графике (A) представляет собой пиковую свечу. В этом случае стрелка отображается под свечой, хотя она должна быть над свечой. Эта проблема возникает из-за того, что стрелка обозначает зону перекупленности RSI, которая возникает только после закрытия свечи.
        • Функция DRAW_LINE позволяет нам выявлять новые тренды, наблюдая за изменениями цвета линии. Мы хотим, чтобы он давал возможность свинговой торговли, поскольку, смена направления рынка обычно сопровождается значительным движением.
        • Синие стрелки на покупку указывают на потенциальное продолжение тренда, а в данном случае - еще и на зону перепроданности RSI в преобладающем восходящем тренде. Не забывайте, что мы ограничиваемся свечой D1 и настроением рынка. Хотя экстремальные уровни индекса относительной силы (RSI) могут быть хороши для входов, они обычно указывают на рыночную зону, а не на гарантированный разворот. Тренд может продолжиться независимо от уровней RSI. Я предлагаю повторно ввести пересечение скользящих средних для других входов, чтобы максимизировать прибыль, поскольку это обычно хорошо согласуется с ценовым действием.
        • Я также вижу преимущество использования прямоугольников для отображения соотношений риска и прибыли, которые будут отображаться, когда индикатор подает сигналы на вход, как упоминалось во введении. В новой версии необходимо точно очертить прямоугольники, чтобы правильно представить риск и выгоду. Эта идея отражена на скриншотах графиков ниже: первый — EURUSD, второй — Boom 500, соответственно.

        EURUSD, M1: Euro vs US Dollar

        EURUSD


        Boom 500 Index, M1:

        BOOM 500 INDEX M1

        На этих графиках мы можем наглядно увидеть новый подход. Код индикатора должен точно отражать то, что было здесь продемонстрировано вручную. По мнению ряда технических аналитиков и трейдеров, точки разворота (pivots) традиционно считаются подходящими уровнями тейк-профита и стоп-лосса. Таким образом, идея соотношения риска и прибыли 1:3 может оказаться неприменимой в сценариях, подобных показанным на графике, где иногда риск намного превышает потенциальную прибыль.

        Подводя итог:

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

        В следующем разделе мы рассмотрим наиболее важные участки кода.


        Код

        Вот фрагмент, в который мы можем внести изменения:
        #property indicator_type1 DRAW_ARROW
        #property indicator_width1 5
        #property indicator_color1 0xFF3C00
        #property indicator_label1 "Buy" // We are going to change it to "Buy Zone"
        
        #property indicator_type2 DRAW_ARROW
        #property indicator_width2 5
        #property indicator_color2 0x0000FF
        #property indicator_label2 "Sell"// We are going to change it to "Sell Zone"


        В этом разделе программы мы фиксируем положение стрелки на свече. Раздел включает в себя функцию итерации буфера 2. Индикатор должен отображаться на максимуме свечи, поэтому мы изменим значение с Low[i] на High[i];

        //Indicator Buffer 2 // We are going to set the indicator to display at candlestick high by changing the highlighted text to High[i]
              if(RSI[i] > Overbought
              && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value
              && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              && MA[i] < MA2[i] //Moving Average < Moving Average
              && MA3[i] < MA4[i] //Moving Average < Moving Average
              )
        
                {
                 Buffer2[i] = Low[i]; //Set indicator value at Candlestick Low // change to High[i]
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Zone"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer2[i] = EMPTY_VALUE;
                }



        Пересечение скользящих средних

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

        Следуя логике нашей программы, начнем с определения ее свойств. Мы недавно внедрили Sell Zone (зону продажи) и Buy Zone (зону покупки) для индикатора RSI. Они будут играть роль новых сигналов Sell (продажа) и Buy (покупка) для индикатора.

        #property indicator_type6 DRAW_ARROW
        #property indicator_width6 1
        #property indicator_color6 0x0000FF
        #property indicator_label6 "Sell"
        
        #property indicator_type7 DRAW_ARROW
        #property indicator_width7 1
        #property indicator_color7 0xFFAA00
        #property indicator_label7 "Buy"

        Нам необходимо задать входные данные для настройки, чтобы оптимизировать получение наилучших сигналов. Будем использовать скользящие средние со значениями 7 и 21.

        input int Entry_MA_fast = 7 ;
        input int Entry_MA_slow = 21 ;

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

        SetIndexBuffer(5, Buffer6);
           PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(5, PLOT_ARROW, 242);
           SetIndexBuffer(6, Buffer7);
           PlotIndexSetDouble(6, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(6, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(6, PLOT_ARROW, 241);


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

        //Indicator Buffer 6
              if(MA8[i] < MA9[i]
              && MA8[i+1] > MA9[i+1] //Moving Average crosses below Moving Average
              && Open2[i] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              )
                {
                 Buffer6[i] = High[i]; //Set indicator value at Candlestick High
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer6[i] = EMPTY_VALUE;
                }
              //Indicator Buffer 7
              if(MA8[i] > MA9[i]
              && MA8[i+1] < MA9[i+1] //Moving Average crosses above Moving Average
              && Open2[i] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close
              )
                {
                 Buffer7[i] = Low[i]; //Set indicator value at Candlestick Low
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer7[i] = EMPTY_VALUE;
                }


        Отрисовка прямоугольников

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

        Ниже приведена логика до фактической реализации:

         Быстрая скользящая средняя пересекает медленную сверху вниз: 

        При выполнении условий буфера 7, прямоугольник должен быть помещен ниже минимума текущей свечи.
          • Ширина прямоугольника должна быть не менее 5 баров.
          • Высота прямоугольника - X пипсов вниз.
          • Прямоугольник должен быть зеленым.
          • Поверх зеленого прямоугольника, при выполнении условий буфера 7, помещаем красный прямоугольник, высота которого составляет 1/3 высоты зеленого.
          • Все параметры должны быть настраиваемыми.

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

        Параметры следующие:

          • Ширина прямоугольника - не менее 5 столбцов.
          • Высота прямоугольника - X пипсов вверх.
          • Прямоугольник должен быть зеленым.
          • Под зеленым прямоугольником, при выполнении условий буфера 6, помещаем красный прямоугольник, высота которого составляет 1/3 высоты зеленого.
          • Все параметры должны быть настраиваемыми.

        Во-первых, нам нужна возможность ввода параметров для настройки. Ниже приведен необходимый отрывок кода:

        //--- new inputs for rectangles
        input int RectWidth = 5;                 // Width of the rectangle in bars
        input int RectHeightPointsBuy = 50;      // Height of the profit rectangle in points for Buy
        input int RectHeightPointsSell = 50;     // Height of the profit rectangle in points for Sell
        input color ProfitRectColor = clrGreen;  // Color of the profit rectangle
        input color RiskRectColor = clrRed;      // Color of the risk rectangle
        
        По умолчанию я установил значение 50, но его можно изменить в соответствии с вашими потребностями и целями по прибыли. Я также включил функцию, которая автоматически регулирует высоту прямоугольника риска в пропорции к целевой прибыли. Это важно, поскольку позволяет индикатору адаптироваться к любым изменениям цвета фона. Например, если вы используете зеленый фон графике, зеленый прямоугольник станет невидимым, в то время как желтый фон обеспечит лучшую контрастность. Настройка имеет решающее значение для обеспечения полного контроля над нашими инструментами.


        Теперь давайте рассмотрим функцию OnCalculate, чтобы увидеть работу цикла.

        if(RSI[i] > Overbought) {
                if(close[i] > MA[i])
                    Buffer6[i] = close[i] - pips * myPoint;
            }
        
            if(RSI[i] < Oversold) {
                if(close[i] < MA[i])
                    Buffer7[i] = close[i] + pips * myPoint;
            }
        
            if(Buffer6[i] > 0) {
                Buffer1[i] = close[i] - pips * myPoint;
                Buffer3[i] = close[i] - pips * myPoint;
                if (Buffer6[i - 1] < 0) {
                    myAlert("indicator", "Sell Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Sell Signal Detected!");
        
                    // Create profit rectangle for Sell
                    double highProfitRect = close[i];
                    double lowProfitRect = close[i] - RectHeightPointsSell * myPoint;
                    string profitRectName = "SellProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Sell
                    double highRiskRect = close[i];
                    double lowRiskRect = close[i] + (RectHeightPointsSell / 3) * myPoint;
                    string riskRectName = "SellRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            }
        
            if(Buffer7[i] > 0) {
                Buffer2[i] = close[i] + pips * myPoint;
                Buffer4[i] = close[i] + pips * myPoint;
                if (Buffer7[i - 1] < 0) {
                    myAlert("indicator", "Buy Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Buy Signal Detected!");
        
                    // Create profit rectangle for Buy
                    double highProfitRect = close[i] + RectHeightPointsBuy * myPoint;
                    double lowProfitRect = close[i];
                    string profitRectName = "BuyProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Buy
                    double highRiskRect = close[i] - (RectHeightPointsBuy / 3) * myPoint;
                    double lowRiskRect = close[i];
                    string riskRectName = "BuyRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            
        

        Здесь происходит создание прямоугольников:

        Прямоугольники прибыли:

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


        Прямоугольники риска:

          • Для сигналов на продажу красный прямоугольник отображается вверх от цены закрытия, его высота составляет 1/3 прямоугольника прибыли.
          • Для сигналов на покупку красный прямоугольник отображается вниз от цены закрытия, его высота составляет 1/3 прямоугольника прибыли.

        Теперь давайте кратко рассмотрим новые вводимые функции;

        • ObjectFind: Функция, используемая для проверки того, существует ли уже объект с определенным именем на графике.

        int ObjectFind(
           long   chart_id,   // Chart ID (0 means the current chart)
           string name        // Name of the object to search for
        );
        


        • ObjectCreate: Функция создания нового графического объекта на графике. Можно указать тип объекта (например, прямоугольник, линия тренда и т. д.).

        bool ObjectCreate(
           long    chart_id,   // Chart ID (0 means the current chart)
           string  name,       // Name of the object to create
           ENUM_OBJECT type,   // Type of the object (e.g., OBJ_RECTANGLE, OBJ_TREND, etc.)
           int     sub_window, // Number of the subwindow (0 means the main chart window)
           datetime time1,     // First coordinate time
           double  price1,     // First coordinate price
           ...                 // Additional coordinates depending on the object type
        );
        


        • ObjectSetInteger: Функция установки целочисленных свойств графического объекта, таких как цвет, стиль и ширина.

        bool ObjectSetInteger(
           long   chart_id,     // Chart ID (0 means the current chart)
           string name,         // Name of the object
           int    prop_id,      // ID of the property to set (e.g., OBJPROP_COLOR, OBJPROP_STYLE, etc.)
           long   value         // Value of the property
        );
        

        Функция возвращает логическое значение;

        • true: Если свойство успешно установлено.
        • false: Если установка свойства не удалась.

        Подробное описание функций можно найти в документации MQL5


        Точки выхода

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


        Подход следующий:

        Чтобы изменить программу для вставки ценовых линий вдоль прямоугольников, мы добавим три линии на определенных уровнях цен:

        • цена сигнала (цена закрытия сигнала), 
        • целевая прибыль, 
        • и целевой показатель риска. 

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

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

        input double profitPoints = 60; // Points for profit target
        input double riskPoints = 20;   // Points for risk target
        

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

        void CreatePriceLine(string name, color lineColor, double price, datetime time) {
            if (ObjectFind(0, name) == -1) {
                ObjectCreate(0, name, OBJ_HLINE, 0, time, price);
                ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
                ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
            } else {
                ObjectMove(0, name, 0, time, price);
            }
        }
        
        void PlaceSignalLines(double signalPrice, double profitTarget, double riskTarget, datetime time) {
            CreatePriceLine("SignalPriceLine", clrBlue, signalPrice, time);
            CreatePriceLine("ProfitTargetLine", clrGreen, profitTarget, time);
            CreatePriceLine("RiskTargetLine", clrRed, riskTarget, time);
        }
        


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

        void CheckSignalAndPlaceLines() {
            for (int i = rates_total - 2; i >= 0; i--) {
                if (Buffer6[i] != 0.0) { // Buy Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice + profitPoints * Point;
                    double riskTarget = signalPrice - riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Buy Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
                if (Buffer7[i] != 0.0) { // Sell Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice - profitPoints * Point;
                    double riskTarget = signalPrice + riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Sell Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
            }
        }
        


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

        1. Мы усовершенствовали Trend Constraint V1.08 и добавили пересечение скользящих средних для сигналов входа.
        2. Мы обсудили включение прямоугольников для представления зон риска и прибыли.
        3. Кроме того, мы рассмотрели важность точек выхода в нашей программе.

        По мере дальнейшего усовершенствования программы мы создали три версии. Теперь у нас есть Trend Constraint V1.09, V1.10 и V1.11. Теперь оценим ее эффективность и результаты теста.


        Тестирование и проверка

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


        Запуск Trend Constraint V1.09.


        Результат профилирования тестера:

        Профилировщик MetaEditor


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


        Ошибки


        После изучения ошибок стало легче проанализировать программу и определить, чего не хватает. После добавления пересечения скользящих средних также необходимо было объявить MA8, MA9, MA_handle8 и MA_handle9 для новых буферов наряду с существующими. В приведенном ниже коде показаны сделанные объявления. Выделенные строки приводили к ошибкам.

        int RSI_handle;
        double RSI[];
        double Open[];
        double Close[];
        int MA_handle;
        double MA[];
        int MA_handle2;
        double MA2[];
        int MA_handle3;
        double MA3[];
        int MA_handle4;
        double MA4[];
        double Low[];
        double High[];
        int MA_handle5;
        double MA5[];
        int MA_handle6;
        double MA6[];
        int MA_handle7;
        double MA7[];
        int MA_handle8;
        double MA8[];
        int MA_handle9;
        double MA9[];
        double Open2[];

        После добавления выделенных объявлений программа успешно скомпилировалась.

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

        В версии 1.11 обнаружены ошибки компиляции. Я поделюсь всем процессом решения в следующей статье.

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

        Буфер Описание
        Буфер 1 Используется для определения того, пересекает ли RSI фиксированное значение (перекупленность) снизу вверх, текущая цена открытия больше или равна цене закрытия определенного бара, две скользящие средние (MA и MA3) больше соответствующих им сравнительных скользящих средних (MA2 и MA4). На графике отмечается Buy Zone (зона покупки).
        Буфер 2
        Используется для определения того, пересекает ли RSI фиксированное значение (перекупленность) снизу вверх, текущая цена открытия ниже или равна цене закрытия определенного бара, две скользящие средние (MA и MA3) меньше соответствующих им сравнительных скользящих средних (MA2 и MA4). На графике отмечается Sell Zone (зона продажи).
        Буфер 3
        Используется для определения того, пересекает ли средняя MA5 среднюю MA6 снизу вверх. На графике отмечается Buy Swing (покупка на колебании).
        Буфер 4
        Используется для определения того, пересекает ли средняя MA5 среднюю MA6 сверху вниз. На графике отмечается Sell Swing (продажа на колебании).
        Буфер 5
        Отслеживает, превышает ли средняя MA3 среднюю MA7.
        Буфер 6
        Используется для определения того, пересекает ли средняя MA8 среднюю MA9 сверху вниз. На графике отмечается Sell (продажа).
        Буфер 7
        Используется для определения того, пересекает ли средняя MA8 среднюю MA9 снизу вверх. На графике отмечается Buy (покупка).
        Буфер 8  Отслеживает, является ли средняя MA3 меньше средней MA7.


        Заключение

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

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

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

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

        Имя файла Описание
        Trend Constraint V1.09 Усовершенствованная программа с сигналами пересечения скользящих средних.
        Trend Constraint V1.10 Усовершенствованная программа с функциями отрисовки объектов. Пока на стадии отладки.


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

        Прикрепленные файлы |
        Нейросети в трейдинге: Контекстно-зависимое обучение, дополненное памятью (Окончание) Нейросети в трейдинге: Контекстно-зависимое обучение, дополненное памятью (Окончание)
        Мы завершаем реализацию фреймворка MacroHFT для высокочастотной торговли криптовалютами, который использует контекстно-зависимое обучение с подкреплением и памятью для адаптации к динамичным рыночным условиям. И в завершении данной статьи будет проведено тестирование реализованных подходов, на реальных исторических данных, для оценки их эффективности.
        Визуальная оценка и корректировка торговли в MetaTrader 5 Визуальная оценка и корректировка торговли в MetaTrader 5
        В тестере стратегий можно не только оптимизировать параметры торгового робота. Мы покажем, как оценить постфактум проторгованную историю своего счёта и внести корректировки в торговлю в тестере, изменяя размеры стоп-приказов открываемых позиций.
        Машинное обучение и Data Science (Часть 26): Решающая битва в прогнозирование временных рядов — LSTM против GRU Машинное обучение и Data Science (Часть 26): Решающая битва в прогнозирование временных рядов — LSTM против GRU
        В предыдущей статье мы рассмотрели простую рекуррентную нейронную сеть, которая, несмотря на свою неспособность понимать долгосрочные зависимости в данных, смогла разработать прибыльную стратегию. В этой статье мы поговорим о долгой кратковременной памяти (Long-Short Term Memoryю LSTM) и об управляемом рекуррентном блоке (Gated Recurrent Unit, GRU). Эти два подхода были разработаны для преодоления недостатков простой рекуррентной нейронной сети.
        От начального до среднего уровня: Операторы От начального до среднего уровня: Операторы
        В этой статье мы рассмотрим основных операторов. Хотя тема проста для понимания, есть определенные моменты, которые имеют большое значение, когда речь идет о включении математических выражений в формат кода. Без адекватного понимания этих деталей, программисты с небольшим опытом или вообще без него в итоге отказываются от попыток создать собственных решений.