English Deutsch 日本語
preview
Автоматизация торговых стратегий в MQL5 (Часть 27): Выявление и визуализация гармонического паттерна "Краб" на основе Price Action

Автоматизация торговых стратегий в MQL5 (Часть 27): Выявление и визуализация гармонического паттерна "Краб" на основе Price Action

MetaTrader 5Торговые системы |
103 7
Allan Munene Mutiiria
Allan Munene Mutiiria

Введение

В своей предыдущей статье (Часть 26) мы создали систему усреднения на основе пин-баров в MetaQuotes Language 5 (MQL5). В ней используются свечные паттерны пин-баров для открытия сделок и управления несколькими позициями с помощью стратегии усреднения, она дополнена динамическим дашбордом для мониторинга в реальном времени. В Части 27 мы создадим систему паттерна "Краб", которая определяет бычьи и медвежьи гармонические паттерны "Краб" с использованием точек разворота и уровней Фибоначчи, автоматизируя сделки с точными уровнями входа, стоп-лосса и тейк-профита. Она дополнена визуальными графическими объектами, такими как треугольники и линии тренда, для четкого отображения паттернов. В статье рассмотрим следующие темы:

  1. Изучение структуры гармонического паттерна "Краб"
  2. Реализация средствами MQL5
  3. Тестирование на истории
  4. Заключение

В итоге у вас будет тщательно продуманная стратегия в MQL5 для торговли на основе гармонических паттернов, готовая к настройке. Перейдём к реализации!


Изучение структуры гармонического паттерна "Краб"

Паттерн "Краб" — это гармоническая торговая формация, определяемая пятью ключевыми точками колебания — X, A, B, C и D - и существующая в двух формах: бычий паттерн и медвежий паттерн. При бычьем "Крабе" структура образует последовательность "минимум-максимум-минимум-максимум-минимум", где точка X - свинг-лоу, точка A - свинг-хай, точка B - свинг-лоу (коррекция 0,618 от XA), точка C - свинг-хай (продление от 0,382 до 0,886 от AB) и точка D - свинг-лоу (расширение 1,618 от XA, расположена ниже X). Напротив, медвежий "Краб" формирует последовательность "максимум-минимум-максимум-минимум-максимум", где точка X является свинг-хай, точка A — свинг-лоу, точка B — свинг-хай, точка C — свинг-лоу, а точка D — свинг-хай (расширение 1,618 от XA и располагается выше X). Ниже представлены визуализированные типы паттернов.

Бычий гармонический паттерн "Краб":

BULLISH CRAB HARMONIC PATTERN

Медвежий гармонический паттерн "Краб":

BEARISH CRAB HARMONIC PATTERN

Для выявления паттернов ниже представлен наш структурированный подход:

  • Определение отезка XA: Первоначальное импульсное движение из точки X в точку A закладывает основу паттерна, определяя направление (вниз для бычьего тренда, вверх для медвежьего) и служа контрольной отметкой для вычислений Фибоначчи.
  • Создание отрезка "AB": Точка B должна откатиться примерно на 0,618 длины отрезка XA, подтверждая коррекцию без слишком резкого разворота относительно первоначального движения.
  • Анализ отрезка "BC": Этот отрезок должен находиться в диапазоне от 0,382 до 0,886 длины AB, создавая резкое контрдвижение, которое подготавливает финальное расширение.
  • Настройка отрезка "CD": Заключительный отрезок должен продолжаться на 1,618 длины отрезка XA, отмечая потенциальную зону разворота в точке D, где паттерн завершается и генерируется торговый сигнал.

Применяя эти геометрические критерии и критерии, основанные на Фибоначчи, наша торговая система будет систематически выявлять достоверные паттерны "Краб" в ценовых данных. После идентификации система будет визуализировать формацию на графике с помощью треугольников, линий тренда, меток для точек X, A, B, C и D, а также пунктирных линий для уровней входа и тейк-профита. Этот сетап позволит автоматизировать открытие сделок в точке D с рассчитанным стоп-лоссом и многоуровневыми тейк-профитами, используя высокую вероятность разворота паттерна для эффективного входа на рынок. Приступим к реализации!


Реализация средствами MQL5

Чтобы создать программу на MQL5, откройте  MetaEditor, перейдите в Навигатор, найдите папку «Индикаторы» (Indicators), перейдите на вкладку "Создать" (New) и следуйте инструкциям по созданию файла. Как только это будет сделано, в среде программирования нам нужно будет объявить некоторые глобальные переменные, которые будем использовать во всей программе.

//+------------------------------------------------------------------+
//|                                             Crab Pattern EA.mq5. |
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "https://t.me/Forex_Algo_Trader" |
//+------------------------------------------------------------------+
#property copyright   "Forex Algo-Trader, Allan"
#property link        "https://t.me/Forex_Algo_Trader"
#property version     "1.00"
#property description "This EA trades based on Crab Strategy"
#property strict

#include <Trade\Trade.mqh>                         //--- Include Trade library for order management
CTrade obj_Trade;                                  //--- Instantiate trade object for executing orders

//--- Input parameters for user configuration
input int    PivotLeft = 5;                        // Number of bars to the left for pivot identification
input int    PivotRight = 5;                       // Number of bars to the right for pivot identification
input double Tolerance = 0.10;                     // Allowed deviation for Fibonacci levels (10% of XA move)
input double LotSize = 0.01;                       // Lot size for opening new trade positions
input bool   AllowTrading = true;                  // Enable or disable automated trading functionality

//---------------------------------------------------------------------------
//--- Crab pattern definition:
//--- Bullish Crab:
//--- Pivots (X-A-B-C-D): X swing low, A swing high, B swing low, C swing high, D swing low.
//--- Normally XA > 0; Ideal B = A - 0.5*(A-X); Legs within specified ranges.
//--- Bearish Crab:
//--- Pivots (X-A-B-C-D): X swing high, A swing low, B swing high, C swing low, D swing high.
//--- Normally XA > 0; Ideal B = A + 0.5*(X-A); Legs within specified ranges.
//---------------------------------------------------------------------------

struct Pivot {                                     //--- Define structure for pivot points
   datetime time;                                  //--- Store time of pivot bar
   double   price;                                 //--- Store price (high for swing high, low for swing low)
   bool     isHigh;                                //--- Indicate true for swing high, false for swing low
};

Pivot pivots[];                                    //--- Declare array to store pivot points
int      g_patternFormationBar = -1;               //--- Store bar index of pattern formation (-1 if none)
datetime g_lockedPatternX = 0;                     //--- Store X pivot time for locked pattern

Начнем реализацию паттерна "Краб", подключая библиотеку "<Trade\Trade.mqh>" и создавая экземпляр объекта CTrade "obj_Trade" для упрощения управления ордерами, например, отправки запросов на покупку и продажу. Затем перейдём к определению пяти входных параметров для обеспечения пользовательской настройки: Значения параметров "PivotLeft" и "PivotRight" равны 5 барам каждый, чтобы указать период ретроспективного анализа для определения свинг-пивотов, "Tolerance" равно 0,10 для допустимого отклонения от уровней Фибоначчи, "LotSize" равно 0,01 для объема торговли, а значение "AllowTrading" равное true, чтобы включить автоматическое открытие.

Далее определим структуру "Pivot" с параметрами "time" (datetime), "price" (double) и "isHigh" (bool) для хранения точек разворота, объявим "pivots" как массив "Pivot" и инициализируем глобальные переменные "g_patternFormationBar" значением -1 для отслеживания баров формирования паттернов и "g_lockedPatternX" значением 0 для сохранения времени пивота X, для подтверждения. Это закладывает основу для идентификации паттернов. Для визуализации можно использовать функции для рисования линий, меток и треугольников.

//+------------------------------------------------------------------+
//| Draw filled triangle on chart                                    |
//+------------------------------------------------------------------+
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {
   if (ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) { //--- Create triangle with three points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set triangle color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set solid line style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
      ObjectSetInteger(0, name, OBJPROP_FILL, fill); //--- Enable or disable fill
      ObjectSetInteger(0, name, OBJPROP_BACK, back); //--- Set background or foreground
   }
}

//+------------------------------------------------------------------+
//| Draw trend line on chart                                         |
//+------------------------------------------------------------------+
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) { //--- Create trend line between two points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, style); //--- Set line style (solid, dotted, etc.)
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
   }
}

//+------------------------------------------------------------------+
//| Draw dotted horizontal line on chart                             |
//+------------------------------------------------------------------+
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) { //--- Create horizontal dotted line
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); //--- Set dotted style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); //--- Set line width to 1
   }
}

//+------------------------------------------------------------------+
//| Draw anchored text label for pivots                              |
//+------------------------------------------------------------------+
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {
   if (ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) { //--- Create text label at specified coordinates
      ObjectSetString(0, name, OBJPROP_TEXT, text); //--- Set label text content
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set text color
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize); //--- Set font size
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); //--- Set font to Arial Bold
      if (isHigh) {                                //--- Check if pivot is swing high
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM); //--- Anchor label above pivot
      } else {                                     //--- Handle swing low
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP); //--- Anchor label below pivot
      }
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
   }
}

Здесь мы реализуем функции визуализации для программы, позволяющие рисовать графические объекты, представляющие гармонический паттерн "Краб" и его торговые уровни. Сначала мы создадим функцию "DrawTriangle", которая использует ObjectCreate для рисования заполненного треугольника (OBJ_TRIANGLE) с тремя точками, заданными временем ("t1", "t2", "t3") и ценами ("p1", "p2", "p3"). Установим OBJPROP_COLOR в указанный цвет, "OBJPROP_STYLE" в значение "STYLE_SOLID", "OBJPROP_WIDTH" в заданную ширину, "OBJPROP_FILL" для включения или выключения заливки и "OBJPROP_BACK" для установки положения фона или переднего плана с помощью ObjectSetInteger функции.

Затем перейдём к реализации функции "DrawTrendLine", которая создает линию тренда ("OBJ_TREND") между двумя точками с помощью функции "ObjectCreate". Затем настраиваем параметры "OBJPROP_COLOR", "OBJPROP_STYLE" (сплошная, пунктирная и т. д.) и OBJPROP_WIDTH с помощью функции "ObjectSetInteger" для настраиваемого внешнего вида линии. Далее разработаем функцию "DrawDottedLine", которая рисует горизонтальную пунктирную линию (OBJ_TREND) по заданной цене от "t1" до "t2" и строится по тому же принципу. Наконец, реализуем функцию "DrawTextEx", которая создает текстовую метку (OBJ_TEXT) в координатах ("t", "p") с помощью функции создания объекта и использует тот же формат, что и предыдущие функции, обеспечивая четкое визуальное представление паттерна "Краб" и торговых уровней на графике. Теперь можно перейти к обработчику OnTick и попытаться найти пивоты, которые мы сможем использовать позже для идентификации паттернов. Вот логика, которую мы используем для достижения этой цели.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastBarTime = 0;               //--- Store time of last processed bar
   datetime currentBarTime = iTime(_Symbol, _Period, 1); //--- Get time of current confirmed bar
   if (currentBarTime == lastBarTime) return;     //--- Exit if no new bar
   lastBarTime = currentBarTime;                  //--- Update last processed bar time
   ArrayResize(pivots, 0);                        //--- Clear pivot array for fresh analysis
   int barsCount = Bars(_Symbol, _Period);        //--- Retrieve total number of bars
   int start = PivotLeft;                         //--- Set starting index for pivot detection
   int end = barsCount - PivotRight;              //--- Set ending index for pivot detection
   for (int i = end - 1; i >= start; i--) {       //--- Iterate through bars to identify pivots
      bool isPivotHigh = true;                    //--- Assume bar is a swing high
      bool isPivotLow = true;                     //--- Assume bar is a swing low
      double currentHigh = iHigh(_Symbol, _Period, i); //--- Get current bar high price
      double currentLow = iLow(_Symbol, _Period, i); //--- Get current bar low price
      for (int j = i - PivotLeft; j <= i + PivotRight; j++) { //--- Check surrounding bars
         if (j < 0 || j >= barsCount) continue;   //--- Skip out-of-bounds indices
         if (j == i) continue;                    //--- Skip current bar
         if (iHigh(_Symbol, _Period, j) > currentHigh) isPivotHigh = false; //--- Invalidate swing high
         if (iLow(_Symbol, _Period, j) < currentLow) isPivotLow = false; //--- Invalidate swing low
      }
      if (isPivotHigh || isPivotLow) {            //--- Check if bar is a pivot
         Pivot p;                                 //--- Create new pivot structure
         p.time = iTime(_Symbol, _Period, i);     //--- Set pivot bar time
         p.price = isPivotHigh ? currentHigh : currentLow; //--- Set pivot price
         p.isHigh = isPivotHigh;                  //--- Set pivot type
         int size = ArraySize(pivots);            //--- Get current pivot array size
         ArrayResize(pivots, size + 1);           //--- Resize pivot array
         pivots[size] = p;                        //--- Add pivot to array
      }
   }
}

Приступим к реализации начальной логики обработчика OnTick для нашей программы, чтобы обнаруживать точки пивот свингов, формируя основу для идентификации гармонических паттернов "Краб". Сначала проверим наличие нового бара, сравнивая "lastBarTime" (статическое значение, инициализированное нулем) с "currentBarTime" из iTime со сдвигом 1, чтобы избежать использования текущего неполного бара для текущего инструмента и периода. Выходим из функции, если новый бар не появился и обновляем "lastBarTime", если обнаружен новый бар. Затем очистим массив "pivots" с помощью ArrayResize для обеспечения нового анализа. Далее получаем общее количество баров с помощью Bars, установим диапазон определения пивотов от "start" (равный "PivotLeft") до "end" (общее количество баров минус "PivotRight") и перебираем бары от "end - 1" до "start".

Для каждого бара предположим, что это свинг-хай ("isPivotHigh" true) и свинг-лоу ("isPivotLow" true), получаем его максимальную и минимальную цены с помощью iHigh и "iLow", а затем проверим окружающие бары в пределах "PivotLeft" и "PivotRight" с помощью "iHigh" и iLow для аннулирования пивота, если у какого-либо соседнего бара более высокий максимум или более низкий минимум. Наконец, если бар остается допустимым пивотом (максимумом или минимумом), создадим структуру "Pivot", установим для нее "time" с помощью параметра "iTime", "price" на максимум или минимум на основе параметра "isPivotHigh" и флага "isHigh". Затем добавим ее в массив "pivots" с помощью "ArrayResize" и сохраним. При выводе массива на экран получим следующий результат.

PIVOTS DATA

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

int pivotCount = ArraySize(pivots);            //--- Get total number of pivots
if (pivotCount < 5) {                          //--- Check if insufficient pivots
   g_patternFormationBar = -1;                 //--- Reset pattern formation bar
   g_lockedPatternX = 0;                       //--- Reset locked X pivot
   return;                                     //--- Exit function
}
Pivot X = pivots[pivotCount - 5];              //--- Extract X pivot (earliest)
Pivot A = pivots[pivotCount - 4];              //--- Extract A pivot
Pivot B = pivots[pivotCount - 3];              //--- Extract B pivot
Pivot C = pivots[pivotCount - 2];              //--- Extract C pivot
Pivot D = pivots[pivotCount - 1];              //--- Extract D pivot (latest)
bool patternFound = false;                     //--- Initialize pattern detection flag
if (X.isHigh && !A.isHigh && B.isHigh && !C.isHigh && D.isHigh) { //--- Check bearish Crab pattern
   double diff = X.price - A.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price + 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = B.price - A.price;        //--- Calculate AB leg length
         double BC = B.price - C.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = D.price - A.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price > X.price) { //--- Verify 1.618 extension and D > X
               patternFound = true;            //--- Confirm bearish pattern
            }
         }
      }
   }
}
if (!X.isHigh && A.isHigh && !B.isHigh && C.isHigh && !D.isHigh) { //--- Check bullish Crab pattern
   double diff = A.price - X.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price - 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = A.price - B.price;        //--- Calculate AB leg length
         double BC = C.price - B.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = A.price - D.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price < X.price) { //--- Verify 1.618 extension and D < X
               patternFound = true;            //--- Confirm bullish pattern
            }
         }
      }
   }
}

Для определения паттернов мы используем критерии, основанные на методе Фибоначчи. Сначала получим общее количество пивотов с помощью "arraySize(pivots)" и сохраним его в "pivotCount", выйдем с помощью "g_patternFormationBar", а "g_lockedPatternX" сбрасывается на -1 и 0, соответственно, если найдено менее 5 пивотов, поскольку для паттерна "Краб" требуются точки X, A, B, C и D. Затем извлечем последние пять пивотов из массива "pivots", присвоим "X" (самые ранние), "A", "B", "C" и "D" (самые поздние) для представления структуры паттерна.

Затем проверим наличие медвежьего паттерна "Краб", проверяя последовательность (максимум X, минимум A, максимум B, минимум C, максимум D), вычислим разницу в длине XA ("X.price - A.price"). Убедимся, что она положительная, и вычислим идеальную точку B как "A.price + 0.618 * diff", и подтвердим, что B находится в пределах "Tolerance * diff" с помощью MathAbs. Затем проверим отрезок BC (от 0,382 до 0,886 отрезка AB) и расширение AD (1,618 отрезка XA с D выше X). Установим для параметра "patternFound" значение true, если все условия выполнены. Наконец проверим наличие бычьего паттерна "Краб" (минимум X, максимум A, минимум B, максимум C, минимум D), рассчитаем XA как "A.price - X.price", убедимся в его положительном значении. Проверим, что B находится на уровне коррекции 0,618, BC — в пределах от 0,382 до 0,886 отрезка AB, а AD — на уровне 1,618 отрезка XA, при этом D находится ниже X. Установим "patternFound" в значение true, если паттерн действителен. Если паттерн найден, можно приступить к его отображению на графике.

string patternType = "";                                                //--- Initialize pattern type
if (patternFound) {                                                     //--- Check if pattern detected
   if (D.price > X.price) patternType = "Bearish";                      //--- Set bearish pattern (sell signal)
   else if (D.price < X.price) patternType = "Bullish";                 //--- Set bullish pattern (buy signal)
}
if (patternFound) {                                                     //--- Process valid Crab pattern
   Print(patternType, " Crab pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); //--- Log pattern detection
   string signalPrefix = "CR_" + IntegerToString(X.time);               //--- Generate unique prefix for objects
   color triangleColor = (patternType == "Bullish") ? clrBlue : clrRed; //--- Set triangle color based on pattern
   DrawTriangle(signalPrefix + "_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price, triangleColor, 2, true, true); //--- Draw XAB triangle
   DrawTriangle(signalPrefix + "_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price, triangleColor, 2, true, true); //--- Draw BCD triangle
}

Для классификации и визуализации обнаруженных паттернов на графике мы инициализируем переменную "patternType" пустой строкой, чтобы указать, является ли паттерн бычьим или медвежьим. Затем, если "patternFound" имеет значение true, определим тип паттерна, сравнивая "D.price" с "X.price": установим "patternType" в значение "Bearish", если D выше X (что указывает на сигнал к продаже), или в значение "Bullish", если D ниже X (что указывает на сигнал к покупке). Далее, после подтверждения действительного паттерна "Краб", мы регистрируем обнаружение с помощью функции Print, выводим "patternType" и время пивота D с помощью "TimeToString", отформатированной в виде даты, минут и секунд.

Наконец создадим уникальный идентификатор "signalPrefix" как "CR_", конкатенированный с "X.time", преобразованным в строку. Установим "triangleColor" в синий цвет для бычьих или красный для медвежьих паттернов и вызовем "DrawTriangle" дважды для визуализации паттерна: сначала для треугольника XAB (соединяющего X, A, B), а затем для треугольника BCD (соединяющего B, C, D), используем "signalPrefix" с суффиксами "_Triangle1" и "_Triangle2", соответствующими показателями времени и цен пивотов, "triangleColor", шириной 2. А также включим заливку и отображение фона, обеспечивая чёткую идентификацию и визуальное представление обнаруженных паттернов "Краб" для принятия торговых решений. Мы выполнили следующий этап.

PATTERN WITH TRIANGLES

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

DrawTrendLine(signalPrefix + "_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID); //--- Draw XA trend line
DrawTrendLine(signalPrefix + "_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw AB trend line
DrawTrendLine(signalPrefix + "_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID); //--- Draw BC trend line
DrawTrendLine(signalPrefix + "_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw CD trend line
DrawTrendLine(signalPrefix + "_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw XB trend line
DrawTrendLine(signalPrefix + "_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw BD trend line
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Retrieve symbol point size
double offset = 15 * point;                    //--- Calculate label offset (15 points)
double textY_X = X.isHigh ? X.price + offset : X.price - offset; //--- Set X label Y coordinate
double textY_A = A.isHigh ? A.price + offset : A.price - offset; //--- Set A label Y coordinate
double textY_B = B.isHigh ? B.price + offset : B.price - offset; //--- Set B label Y coordinate
double textY_C = C.isHigh ? C.price + offset : C.price - offset; //--- Set C label Y coordinate
double textY_D = D.isHigh ? D.price + offset : D.price - offset; //--- Set D label Y coordinate
DrawTextEx(signalPrefix + "_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh); //--- Draw X pivot label
DrawTextEx(signalPrefix + "_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh); //--- Draw A pivot label
DrawTextEx(signalPrefix + "_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh); //--- Draw B pivot label
DrawTextEx(signalPrefix + "_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh); //--- Draw C pivot label
DrawTextEx(signalPrefix + "_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh); //--- Draw D pivot label
datetime centralTime = (X.time + B.time) / 2;                                       //--- Calculate central label time
double centralPrice = D.price;                                                      //--- Set central label price
if (ObjectCreate(0, signalPrefix + "_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) { //--- Create central pattern label
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_TEXT, patternType == "Bullish" ? "Bullish Crab" : "Bearish Crab"); //--- Set pattern name
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_COLOR, clrBlack);     //--- Set text color
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_FONTSIZE, 11);        //--- Set font size
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_FONT, "Arial Bold");   //--- Set font type
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
}

Для отображения структуры паттерна продолжим добавлять линии и метки. Сначала нарисуем шесть линий тренда, используя "DrawTrendLine" с уникальным "signalPrefix", чтобы соединить ключевые точки разворота: Отрезки XA, AB, BC, CD, XB и BD, каждый из которых имеет конечные точки, определенные соответствующим временем и ценами пивотов (например, "X.time", "X.price"), использует "clrBlack" для цвета, значение ширины 2 и STYLE_SOLID для стиля сплошной линии, очерчивает структуру XABCD и вспомогательные отрезки. Затем приступим к вычислению смещения метки, извлекая размер пункта символа с помощью "SymbolInfoDouble(_Symbol, SYMBOL_POINT)" и умножая на 15, определяя координаты Y для меток пивотов ("textY_X", "textY_A", "textY_B", "textY_C", "textY_D") путем добавления или вычитания смещения в зависимости от того, является ли каждый пивот максимумом свинга ("isHigh" true) или минимумом. Это гарантирует, что метки будут отображаться выше максимумов и ниже минимумов.

Затем мы используем "DrawTextEx" для создания текстовых меток для пивотов X, A, B, C и D, каждая из которых содержит "signalPrefix" и суффиксы типа "_Text_X", отображающие соответствующую букву, расположенную во времени пивота и скорректированную по координате Y. Для привязки используется "clrBlack", размер шрифта 11 и статус пивота "isHigh". Наконец вычислим центральную позицию метки в точке "centralTime" как середину между "X.time" и "B.time", а также "centralPrice" в точке "D.price", создавая текстовый объект с помощью ObjectCreate с именем "signalPrefix + '_Text_Center'". Установим OBJPROP_TEXT в значение "Bullish Crab" или "Bearish Crab" на основе "patternType", и настраиваем "OBJPROP_COLOR" в значение "clrBlack", "OBJPROP_FONTSIZE" - на 11, "OBJPROP_FONT" на "Arial Bold" и "OBJPROP_ALIGN" на "ALIGN_CENTER" с помощью ObjectSetString и "ObjectSetInteger". Это обеспечивает полное визуальное представление структуры и типа паттерна "Краб" на графике. При запуске программы получим следующий результат.

CRAB PATTERN WITH EDGES AND LABELS

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

datetime lineStart = D.time;                                     //--- Set start time for trade level lines
datetime lineEnd = D.time + PeriodSeconds(_Period) * 2;          //--- Set end time for trade level lines
double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff; //--- Declare trade level variables
if (patternType == "Bullish") {                                  //--- Handle bullish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK);      //--- Set entry at ask price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = TP3Level - entryPriceLevel;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel + tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel + 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
} else {                                                         //--- Handle bearish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID);      //--- Set entry at bid price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = entryPriceLevel - TP3Level;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel - tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel - 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
}
DrawDottedLine(signalPrefix + "_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);  //--- Draw entry level line
DrawDottedLine(signalPrefix + "_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);       //--- Draw TP1 level line
DrawDottedLine(signalPrefix + "_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);             //--- Draw TP2 level line
DrawDottedLine(signalPrefix + "_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);         //--- Draw TP3 level line
datetime labelTime = lineEnd + PeriodSeconds(_Period) / 2;                                     //--- Set time for trade level labels
string entryLabel = patternType == "Bullish" ? "BUY (" : "SELL (";                             //--- Start entry label text
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";                                  //--- Append entry price
DrawTextEx(signalPrefix + "_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true); //--- Draw entry label
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";                           //--- Create TP1 label text
DrawTextEx(signalPrefix + "_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true); //--- Draw TP1 label
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";                           //--- Create TP2 label text
DrawTextEx(signalPrefix + "_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);     //--- Draw TP2 label
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";                           //--- Create TP3 label text
DrawTextEx(signalPrefix + "_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true); //--- Draw TP3 label

Здесь мы продолжим определять и визуализировать торговые уровни для обнаруженного паттерна. Сначала установим значение "lineStart" на время пивота D ("D.time"), а "lineEnd" - на два периода вперед, используя "PeriodSeconds(_Period) * 2". Объявим переменные "entryPriceLevel", "TP1Level", "TP2Level", "TP3Level" и "tradeDiff" для расчетов сделок. Затем, для бычьего паттерна ("patternType == 'Bullish'"), установим "entryPriceLevel" равным текущей цене Ask с помощью SymbolInfoDouble, "TP3Level" равным цене пивота C, вычислим "tradeDiff" как "TP3Level - entryPriceLevel", а "TP1Level" и "TP2Level" вычислим как одну треть и две трети от "tradeDiff", прибавленные к "entryPriceLevel". Для медвежьего паттерна используем цену Bid, установим "TP3Level" равным цене точки C, вычислим "tradeDiff" как "entryPriceLevel - TP3Level", а "TP1Level" и "TP2Level" вычислим, вычитая одну треть и две трети от разницы между ценами сделок.

Далее, используя функцию "DrawDottedLine", нарисуем четыре пунктирные горизонтальные линии: линию входа на уровне "entryPriceLevel" пурпурного цвета и линии тейк-профита на уровнях "TP1Level" (глубокий лесной зеленый), "TP2Level" (зеленый) и "TP3Level" (темно-зеленый), от "lineStart" до "lineEnd". Наконец, установим значение "labelTime" равным "lineEnd" плюс половина периода, создадим текстовые метки с ценами, отформатированными с помощью DoubleToString (например, "BUY (price)" or "SELL (price)" для входа, "TP1 (price)" и т. д.). Используем "DrawTextEx" для отрисовки этих меток в "labelTime" с соответствующими цветами, размером шрифта 11 и привязкой над уровнями цен. После компиляции получаем следующий результат.

Медвежий паттерн:

BEARISH

Бычий паттерн:

BULLISH

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

int currentBarIndex = Bars(_Symbol, _Period) - 1;       //--- Retrieve current bar index
if (g_patternFormationBar == -1) {                      //--- Check if no pattern is locked
   g_patternFormationBar = currentBarIndex;             //--- Lock current bar as formation bar
   g_lockedPatternX = X.time;                           //--- Lock X pivot time
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar."); //--- Log detection
   return;                                              //--- Exit function
}
if (currentBarIndex == g_patternFormationBar) {         //--- Check if still on formation bar
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet."); //--- Log repainting
   return;                                              //--- Exit function
}
if (currentBarIndex > g_patternFormationBar) {          //--- Check if new bar after formation
   if (g_lockedPatternX == X.time) {                    //--- Verify same X pivot for confirmation
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, "."); //--- Log confirmed pattern
      g_patternFormationBar = currentBarIndex;          //--- Update formation bar to current
      if (AllowTrading && !PositionSelect(_Symbol)) {   //--- Check trading allowed and no open position
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0; //--- Declare trade parameters
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Update point value
         bool tradeResult = false;                      //--- Initialize trade result flag
         if (patternType == "Bullish") {                //--- Process bullish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Set entry at ask price
            double diffTrade = TP2Level - entryPriceTrade; //--- Calculate trade distance
            stopLoss = entryPriceTrade - diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute buy trade
            if (tradeResult) {                          //--- Check trade success
               Print("Buy order opened successfully."); //--- Log successful buy
            } else {                                    //--- Handle trade failure
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         } else if (patternType == "Bearish") {         //--- Process bearish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Set entry at bid price
            double diffTrade = entryPriceTrade - TP2Level; //--- Calculate trade distance
            stopLoss = entryPriceTrade + diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute sell trade
            if (tradeResult) {                          //--- Check trade success
               Print("Sell order opened successfully."); //--- Log successful sell
            } else {                                    //--- Handle trade failure
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         }
      } else {                                          //--- Trading not allowed or position exists
         Print("A position is already open for ", _Symbol, ". No new trade executed."); //--- Log no trade
      }
   } else {                                            //--- Pattern has changed
      g_patternFormationBar = currentBarIndex;         //--- Update formation bar
      g_lockedPatternX = X.time;                       //--- Update locked X pivot
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation."); //--- Log pattern change
      return;                                          //--- Exit function
   }
}
} else {                                               //--- No valid pattern detected
   g_patternFormationBar = -1;                         //--- Reset formation bar
   g_lockedPatternX = 0;                               //--- Reset locked X pivot
}

Сначала получим индекс текущего бара с помощью функции "Bars(_Symbol, _Period) - 1" и сохраним его в "currentBarIndex". Затем, если ни один паттерн не заблокирован ("g_patternFormationBar == -1"), установим "g_patternFormationBar" в "currentBarIndex", сохраняем время пивота X в "g_lockedPatternX" с помощью "X.time", регистрируем обнаружение, указывающее на ожидание подтверждения. Завершаем работу. Далее, если паттерн все еще находится на формирующем баре ("currentBarIndex == g_patternFormationBar"), фиксируем, что паттерн перерисовывается, и выходим, чтобы избежать преждевременной торговли.

Наконец, если сформировался новый бар ("currentBarIndex > g_patternFormationBar") и пивот X совпадает с "g_lockedPatternX", подтверждаем паттерн, фиксируем его, обновляем "g_patternFormationBar" и проверяем, разрешена ли торговля с помощью "AllowTrading" и нет ли открытых позиций с помощью PositionSelect. Для бычьего паттерна установим "entryPriceTrade" равным цене Ask, рассчитаем "diffTrade" как "TP2Level - entryPriceTrade", установим "stopLoss" в три раза больше этого значения расстояния ниже, установим "takeProfit" равным "TP2Level" и откроем сделку на покупку с помощью "obj_Trade.Buy", используя "LotSize" и комментарий "Crab Signal", регистрируем успешное или неудачное выполнение. Для медвежьего паттерна мы используем цену Bid, установим "stopLoss" в три раза больше и откроем сделку на продажу с помощью "obj_Trade.Sell". Если торговля запрещена или позиция уже существует, новая сделка не открывается. Если паттерн меняется, обновим сохранение и ждем. Если паттерн не найден, сбросим "g_patternFormationBar" и "g_lockedPatternX", обеспечивая, чтобы подтвержденные паттерны "Краб" запускали сделки с точным управлением рисками. Наконец, нам просто нужно удалить паттерны с графика при удалении программы.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0, "CR_");                    //--- Remove all chart objects with "CR_" prefix
   ArrayResize(pivots, 0);                        //--- Clear pivots array
   g_patternFormationBar = -1;                    //--- Reset pattern formation bar index
   g_lockedPatternX = 0;                          //--- Reset locked pattern X pivot time
   ChartRedraw(0);                                //--- Redraw chart to reflect changes
}

Здесь мы реализуем обработчик OnDeinit, чтобы обеспечить надлежащую очистку при удалении советника с графика. Сначала удалим все объекты графика с префиксом "CR_", используя ObjectsDeleteAll для очистки визуальных элементов, таких как треугольники, линии тренда и метки, связанных с паттернами "Краб". Затем перейдем к изменению размера массива "pivots" на 0 с помощью ArrayResize для очистки сохраненных данных о пивотах. Затем сбросим значение "g_patternFormationBar" на -1 и "g_lockedPatternX" на 0, чтобы очистить переменные отслеживания паттернов. В конце вызовем ChartRedraw, чтобы обновить график, убедившись, что отражено удаление всех объектов и данных. Это обеспечивает чистый выход, высвобождая ресурсы и предотвращая появление остаточных элементов. После компиляции получаем следующий результат.

Медвежий сигнал:

BEARISH SIGNAL

Бычий сигнал:

BULLISH SIGNAL

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


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

После тщательного тестирования на истории мы получили следующие результаты.

График тестирования на истории:

График

Отчет о тестировании на истории:

REPORT


Заключение

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

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

Используя представленные концепции и методы реализации, можно адаптировать эту систему паттерна "Краб" к своему стилю торговли, улучшая свои алгоритмические стратегии. Удачной торговли! 

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

Прикрепленные файлы |
Crab_Pattern_EA.mq5 (47.93 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (7)
Maxim Kuznetsov
Maxim Kuznetsov | 15 мая 2026 в 12:12
Andrey F. Zelinsky #:
чем гармонический паттерн краб отличается от бабочки гартли

вообще ничем :-)

правый выступ увеличен на 1.68, то есть та-же бабочка, но с приделкой ещё одной фибо. Будешь делать - код один и тот-же. 

Andrey F. Zelinsky
Andrey F. Zelinsky | 15 мая 2026 в 13:17
Maxim Kuznetsov #:

вообще ничем :-)

правый выступ увеличен на 1.68, то есть та-же бабочка, но с приделкой ещё одной фибо. Будешь делать - код один и тот-же

любой паттерн это не про код, а про сигнал.

типы сигналов у паттернов разные -- это означает, что один паттерн пригоден для применения в одной ситуации, а другой паттерн -- в другой.

Vitaly Muzichenko
Vitaly Muzichenko | 15 мая 2026 в 13:45
Andrey F. Zelinsky #:

любой паттерн это не про код, а про сигнал.

типы сигналов у паттернов разные -- это означает, что один паттерн пригоден для применения в одной ситуации, а другой паттерн -- в другой.

... и при этом результат всё тот-же = 50/50
Andrey F. Zelinsky
Andrey F. Zelinsky | 15 мая 2026 в 13:59
Vitaly Muzichenko #:
... и при этом результат всё тот-же = 50/50

в статье про паттерн, а не про философию сигнала.

философия сигнала давно описана, обыграна и предельно понятно пояснена, например в культовом фильме "Волк из Уолл-Стрит" в диалоге Марка Ханна и Джордана Белфорта:

Видео в русском переводе здесь: https://www.youtube.com/watch?v=fRVwyZ6t7Fk&t=112s

Цитата: "Никто не знает пойдёт ли цена акции вверх, вниз, вбок или кругами -- это всё хумера, химера, хумер его поймёт"

Andrey F. Zelinsky
Andrey F. Zelinsky | 15 мая 2026 в 14:16
Vitaly Muzichenko #:
... и при этом результат всё тот-же = 50/50

только кроме одного сигнала от паттерна -- есть совокупность разнотипных сигналов (сигналы на вход/выход, фильтрующие), есть манименеджмент -- в итоге вероятность на выходе далеко не 50/50

Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Сила MetaTrader 5: от пошаговой отладки до защиты EX5 в одной среде Сила MetaTrader 5: от пошаговой отладки до защиты EX5 в одной среде
В статье рассматривается комплексный подход к разработке торговых алгоритмов: от настройки проекта и отладки логики до защиты готового продукта. Разбираются встроенные инструменты MetaEditor, включая пошаговый дебаггинг на реальных тиках, профилирование производительности и прямую интеграцию с C++ DLL для ускорения вычислений. Описывается методика защиты интеллектуальной собственности с помощью MQL5 Cloud Protector. Применение описанных техник позволяет превратить разработку эксперта из хаотичного поиска решений в системный процесс, существенно сокращая время разработки стратегии.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Нейросети в трейдинге: Внимание, память и рыночные паттерны в GDformer (Global Dictionary) Нейросети в трейдинге: Внимание, память и рыночные паттерны в GDformer (Global Dictionary)
Представлена реализация основного модуля GDformer — Global Dictionary-based Cross-Attention — для анализа финансовых временных рядов в среде MQL5/OpenCL. Описаны глобальный словарь паттернов, многоголовое кросс-внимание, ветка сходства с обучаемыми прототипами и разреженный SoftMax без повторной нормализации. Показано, как получать устойчивое контекстное представление рыночного состояния для последующего использования в торговой инфраструктуре.