English 中文 Deutsch 日本語
preview
Разработка инструментария для анализа движения цен (Часть 26): Инструмент для работы с несколькими паттернами – пин-баром, паттернами поглощения и дивергенцией RSI

Разработка инструментария для анализа движения цен (Часть 26): Инструмент для работы с несколькими паттернами – пин-баром, паттернами поглощения и дивергенцией RSI

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

Введение

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

В этой статье представлен новый инструмент, сосредоточенный на двух ключевых паттернах: пин-баре и поглощении. Эти паттерны сочетаются с дивергенцией RSI, которая подтверждает каждый сигнал. Дивергенция RSI – это инструмент, который я ранее разработал как самостоятельный индикатор на основе анализа движения цены. Объединив распознавание паттернов с дивергенцией RSI, мы создадим более надежный и мощный аналитический метод, который даст более глубокое понимание рынка и повысит точность торговли за счет комплексного анализа движения цены.

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


Понимание свечных паттернов

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

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

Рис. 1. Медвежье поглощение на M30

Тот же паттерн может быть представлен несколькими свечами, если смотреть его на более низком таймфрейме. Давайте посмотрим на схему ниже (рис. 2). На ней показан тот же свечной паттерн, что и выше, но уже на таймфрейме M5, где он представлен серией из нескольких свечей.

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

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

Пин-бар

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

Паттерн поглощения

Паттерн поглощения – это популярный в техническом анализе свечной паттерн, который сигнализирует о возможном развороте рынка. Он состоит из двух последовательных свечей, причем тело второй полностью поглощает тело первой, что указывает на изменение рыночных настроений. Этот паттерн служит сигналом разворота и указывает на возможное завершение предыдущего тренда и начало нового. Бычий разворот возникает, когда крупная восходящая свеча поглощает меньшую нисходящую, а медвежий – когда крупная нисходящая свеча поглощает меньшую восходящую.

Дивергенция RSI

Дивергенция RSI (индекса относительной силы) возникает, когда цена актива и индикатор RSI движутся в противоположных направлениях, сигнализируя о возможном изменении моментума и развороте тренда. Такая дивергенция наблюдается, когда цена формирует новые максимумы или минимумы, а RSI не подтверждает их соответствующими максимумами или минимумами. Существует два основных типа. Медвежья дивергенция возникает, когда цена формирует новый максимум, а RSI – более низкий максимум, что указывает на ослабление восходящего моментума и возможное последующее снижение. Бычья дивергенция возникает, когда цена формирует новый минимум, а RSI – более высокий минимум, что говорит об ослаблении нисходящего моментума и возможном движении вверх.

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

Медвежья дивергенция

Рис. 3. Медвежья дивергенция RSI

Бычья дивергенция RSI

Рис. 4. Бычья дивергенция RSI


Обзор стратегии

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

Рис. 5. Подтверждение пин-бара


Разбор компонентов кода

1. Метаданные и свойства инициализации

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

//+------------------------------------------------------------------+
//|                                           Multi-Pattern Signal EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ru/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ru/users/lynnchris"
#property version   "1.0"
#property strict

2. Подключение библиотек и настройка входных параметров

В советник подключается библиотека <Trade/Trade.mqh> которая позволяет открывать, закрывать и сопровождать сделки. Хотя в этой версии основной акцент сделан на выявлении паттернов и алертах, подключенная торговая библиотека в будущем позволит расширить функциональность до полностью автоматической торговли. Входные параметры критичны для гибкой настройки: трейдер может менять период RSI, регулируя чувствительность, задавать уровни перекупленности и перепроданности для обнаружения дивергенций и задавать уровни стоп‑лосса и тейк‑профита в пунктах.

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

#include <Trade/Trade.mqh>

input int    RSI_Period     = 14;      // RSI calculation period
input double RSI_Overbought = 70.0;    // RSI level indicating overbought
input double RSI_Oversold   = 30.0;    // RSI level indicating oversold
input double SL_Pips        = 20.0;    // Default stop loss in pips
input double TP_Pips        = 20.0;    // Default take profit in pips
input double EntryBuffer    = 5.0;     // Buffer in points for entry price
input bool   EnableSound    = true;    // Enable sound alerts
input bool   EnablePush     = true;    // Enable push notifications
input bool   EnableEmail    = false;   // Enable email alerts

3. Инициализация: Создание хэндлов индикатора и буферов

Во время начальной инициализации советник создает хэндл индикатора RSI с помощью iRSI(), задавая символ, таймфрейм и параметры RSI. Этот хэндл нужен, чтобы на каждом тике получать актуальные значения RSI – на них строится вся логика поиска дивергенций. Массивы объявлены как серии, то есть самые свежие данные всегда находятся по индексу 0. Внутренние буферы времени, а также цен открытия, максимума, минимума и закрытия инициализируются для хранения исторических данных, которые будут использоваться в анализе. Переменная lastBarTime хранит время последней обработанной свечи. Это не дает советнику несколько раз сгенерировать сигнал внутри одной и той же свечи, избавляет от лишних алертов и повышает точность сигналов.

int OnInit()
{
    rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
    if(rsiHandle == INVALID_HANDLE)
        return INIT_FAILED;

    // Set arrays as series for easier access to recent data
    ArraySetAsSeries(rsiBuffer,   true);
    ArraySetAsSeries(timeBuffer,  true);
    ArraySetAsSeries(openBuffer,  true);
    ArraySetAsSeries(highBuffer,  true);
    ArraySetAsSeries(lowBuffer,   true);
    ArraySetAsSeries(closeBuffer, true);

    return INIT_SUCCEEDED;
}

4. Непрерывный мониторинг и обработка данных в OnTick()

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

Чтобы избежать нескольких сигналов в пределах одной свечи, проверяется, совпадает ли время предыдущей свечи со значением lastBarTime. Если да, обработка пропускается; если нет – lastBarTime обновляется временем новой свечи. Затем обновляются данные индикатора RSI. Когда данные готовы, вызывается FindSignalBar(), которая анализирует последние бары на наличие дивергенций и свечных сигналов – именно на этом этапе формируется основа для будущих алертов.

void OnTick()
{
    // Ensure enough data is available
    if(Bars(_Symbol, _Period) < 20)
        return;

    // Copy recent market data
    if(CopyTime(_Symbol, _Period, 0, 20, timeBuffer)   <= 0 ||
       CopyOpen(_Symbol, _Period, 0, 20, openBuffer)   <= 0 ||
       CopyHigh(_Symbol, _Period, 0, 20, highBuffer)   <= 0 ||
       CopyLow(_Symbol, _Period, 0, 20, lowBuffer)     <= 0 ||
       CopyClose(_Symbol, _Period, 0, 20, closeBuffer) <= 0)
       return;

    // Prevent multiple signals in the same candle
    if(timeBuffer[1] == lastBarTime)
        return;
    lastBarTime = timeBuffer[1];

    // Update RSI data
    if(CopyBuffer(rsiHandle, 0, 0, 20, rsiBuffer) <= 0)
        return;

    // Detect potential divergence and pattern
    int dir = FindSignalBar();
    if(dir == 0)
        return;

    // Determine direction and compute entry/stop levels
    bool isBullish = (dir > 0);
    int idx = 1; // most recent completed bar
    double entry = isBullish
                   ? highBuffer[idx] + EntryBuffer * _Point
                   : lowBuffer[idx] - EntryBuffer * _Point;
    double stopL = isBullish
                   ? lowBuffer[idx] - SL_Pips * _Point
                   : highBuffer[idx] + SL_Pips * _Point;

    // Visualize and notify
    DrawSignal(idx, isBullish, entry, stopL);
}

5. Распознавание паттернов через дивергенцию и анализ свечей

Функция FindSignalBar() играет ключевую роль в выявлении потенциальных сигналов разворота. Она сканирует недавние бары (от 5 до 15 баров назад), чтобы обнаружить дивергенции. Бычья дивергенция подтверждается, когда минимумы растут, а RSI при этом опускается ниже уровня перепроданности – это указывает на возможный разворот вверх. Напротив, медвежья дивергенция возникает, когда максимумы снижаются на фоне перекупленности по RSI, что говорит о возможном движении вниз.

Функция также проверяет последний бар на наличие подтверждающих свечных паттернов – пин‑баров и паттернов поглощения, которые известны как сильные разворотные сигналы. Если условия по дивергенции и паттерну выполняются, функция возвращает +1 для бычьего сигнала или -1 для медвежьего; в противном случае возвращается 0, что означает отсутствие сетапа в данный момент. Такой многоуровневый подход сочетает дивергенцию моментума с анализом движения цены и повышает надежность сигналов.

int FindSignalBar()
{
    bool bullDiv = false, bearDiv = false;
    for(int i = 5; i <= 15; i++)
    {
        // Bullish divergence condition
        if(lowBuffer[i] > lowBuffer[1] && rsiBuffer[i] < rsiBuffer[1] && rsiBuffer[1] < RSI_Oversold)
            bullDiv = true;
        // Bearish divergence condition
        if(highBuffer[i] < highBuffer[1] && rsiBuffer[i] > rsiBuffer[1] && rsiBuffer[1] > RSI_Overbought)
            bearDiv = true;
    }

    // No divergence detected
    if(!bullDiv && !bearDiv)
        return 0;

    // Check for candlestick patterns supporting divergence
    bool bullPat = IsBullishPinBar(1) || IsBullishEngulfing(1);
    bool bearPat = IsBearishPinBar(1) || IsBearishEngulfing(1);

    // Confirmed signals
    if(bullDiv && bullPat)
        return +1;
    if(bearDiv && bearPat)
        return -1;

    return 0; // No valid signal
}

6. Обнаружение свечных паттернов для подтверждения

Эти функции анализируют отдельные свечи, чтобы подтвердить сигналы разворота. IsBullishPinBar() проверяет, имеет ли свеча небольшое тело и длинную нижнюю тень – это говорит об отскоке от низких цен и намекает на бычий разворот. IsBearishPinBar() проверяет аналогичный паттерн, но для медвежьего пин‑бара. IsBullishEngulfing() проверяет, поглощает ли тело текущей свечи тело предыдущей, что говорит о сильном покупательском давлении, а IsBearishEngulfing() – о сильном давлении продавцов. Эти функции распознавания паттернов сравнивают размеры тел свечей и их теней с полным диапазоном от максимума до минимума, чтобы отбирать только хорошо сформированные паттерны. Это снижает количество ложных срабатываний и повышает надежность сигналов.

// Bullish Pin Bar Pattern
bool IsBullishPinBar(int i)
{
    double body = MathAbs(openBuffer[i] - closeBuffer[i]);
    double rng  = highBuffer[i] - lowBuffer[i];
    double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
    double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
    return closeBuffer[i] > openBuffer[i]
           && lw > 2.0 * body
           && uw < 0.5 * body
           && body > 0.1 * rng;
}

// Bearish Pin Bar Pattern
bool IsBearishPinBar(int i)
{
    double body = MathAbs(openBuffer[i] - closeBuffer[i]);
    double rng  = highBuffer[i] - lowBuffer[i];
    double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
    double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
    return closeBuffer[i] < openBuffer[i]
           && uw > 2.0 * body
           && lw < 0.5 * body
           && body > 0.1 * rng;
}

// Bullish Engulfing Pattern
bool IsBullishEngulfing(int i)
{
    if(closeBuffer[i] <= openBuffer[i])
        return false;
    if(openBuffer[i] > closeBuffer[i+1])
        return false;
    if(closeBuffer[i] < openBuffer[i+1])
        return false;
    return true;
}

// Bearish Engulfing Pattern
bool IsBearishEngulfing(int i)
{
    if(closeBuffer[i] >= openBuffer[i])
        return false;
    if(openBuffer[i] < closeBuffer[i+1])
        return false;
    if(closeBuffer[i] > openBuffer[i+1])
        return false;
    return true;
}

7. Визуализация и генерация алертов

Как только паттерн и дивергенция подтверждены, DrawSignal() визуализирует сетап: рисует стрелки в точках входа, горизонтальные линии уровней входа и стоп‑лосса и трендовые линии, показывающие паттерн дивергенции. Цвет и код стрелки отражают направление: зеленоватый цвет – для бычьих сигналов, красный – для медвежьих. Трендовые линии соединяют недавние минимумы или максимумы, чтобы визуально подчеркнуть дивергенцию.

Функция также формирует подробный текст алерта: в нем указываются тип паттерна, направление, символ, время, цена входа и уровень стоп‑лосса. Затем, в соответствии с настройками пользователя, запускаются уведомления: проигрывается звук, отправляются push‑уведомления или письма на e‑mail. Это обеспечивает трейдеру своевременное получение информации и возможность быстро реагировать. Такая система визуализации и алертов помогает лучше понимать текущую ситуацию на рынке и принимать более взвешенные решения.

void DrawSignal(int i, bool isBullish, double entry, double stopL)
{
    string tag = TimeToString(timeBuffer[i], TIME_SECONDS);
    string nameA = "Arr_" + tag;
    string nameE = "Ent_" + tag;
    string nameS = "SL_" + tag;
    string nameL = "Div_" + tag;

    color clrArr = isBullish ? clrLime : clrRed;
    int code = isBullish ? 233 : 234;

    // Arrow at entry point
    ObjectCreate(0, nameA, OBJ_ARROW, 0, timeBuffer[i], entry);
    ObjectSetInteger(0, nameA, OBJPROP_COLOR, clrArr);
    ObjectSetInteger(0, nameA, OBJPROP_ARROWCODE, code);
    ObjectSetInteger(0, nameA, OBJPROP_WIDTH, 2);

    // Horizontal lines for entry and stop-loss
    ObjectCreate(0, nameE, OBJ_HLINE, 0, 0, entry);
    ObjectSetInteger(0, nameE, OBJPROP_COLOR, clrAqua);
    ObjectCreate(0, nameS, OBJ_HLINE, 0, 0, stopL);
    ObjectSetInteger(0, nameS, OBJPROP_COLOR, clrOrangeRed);
    ObjectSetInteger(0, nameS, OBJPROP_STYLE, STYLE_DASH);

    // Divergence trend line for visual confirmation
    for(int j = i + 5; j < i + 15; j++)
    {
        if(isBullish && lowBuffer[j] > lowBuffer[i])
        {
            ObjectCreate(0, nameL, OBJ_TREND, 0,
                         timeBuffer[j], lowBuffer[j],
                         timeBuffer[i], lowBuffer[i]);
            ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrDodgerBlue);
            break;
        }
        if(!isBullish && highBuffer[j] < highBuffer[i])
        {
            ObjectCreate(0, nameL, OBJ_TREND, 0,
                         timeBuffer[j], highBuffer[j],
                         timeBuffer[i], highBuffer[i]);
            ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrOrange);
            break;
        }
    }

    // Construct alert message
    string pattern = isBullish
                     ? (IsBullishEngulfing(i) ? "Engulfing" : "PinBar")
                     : (IsBearishEngulfing(i) ? "Engulfing" : "PinBar");
    string side = isBullish ? "Buy" : "Sell";
    string txt = StringFormat(
                     "%s + RSI Divergence %s Signal\nSymbol: %s\nTime: %s\nEntry: %.5f\nSL: %.5f",
                     pattern, side, _Symbol,
                     TimeToString(timeBuffer[i], TIME_MINUTES),
                     entry, stopL
                 );

    // Notify trader
    Alert(txt);
    if(EnableSound)
        PlaySound("alert.wav");
    if(EnablePush)
        SendNotification(txt);
    if(EnableEmail)
        SendMail("Signal EA Alert", txt);

    Print(txt);
}

Полный советник на MQL5

//+------------------------------------------------------------------+
//|                                           Multi-Pattern Signal EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ru/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ru/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade/Trade.mqh>

input int    RSI_Period     = 14;
input double RSI_Overbought = 70.0;
input double RSI_Oversold   = 30.0;
input double SL_Pips        = 20.0;  // in pips
input double TP_Pips        = 20.0;  // in pips
input double EntryBuffer    = 5.0;   // in points
input bool   EnableSound    = true;
input bool   EnablePush     = true;
input bool   EnableEmail    = false;

CTrade   trade;

// internal buffers
double   rsiBuffer[];
datetime timeBuffer[];
double   openBuffer[], highBuffer[], lowBuffer[], closeBuffer[];
int      rsiHandle;
datetime lastBarTime = 0;

//+------------------------------------------------------------------+
int OnInit()
  {
   rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
      return INIT_FAILED;

   ArraySetAsSeries(rsiBuffer,   true);
   ArraySetAsSeries(timeBuffer,  true);
   ArraySetAsSeries(openBuffer,  true);
   ArraySetAsSeries(highBuffer,  true);
   ArraySetAsSeries(lowBuffer,   true);
   ArraySetAsSeries(closeBuffer, true);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
void OnTick()
  {
   if(Bars(_Symbol,_Period) < 20)
      return;

   if(CopyTime(_Symbol,_Period,0,20,timeBuffer)   <= 0 ||
      CopyOpen(_Symbol,_Period,0,20,openBuffer)   <= 0 ||
      CopyHigh(_Symbol,_Period,0,20,highBuffer)   <= 0 ||
      CopyLow(_Symbol,_Period,0,20,lowBuffer)     <= 0 ||
      CopyClose(_Symbol,_Period,0,20,closeBuffer) <= 0)
      return;

   if(timeBuffer[1] == lastBarTime)
      return;
   lastBarTime = timeBuffer[1];

   if(CopyBuffer(rsiHandle,0,0,20,rsiBuffer) <= 0)
      return;

   int dir = FindSignalBar();
   if(dir == 0)
      return;

   int idx = 1;
   bool isBullish = (dir > 0);

   double entry = isBullish
                  ? highBuffer[idx] + EntryBuffer * _Point
                  : lowBuffer[idx]  - EntryBuffer * _Point;
   double stopL = isBullish
                  ? lowBuffer[idx]  - SL_Pips * _Point
                  : highBuffer[idx] + SL_Pips * _Point;

   DrawSignal(idx, isBullish, entry, stopL);
  }

//+------------------------------------------------------------------+
int FindSignalBar()
  {
   bool bullDiv = false, bearDiv = false;
   for(int i = 5; i <= 15; i++)
     {
      if(lowBuffer[i] > lowBuffer[1] && rsiBuffer[i] < rsiBuffer[1] && rsiBuffer[1] < RSI_Oversold)
         bullDiv = true;
      if(highBuffer[i] < highBuffer[1] && rsiBuffer[i] > rsiBuffer[1] && rsiBuffer[1] > RSI_Overbought)
         bearDiv = true;
     }
   if(!bullDiv && !bearDiv)
      return 0;

   bool bullPat = IsBullishPinBar(1) || IsBullishEngulfing(1);
   bool bearPat = IsBearishPinBar(1) || IsBearishEngulfing(1);

   if(bullDiv && bullPat)
      return +1;
   if(bearDiv && bearPat)
      return -1;
   return 0;
  }

//+------------------------------------------------------------------+
bool IsBullishPinBar(int i)
  {
   double body = MathAbs(openBuffer[i] - closeBuffer[i]);
   double rng  = highBuffer[i] - lowBuffer[i];
   double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
   double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
   return closeBuffer[i] > openBuffer[i]
          && lw > 2.0 * body
          && uw < 0.5 * body
          && body > 0.1 * rng;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBearishPinBar(int i)
  {
   double body = MathAbs(openBuffer[i] - closeBuffer[i]);
   double rng  = highBuffer[i] - lowBuffer[i];
   double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
   double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
   return closeBuffer[i] < openBuffer[i]
          && uw > 2.0 * body
          && lw < 0.5 * body
          && body > 0.1 * rng;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBullishEngulfing(int i)
  {
   if(closeBuffer[i] <= openBuffer[i])
      return false;
   if(openBuffer[i]  > closeBuffer[i+1])
      return false;
   if(closeBuffer[i] < openBuffer[i+1])
      return false;
   return true;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBearishEngulfing(int i)
  {
   if(closeBuffer[i] >= openBuffer[i])
      return false;
   if(openBuffer[i]  < closeBuffer[i+1])
      return false;
   if(closeBuffer[i] > openBuffer[i+1])
      return false;
   return true;
  }

//+------------------------------------------------------------------+
void DrawSignal(int i, bool isBullish, double entry, double stopL)
  {
   string tag   = TimeToString(timeBuffer[i], TIME_SECONDS);
   string nameA = "Arr_" + tag;
   string nameE = "Ent_" + tag;
   string nameS = "SL_"  + tag;
   string nameL = "Div_" + tag;

   color clrArr = isBullish ? clrLime : clrRed;
   int   code   = isBullish ? 233 : 234;

   ObjectCreate(0, nameA, OBJ_ARROW, 0, timeBuffer[i], entry);
   ObjectSetInteger(0, nameA, OBJPROP_COLOR, clrArr);
   ObjectSetInteger(0, nameA, OBJPROP_ARROWCODE, code);
   ObjectSetInteger(0, nameA, OBJPROP_WIDTH, 2);

   ObjectCreate(0, nameE, OBJ_HLINE, 0, 0, entry);
   ObjectSetInteger(0, nameE, OBJPROP_COLOR, clrAqua);
   ObjectCreate(0, nameS, OBJ_HLINE, 0, 0, stopL);
   ObjectSetInteger(0, nameS, OBJPROP_COLOR, clrOrangeRed);
   ObjectSetInteger(0, nameS, OBJPROP_STYLE, STYLE_DASH);

   for(int j = i + 5; j < i + 15; j++)
     {
      if(isBullish && lowBuffer[j] > lowBuffer[i])
        {
         ObjectCreate(0, nameL, OBJ_TREND, 0,
                      timeBuffer[j], lowBuffer[j],
                      timeBuffer[i], lowBuffer[i]);
         ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrDodgerBlue);
         break;
        }
      if(!isBullish && highBuffer[j] < highBuffer[i])
        {
         ObjectCreate(0, nameL, OBJ_TREND, 0,
                      timeBuffer[j], highBuffer[j],
                      timeBuffer[i], highBuffer[i]);
         ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrOrange);
         break;
        }
     }

   string pattern = isBullish
                    ? (IsBullishEngulfing(i) ? "Engulfing" : "PinBar")
                    : (IsBearishEngulfing(i) ? "Engulfing" : "PinBar");
   string side    = isBullish ? "Buy" : "Sell";
   string txt     = StringFormat(
                       "%s + RSI Divergence %s Signal\nSymbol: %s\nTime: %s\nEntry: %.5f\nSL: %.5f",
                       pattern, side, _Symbol,
                       TimeToString(timeBuffer[i], TIME_MINUTES),
                       entry, stopL
                    );

   Alert(txt);
   if(EnableSound)
      PlaySound("alert.wav");
   if(EnablePush)
      SendNotification(txt);
   if(EnableEmail)
      SendMail("Signal EA Alert", txt);
   Print(txt);
  }
//+------------------------------------------------------------------+


Тестирование на исторических данных и результаты

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

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

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

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

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

Ниже – результаты моего тестирования на исторических данных.

Рис. 6. Подтверждение паттерна бычьего поглощения

На рисунке выше (рис. 6) показан скриншот тестирования EURUSD на таймфрейме M30: паттерн бычьего поглощения, подтвержденный бычьей дивергенцией.

Рис. 7. Тестирование на исторических данных по USDCHF

На рисунке выше (рис. 7) показано тестирование USDCHF на таймфрейме M30: паттерн бычьего поглощения, подтвержденный бычьей дивергенцией. Процесс тестирования на исторических данных по паре USDCHF представлен в виде GIF-анимации, где видно, как советник последовательно отрабатывает несколько сигналов.


Заключение

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

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


Дата Название инструмента  Описание Версия  Обновления  Примечания
01/10/24 Chart Projector Скрипт для наложения эффекта призрака на движение цены за предыдущий день 1.0 Первоначальная версия Инструмент номер 1
18/11/24 Analytical Comment Предоставляет информацию за предыдущий день в табличном формате, а также прогнозирует будущее направление рынка 1.0 Первоначальная версия Инструмент номер 2
27/11/24 Analytics Master Регулярное обновление рыночных показателей каждые два часа  1.01 Вторая версия Инструмент номер 3
02/12/24 Analytics Forecaster  Регулярное обновление рыночных показателей каждые два часа с интеграцией с Telegram 1.1 Третья версия Инструмент номер 4
09/12/24 Volatility Navigator Советник анализирует рыночные условия с помощью полос Боллинджера, RSI и ATR 1.0 Первоначальная версия Инструмент номер 5
19/12/24 Mean Reversion Signal Reaper  Анализирует рынок и генерирует сигналы, используя стратегию возврата к среднему  1.0  Первоначальная версия  Инструмент номер 6 
9/01/25  Signal Pulse  Анализирует несколько таймфреймов 1.0  Первоначальная версия  Инструмент номер 7 
17/01/25  Metrics Board  Панель с кнопками для анализа  1.0  Первоначальная версия Инструмент номер 8 
21/01/25 External Flow Аналитика с помощью внешних библиотек 1.0  Первоначальная версия Инструмент номер 9 
27/01/25 VWAP Взвешенная по объему средняя цена   1.3  Первоначальная версия  Инструмент номер 10 
02/02/25  Heikin Ashi  Сглаживание тренда и идентификация сигналов разворота  1.0  Первоначальная версия  Инструмент номер 11
04/02/25  FibVWAP  Генерация сигналов с помощью анализа Python  1.0  Первоначальная версия  Инструмент номер 12
14/02/25  RSI DIVERGENCE  Дивергенция цены и RSI  1.0  Первоначальная версия  Инструмент номер 13 
17/02/25  Parabolic Stop and Reverse (PSAR)  Автоматизация стратегии PSAR 1.0 Первоначальная версия  Инструмент номер 14
20/02/25  Скрипт Quarters Drawer  Нанесение уровней четвертей на график  1.0  Первоначальная версия  Инструмент номер 15 
27/02/25  Intrusion Detector Обнаружение и оповещение о достижении ценой уровней четвертей 1.0   Первоначальная версия Инструмент номер 16 
27/02/25  TrendLoom Tool Панель мультитаймфреймового анализа 1.0 Первоначальная версия Инструмент номер 17
11/03/25  Quarters Board  Панель с кнопками для включения/отключения уровней четвертей  1.0  Первоначальная версия Инструмент номер 18
26/03/25  ZigZag Analyzer  Построение линий тренда с помощью индикатора ZigZag  1.0  Первоначальная версия  Инструмент номер 19 
10/04/25  Correlation Pathfinder Построение графиков корреляции валютных курсов с использованием библиотек Python 1.0 Первоначальная версия  Инструмент номер 20 
23/04/25 Market Structure Flip Detector Tool Поиск разворотов рыночной структуры 1.0  Первоначальная версия  Инструмент номер 21
08/05/25  Correlation Dashboard  Корреляции между разными парами 1.0 Первоначальная версия Инструмент номер 22 
13/05/25 Currency Strength Meter  Измеряет силу валюты по нескольким валютным парам с ее участием 1.0 Первоначальная версия Инструмент номер 23 
21/05/25 PAQ Analysis Tool  Обнаруживает свечные паттерны 1.0 Первоначальная версия Инструмент номер 24 
23/05/25 Pin bar, Engulfing and RSI divergence Использует дивергенцию RSI для подтверждения сигналов по паттернам пин-бара и поглощения  1.0 Первоначальная версия Инструмент номер 25 

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

Прикрепленные файлы |
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Разработка инструментария для анализа движения цен (Часть 23): Индикатор силы валют Разработка инструментария для анализа движения цен (Часть 23): Индикатор силы валют
Знаете, что на самом деле определяет направление валютной пары? Его определяет сила каждой отдельной валюты. В этой статье мы будем измерять силу валюты, анализируя все пары, в которых она присутствует. Это позволит прогнозировать движение этих пар, исходя из относительной силы входящих в них валют. Читайте дальше, чтобы узнать больше.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Создание самооптимизирующихся советников на MQL5 (Часть 10): Факторизация матриц Создание самооптимизирующихся советников на MQL5 (Часть 10): Факторизация матриц
Факторизация — это математический процесс, используемый для получения представления о свойствах данных. Когда мы применяем факторизацию к большим наборам рыночных данных — организованных в строки и столбцы — мы можем выявлять закономерности и характеристики рынка. Факторизация является мощным инструментом, и в этой статье показано, как использовать её в терминале MetaTrader 5 через API MQL5, чтобы получить более глубокое понимание рыночных данных.