English
preview
Адаптивный индикатор Malaysian Engulfing (Часть 1): Обнаружение паттернов и валидация ретеста

Адаптивный индикатор Malaysian Engulfing (Часть 1): Обнаружение паттернов и валидация ретеста

MetaTrader 5Примеры |
46 0
Chukwubuikem Okeke
Chukwubuikem Okeke

Введение

Malaysian Engulfing — мощный свечной сигнал, но на практике с ним возникают две проблемы: трейдерам и разработчикам сложно стабильно выявлять действительно «идеальные» свечи поглощения в реальном времени, а сама по себе обнаруженная свеча дает мало прикладного контекста о том, что должно последовать дальше (откат, ретест, инвалидация). В этой статье обе проблемы рассматриваются на платформе MetaTrader 5/MQL5: визуальный паттерн превращается в воспроизводимый процесс, основанный на правилах.

Сначала мы формализуем определение Malaysian Engulfing так, чтобы его можно было однозначно закодировать: тело второй свечи должно полностью доминировать над предыдущим телом (открытие — на уровне или за пределами предыдущего закрытия, закрытие — за пределами предыдущего экстремума). Затем мы преобразуем развитие после паттерна в легкую машину состояний, которая программно отслеживает сетап: определяет зону (high/low), задает уровень инвалидации, ожидает фиксированное окно ретеста (barsRetestRange) и валидирует возвраты с помощью фильтра силы тени (wickThreshold). Результатом являются два взаимодополняющих индикатора MQL5: строгий детектор паттерна (визуальные стрелки) и валидатор ретеста (отрисовка зоны + детерминированные события подтверждения), которые вместе переводят работу от субъективного чтения графика к тестируемому и автоматизируемому процессу.

Концепция Malaysian Engulfing

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

В основе Malaysian Engulfing Concept лежит более строгое определение рыночного намерения. Концепция фокусируется на решительном ценовом смещении, при котором текущая свеча полностью поглощает диапазон предыдущей свечи, сигнализируя о явном сдвиге в потоке ордеров. Точнее, тело текущей свечи должно полностью поглотить тело предыдущей свечи — выйти соответственно за ее high и low, — чтобы диапазон предыдущей свечи был полностью подчинен.

Анатомия идеального поглощения

Понимать логику Malaysian Engulfing Concept необходимо, но сама по себе она остается абстрактной. В реальных рыночных условиях вы интерпретируете не определения — вы читаете цену. Именно здесь многие реализации дают сбой: возникает разрыв между концептуальной ясностью и визуальным распознаванием.

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

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

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

Идеальное бычье поглощение

Рис. 1. Идеальное бычье поглощение

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

Идеальное медвежье поглощение

Рис. 2. Идеальное медвежье поглощение 

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

Обнаружение паттерна

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

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

//+------------------------------------------------------------------+
//|                      Malaysian Engulfing - Pattern Detection.mq5 |
//|                                             © 2026, ChukwuBuikem |
//|                             https://www.mql5.com/en/users/bikeen |
//+------------------------------------------------------------------+
#property copyright "© 2026, ChukwuBuikem"
#property link      "https://www.mql5.com/en/users/bikeen"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrCrimson
#property indicator_label1 "Bearish Engulfing"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrLimeGreen
#property indicator_label2 "Bullish Engulfing"

#define PROG_NAME "Malaysian Engulfing - Pattern Detection"
#define ARROW_OFFSET_FACTOR 0.25
#define OFFSET_MIN 10 * _Point

//--- Indicator buffers and global variables
double bearishEngulfing[];
double bullishEngulfing[];
int start = -1;
double candleRange = EMPTY_VALUE, offSet = EMPTY_VALUE;

//+------------------------------------------------------------------+
//|        Initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   SetIndexBuffer(0, bearishEngulfing);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_ARROW, 234);
   SetIndexBuffer(1, bullishEngulfing);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(1, PLOT_ARROW, 233);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
                const int32_t prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int32_t &spread[])
  {
//---
   start = (prev_calculated == 0) ? 1 : prev_calculated - 1;
   for(int w = start; w < rates_total - 1 && !IsStopped(); w++)
     {
      //--- Compute normalized candle range (high–low)
      candleRange = NormalizeDouble(MathAbs(high[w] - low[w]), _Digits);
      //--- Deriv arrow offset with minimum threshold enforcement
      offSet = MathMax(candleRange * ARROW_OFFSET_FACTOR, OFFSET_MIN);
      //--- Detect bullish engulfing and position arrow below candle
      if(isBullishEngulfing(w, open, high, low, close))
        {
         bullishEngulfing[w] = low[w] - offSet;
        }
      else
        {
         bullishEngulfing[w] = EMPTY_VALUE;
        }
      //--- Detect bearish engulfing and position arrow above candle
      if(isBearishEngulfing(w, open, high, low, close))
        {
         bearishEngulfing[w] = high[w] + offSet;
        }
      else
        {
         bearishEngulfing[w] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Function to detect perfect bullish engulfing                     |
//+------------------------------------------------------------------+
bool isBullishEngulfing(const int index, const double &open[], const double &high[],
                        const double &low[], const double &close[])
  {
//---
   return(close[index - 1] < open[index - 1] && close[index] > open[index]
          && open[index] <= close[index - 1] && close[index] > high[index - 1]);
  }
//+------------------------------------------------------------------+
//| Function to detect perfect bearish engulfing                     |
//+------------------------------------------------------------------+
bool isBearishEngulfing(const int index, const double &open[], const double &high[],
                        const double &low[], const double &close[])
  {
//---
   return(close[index - 1] > open[index - 1] && close[index] < open[index]
          && open[index] >= close[index - 1] && close[index] < low[index - 1]);
  }                                                                     

Malaysian Engulfing — обнаружение паттерна

Пояснение:

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

  • Определения времени компиляции (#define): На верхнем уровне вводится несколько констант для параметризации поведения индикатора.
        ARROW_OFFSET_FACTOR  — управляет тем, насколько далеко сигнальная стрелка смещается от свечи.
        OFFSET_MIN  — задает минимальный визуальный зазор, предотвращая наложение стрелок на маленькие свечи.

  • Глобальные переменные: Мы используем две вспомогательные переменные.

         candleRange — хранит нормализованный диапазон high-low каждой свечи.

         offSet — рассчитанное смещение, используемое для размещения сигнальной стрелки.    

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

  • Вспомогательные функции: Логика обнаружения намеренно вынесена в две функции, isBullishEngulfing() и isBearishEngulfing(), что обеспечивает строгое и согласованное определение поглощения для обоих паттернов.

После добавления индикатора обнаружения паттернов на график найденные сетапы поглощения визуализируются следующим образом:

Индикатор паттерна Malaysian Engulfing

Рис. 3. Индикатор обнаружения паттернов

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

Валидация отката и ретеста

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

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

Реализация

Когда логика валидации определена, следующий шаг — выразить ее в коде с использованием нативных конструкций MQL5.

Директивы препроцессора

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

//+------------------------------------------------------------------+
//|                      Malaysian Engulfing - Retest Validation.mq5 |
//|                                             © 2026, ChukwuBuikem |
//|                             https://www.mql5.com/en/users/bikeen |
//+------------------------------------------------------------------+
#property copyright "© 2026, ChukwuBuikem"
#property link      "https://www.mql5.com/en/users/bikeen"
#property version  "1.50"
#property indicator_chart_window
#property indicator_plots 0

#include <ChartObjects\ChartObjectsShapes.mqh>

#define PROG_NAME "Malaysian Engulfing - Retest Validation"
#define ZONE_BULL PROG_NAME + "BullishEngulfing"
#define ZONE_BEAR PROG_NAME + "BearishEngulfing"

Перечисление состояний

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

//--- State Enumeration
enum ENUM_SYSTEM_STATE
  {
   SEARCH_STATE = 0, // Discovery Phase
   FOUND_STATE = 1   // Validation Phase
  };

Структура данных

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

//--- Data Structure
struct st_Engulfer
  {
   ENUM_SYSTEM_STATE state;
   datetime          time, retestTime;
   double            high, low;
   //--- Constructor
                     st_Engulfer(): state(SEARCH_STATE), time(LONG_MIN),
                                    retestTime(LONG_MIN), high(EMPTY_VALUE), low(EMPTY_VALUE) {}
  };
  1. Поля:

  • state: отслеживает текущую фазу системы с помощью ENUM_SYSTEM_STATE. Определяет, находится ли структура в поиске паттерна или валидирует ретест.
  • time: хранит временную метку момента, когда паттерн поглощения был впервые идентифицирован.
  • retestTime: фиксирует момент, когда цена возвращается в зону для валидации, отмечая подтвержденный ретест.
  • high и low: задают ценовые границы зоны поглощения, формируя область для мониторинга.

          2. Конструктор: конструктор инициализирует все поля безопасными значениями по умолчанию. Состояние начинается с SEARCH_STATE, поля времени устанавливаются в LONG_MIN, что означает «валидного времени пока нет», а ценовые уровни — в EMPTY_VALUE, показывая, что зона еще не определена. Это гарантирует, что структура начинает работу из чистого и предсказуемого состояния до присвоения каких-либо данных.

    Настраиваемые параметры

    Эти входные параметры позволяют изменять поведение и внешний вид индикатора без правки кода.

    //--- Configurable Parameters
    input int   barsRetestRange  = 10;        // Retest window (in bars)
    input color bullishZoneColor = clrGreen;  // Bullish retest zone color
    input color bearishZoneColor = clrRed;    // Bearish retest zone color
    input int   wickThreshold    = 35;        // Minimum wick rati0 (%)
    • barsRetestRange: определяет, сколько свечей система будет ждать валидного ретеста после обнаружения паттерна поглощения.
    • bullishZoneColor: задает цвет, используемый для отрисовки бычьих зон ретеста на графике.
    • bearishZoneColor: задает цвет, используемый для отрисовки медвежьих зон ретеста.
    • wickThreshold: задает минимальное соотношение тени к телу (в процентах), необходимое для квалификации свечи, помогая отфильтровывать слабые паттерны.

    Глобальные переменные

    Эти переменные поддерживают состояние и объекты, которые сохраняются на протяжении выполнения индикатора.

    //--- Global variables
    int start = -1;
    CChartObjectRectangle rect;
    st_Engulfer bullishEngulfer, bearishEngulfer;
    • start: целочисленная переменная, используемая как начальный индекс в OnCalculate() для прохода по ценовым свечам; инициализируется значением -1, чтобы показать, что позже она будет задана на основе логики расчета.
    • rect: экземпляр CChartObjectRectangle, используемый для отрисовки и управления прямоугольником на графике при визуализации зон или паттернов.
    • bullishEngulfer и bearishEngulfer: экземпляры структуры st_Engulfer, используемые для независимого хранения и отслеживания бычьих и медвежьих паттернов поглощения по мере их прохождения через обнаружение и валидацию.

    Вспомогательные функции

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

    • Обнаружение паттерна

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

    //+------------------------------------------------------------------+
    //|       Perfect bullish engulfing detection                        |
    //+------------------------------------------------------------------+
    bool isBullishEngulfing(const int index, const double &open[], const double &high[],
                            const double &low[], const double &close[])
      {
    //---
       return(close[index - 1] < open[index - 1] && close[index] > open[index]
              && open[index] <= close[index - 1] && close[index] > high[index - 1]);
      }
    //+------------------------------------------------------------------+
    //|       Perfect bearish engulfing detection                        |
    //+------------------------------------------------------------------+
    bool isBearishEngulfing(const int index, const double &open[], const double &high[],
                            const double &low[], const double &close[])
      {
    //---
       return(close[index - 1] > open[index - 1] && close[index] < open[index]
              && open[index] >= close[index - 1] && close[index] < low[index - 1]);
      }

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

    • Валидация соотношения теней свечи

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

    //+------------------------------------------------------------------+
    //|             Upper wick ratio validation                          |
    //+------------------------------------------------------------------+
    int upperWickRatio(const int index, const double &open[], const double &high[],
                       const double &low[], const double &close[])
      {
    //---
       double candleRange = high[index] - low[index];
       double ratio = (high[index] - MathMax(open[index], close[index])) / candleRange;
       ratio = NormalizeDouble(ratio, 2);
    
       return (int)(ratio * 100);
      }
    //+------------------------------------------------------------------+
    //|                Lower wick ratio validation                       |
    //+------------------------------------------------------------------+
    int lowerWickRatio(const int index, const double &open[], const double &high[],
                       const double &low[], const double &close[])
      {
    //---
       double candleRange = high[index] - low[index];
       double ratio = (MathMin(open[index], close[index]) - low[index]) / candleRange;
       ratio = NormalizeDouble(ratio, 2);
    
       return (int)(ratio * 100);
      }

    Применяя порог (например, wickThreshold), система может обеспечить более строгий контроль качества обнаруженных паттернов.

    • Создание зоны
    После того как валидный паттерн проходит структурные проверки, финальным шагом становится визуализация.       
    //+------------------------------------------------------------------+
    //|                   Zone creation                                  |
    //+------------------------------------------------------------------+
    void createZone(const string objName, const datetime time1, const double price1,
                    const datetime time2, const double price2, const color clr)
      {
    //---
       if(rect.Create(0, objName, 0, time1, price1, time2, price2))
         {
          rect.Fill(true);
          rect.Color(clr);
          rect.Selectable(false);
          rect.Background(true);
          ChartRedraw();
         }
      }
    

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

    Инициализация и очистка

    • OnInit(): эта функция выполняет инициализацию при загрузке индикатора. В текущем виде она зарезервирована для адаптивного движка состояний, который будет представлен и расширен в следующих частях.
    //+------------------------------------------------------------------+
    //|        Initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit(void)
      {
    //--- Adaptive engine
    
       return(INIT_SUCCEEDED);
      }
    • OnDeinit(): эта функция выполняется при удалении или перезагрузке индикатора. Она очищает все графические объекты, связанные с программой через PROG_NAME, обеспечивая чистое состояние графика, а затем обновляет отображение.

    //+------------------------------------------------------------------+
    //|        Deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int32_t reason)
      {
    //--- Clear chart
       ObjectsDeleteAll(0, PROG_NAME);
       ChartRedraw();
      }

    Основной движок (OnCalculate())

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

    //+------------------------------------------------------------------+
    //|             Core iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int32_t rates_total,
                    const int32_t prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int32_t &spread[])
      {
    //---
       if(prev_calculated != rates_total && prev_calculated > 0)
         {
          start = (prev_calculated == 0) ? 1 : prev_calculated - 1;
          for(int w = start; w < rates_total - 1 && !IsStopped(); w++)
            {
             //--- Search state
             if(bullishEngulfer.state == SEARCH_STATE)
               {
                if(isBullishEngulfing(w, open, high, low, close))
                  {
                   bullishEngulfer.state = FOUND_STATE;
                   bullishEngulfer.time = time[w - 1];
                   bullishEngulfer.high = high[w - 1];
                   bullishEngulfer.low = low[w - 1];
                   bullishEngulfer.retestTime = time[w] + (PeriodSeconds() * barsRetestRange);// Set retest time window
                   return rates_total;
                  }
               }
             if(bearishEngulfer.state == SEARCH_STATE)
               {
                if(isBearishEngulfing(w, open, high, low, close))
                  {
                   bearishEngulfer.state = FOUND_STATE;
                   bearishEngulfer.time = time[w - 1];
                   bearishEngulfer.high = high[w - 1];
                   bearishEngulfer.low = low[w - 1];
                   bearishEngulfer.retestTime = time[w] + (PeriodSeconds() * barsRetestRange);// Set retest time window
                   return rates_total;
                  }
               }
             //--- Found state
             if(bullishEngulfer.state == FOUND_STATE)
               {
                if(time[w] <= bullishEngulfer.retestTime)
                  {
                   if(close[w] < bullishEngulfer.low)
                     {
                      //--- Broken?  reset state
                      bullishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
                   //--- Validate retest
                   if((low[w] <= bullishEngulfer.high && close[w] > bullishEngulfer.high &&
                       lowerWickRatio(w, open, high, low, close) >= wickThreshold) ||
                      (low[w] <= bullishEngulfer.low && close[w] > bullishEngulfer.low &&
                       lowerWickRatio(w, open, high, low, close) >= wickThreshold))
                     {
                      //--- Draw zone and reset state
                      createZone(ZONE_BULL, bullishEngulfer.time, bullishEngulfer.high,
                                 time[w] + (PeriodSeconds() * 2), bullishEngulfer.low, bullishZoneColor);
                      bullishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
                  }
                else
                   if(time[w] > bullishEngulfer.retestTime)
                     {
                      //--- Outside time range
                      bullishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
               }
             if(bearishEngulfer.state == FOUND_STATE)
               {
                if(time[w] <= bearishEngulfer.retestTime)
                  {
                   if(close[w] > bearishEngulfer.high)
                     {
                      //--- Broken?  reset state
                      bearishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
                   //--- Validate retest
                   if((high[w] >= bearishEngulfer.low && close[w] < bearishEngulfer.low &&
                       upperWickRatio(w, open, high, low, close) >= wickThreshold)
                      || (high[w] >= bearishEngulfer.high && close[w] < bearishEngulfer.high &&
                          upperWickRatio(w, open, high, low, close) >= wickThreshold))
                     {
                      //--- Draw zone and reset state
                      createZone(ZONE_BEAR, bearishEngulfer.time, bearishEngulfer.high,
                                 time[w] + (PeriodSeconds() * 2), bearishEngulfer.low, bearishZoneColor);
                      bearishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
                  }
                else
                   if(time[w] > bearishEngulfer.retestTime)
                     {
                      //--- Outside time range
                      bearishEngulfer.state = SEARCH_STATE;
                      return rates_total;
                     }
               }
            }
         }
       return(rates_total);
      }
    • Состояние поиска: В этом начальном состоянии система сканирует валидные паттерны поглощения с помощью isBullishEngulfing() и isBearishEngulfing(). Когда обнаруживается условие бычьего или медвежьего поглощения, соответствующая структура заполняется ключевыми свойствами, такими как time, high, low, а расчетное окно ретеста задается с использованием barsRetestRange. Затем состояние переходит в FOUND_STATE.
        
    • Состояние найденного паттерна: После нахождения паттерна система отслеживает поведение цены в пределах заданного окна ретеста.
    1. Если цена пробивает уровень инвалидации, сетап отбрасывается, а система сбрасывается.
    2. Если цена возвращается и успешно взаимодействует с зоной при соблюдении условий силы тени, подтверждается валидный ретест.
    3. После подтверждения зона отрисовывается на графике, а состояние сбрасывается обратно в SEARCH_STATE.
    4. Если временное окно истекает без валидации, сетап также сбрасывается.

    Тестирование программы

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

    Live Chart

    Рис. 5. Тест валидации ретеста

    Заключение

    Мы преобразовали концепцию Malaysian Engulfing из неоднозначного графического сигнала в детерминированный инструментарий, совместимый с MetaTrader 5 и состоящий из двух модулей:

    • Строгий детектор «идеального поглощения», который отмечает только те бычьи/медвежьи свечи, которые соответствуют точным правилам доминирования тела.
    • Валидатор ретеста, управляемый состояниями: он создает зону мониторинга, применяет инвалидацию, ограничивает валидацию настраиваемым окном в барах (barsRetestRange) и подтверждает ретесты с помощью фильтра соотношения теней (wickThreshold).
    Архитектура намеренно отделяет обнаружение от валидации сценария, чтобы каждый компонент оставался простым, тестируемым и переиспользуемым как строительный блок советника (EA).

    Такой дизайн устраняет недостатки, выявленные в анализе: он задает платформу и входные параметры, заменяет субъективную интерпретацию формальными правилами и явными переходами состояний (SEARCH → FOUND → reset/confirm), а также создает конкретные артефакты на уровне графика (стрелки и прямоугольники) и детерминированные события, которые можно тестировать на истории и автоматизировать. Следующие шаги включают добавление адаптивного слоя для исторической настройки диапазонов свечей ретеста и интеграцию модулей в полуавтоматическую или полностью автоматическую стратегию для устойчивости в разных рыночных режимах.

    Исходные файлы приложены для тестирования и расширения.

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

    Торговые инструменты MQL5 (Часть 25): Расширяем поддержку нескольких распределений с интерактивным переключением Торговые инструменты MQL5 (Часть 25): Расширяем поддержку нескольких распределений с интерактивным переключением
    В этой статье мы расширим инструмент построения графиков на MQL5 для поддержки семнадцати статистических распределений с циклическим перебором распределений с помощью значка переключения в заголовке. Мы добавим загрузку данных для каждого типа, дискретное и непрерывное вычисление гистограмм и теоретические функции распределения вероятностей/плотности для каждой модели, а также динамические заголовки, метки осей и панели параметров, которые автоматически адаптируются. Результат позволяет накладывать кривые разных распределений на данные одной и той же выборки и сравнивать качество соответствия моделей из разных семейств распределений.
    Архитектура машинного обучения для MetaTrader 5 (Часть 15): Как калибровать уровни тейк-профита и стоп-лосса по синтетическим данным Архитектура машинного обучения для MetaTrader 5 (Часть 15): Как калибровать уровни тейк-профита и стоп-лосса по синтетическим данным
    В статье применяется оптимальное торговое правило из главы 13 AFML для задания уровней тейк-профита и стоп-лосса без внутривыборочной калибровки. Мы моделируем P&L после входа дискретным процессом Орнштейна–Уленбека, выполняем поиск по 100 000 траекториям и используем Python, multiprocessing и параллельное ядро Numba с декоратором @njit (в 242 раза быстрее). Результат — оптимальная пара (PT, SL) для трех спецификаций прогноза с ограничением по дневному лимиту убытка проп-фирмы.
    Возможности Мастера MQL5, которые вам нужно знать (Часть 76): Использование паттернов Awesome Oscillator и каналов конвертов с обучением с учителем Возможности Мастера MQL5, которые вам нужно знать (Часть 76): Использование паттернов Awesome Oscillator и каналов конвертов с обучением с учителем
    В продолжение нашей предыдущей статьи о паре индикаторов Awesome Oscillator и каналов конвертов (Envelope Channels), мы рассмотрим, как эту пару можно улучшить с помощью обучения с учителем. Awesome Oscillator и канал конвертов — это взаимодополняющее сочетание инструментов, позволяющих выявлять тренды и создавать уровни поддержки/сопротивления. Наш подход к обучению с учителем представляет собой сверточную нейронную сеть (CNN), которая использует ядро скалярного произведения (Dot Product Kernel) с механизмом внимания во времени (Cross-Time-Attention) для определения размеров своих ядер и каналов. Как обычно, это делается в пользовательском файле класса сигналов (signal class), который взаимодействует с Мастером MQL5 для сборки советника.
    Торговые инструменты MQL5 (Часть 24): Улучшение восприятия глубины с помощью 3D-кривых, режима панорамирования и навигации через виджет ViewCube Торговые инструменты MQL5 (Часть 24): Улучшение восприятия глубины с помощью 3D-кривых, режима панорамирования и навигации через виджет ViewCube
    В этой статье мы улучшим инструмент построения 3D-графиков биномиального распределения в MQL5, добавим сегментированную 3D-кривую для улучшения восприятия глубины функции массы вероятности. Также интегрируем режим панорамирования для смещения целевой точки камеры и реализуем интерактивный куб обзора с зонами наведения курсора и анимацией для обеспечения быстрой смены ориентации. Мы добавим кликабельные подзоны на кубе обзора для граней, ребер и углов, чтобы анимировать переходы камеры к стандартным видам, сохраняя при этом переключаемые 2D/3D режимы, обновления в реальном времени и настраиваемые параметры для иммерсивного вероятностного анализа в торговле.