Автоматизация торговых стратегий в MQL5 (Часть 27): Выявление и визуализация гармонического паттерна "Краб" на основе Price Action
Введение
В своей предыдущей статье (Часть 26) мы создали систему усреднения на основе пин-баров в MetaQuotes Language 5 (MQL5). В ней используются свечные паттерны пин-баров для открытия сделок и управления несколькими позициями с помощью стратегии усреднения, она дополнена динамическим дашбордом для мониторинга в реальном времени. В Части 27 мы создадим систему паттерна "Краб", которая определяет бычьи и медвежьи гармонические паттерны "Краб" с использованием точек разворота и уровней Фибоначчи, автоматизируя сделки с точными уровнями входа, стоп-лосса и тейк-профита. Она дополнена визуальными графическими объектами, такими как треугольники и линии тренда, для четкого отображения паттернов. В статье рассмотрим следующие темы:
- Изучение структуры гармонического паттерна "Краб"
- Реализация средствами MQL5
- Тестирование на истории
- Заключение
В итоге у вас будет тщательно продуманная стратегия в 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). Ниже представлены визуализированные типы паттернов.
Бычий гармонический паттерн "Краб":

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

Для выявления паттернов ниже представлен наш структурированный подход:
- Определение отезка 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" и сохраним. При выводе массива на экран получим следующий результат.

С помощью этих данных мы можем выделить точки разворота, и если у нас будет достаточно пивотов, сможем проанализировать и обнаружить паттерны. Вот логика, которую мы реализуем для достижения этой цели.
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. А также включим заливку и отображение фона, обеспечивая чёткую идентификацию и визуальное представление обнаруженных паттернов "Краб" для принятия торговых решений. Мы выполнили следующий этап.

На изображении видно, что мы можем корректно отобразить и визуализировать обнаруженный паттерн. Теперь нам нужно продолжить нанесение линий тренда на график, чтобы полностью отобразить паттерн в его границах, и добавить к ним метку для облегчения идентификации уровней.
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". Это обеспечивает полное визуальное представление структуры и типа паттерна "Краб" на графике. При запуске программы получим следующий результат.

На изображении видно, что мы добавили края и метки на паттерн, сделав его более наглядным. Что нам нужно сделать дальше, так это определить торговые уровни для данного паттерна.
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 и привязкой над уровнями цен. После компиляции получаем следующий результат.
Медвежий паттерн:

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

На изображениях видно, что мы корректно нанесли торговые уровни. Что нам нужно сделать сейчас, так это инициировать реальные торговые позиции, и это все.
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, чтобы обновить график, убедившись, что отражено удаление всех объектов и данных. Это обеспечивает чистый выход, высвобождая ресурсы и предотвращая появление остаточных элементов. После компиляции получаем следующий результат.
Медвежий сигнал:

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

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

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

Заключение
В заключение отметим, что нами разработана система паттерн "Краб" в MQL5. Он использует движения цены для выявления бычьих и медвежьих гармонических паттернов "Краб" с точными уровнями Фибоначчи, автоматизирует сделки с расчетными точками входа, стоп-лосса и многоуровневыми точками тейк-профита, визуализированными с помощью динамических графических объектов, таких как треугольники и линии тренда.
Отказ от ответственности: Содержание настоящей статьи предназначено только для целей обучения. Торговля сопряжена со значительными финансовыми рисками, а волатильность рынка может привести к убыткам. Тщательное тестирование на истории и управление рисками имеют решающее значение перед внедрением этой программы в реальных условиях рынка.
Используя представленные концепции и методы реализации, можно адаптировать эту систему паттерна "Краб" к своему стилю торговли, улучшая свои алгоритмические стратегии. Удачной торговли!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19099
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Сила MetaTrader 5: от пошаговой отладки до защиты EX5 в одной среде
Нейросети в трейдинге: Внимание, память и рыночные паттерны в GDformer (Global Dictionary)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
чем гармонический паттерн краб отличается от бабочки гартли
вообще ничем :-)
правый выступ увеличен на 1.68, то есть та-же бабочка, но с приделкой ещё одной фибо. Будешь делать - код один и тот-же.
вообще ничем :-)
правый выступ увеличен на 1.68, то есть та-же бабочка, но с приделкой ещё одной фибо. Будешь делать - код один и тот-же.
любой паттерн это не про код, а про сигнал.
типы сигналов у паттернов разные -- это означает, что один паттерн пригоден для применения в одной ситуации, а другой паттерн -- в другой.
любой паттерн это не про код, а про сигнал.
типы сигналов у паттернов разные -- это означает, что один паттерн пригоден для применения в одной ситуации, а другой паттерн -- в другой.
... и при этом результат всё тот-же = 50/50
в статье про паттерн, а не про философию сигнала.
философия сигнала давно описана, обыграна и предельно понятно пояснена, например в культовом фильме "Волк из Уолл-Стрит" в диалоге Марка Ханна и Джордана Белфорта:
Видео в русском переводе здесь: https://www.youtube.com/watch?v=fRVwyZ6t7Fk&t=112s
Цитата: "Никто не знает пойдёт ли цена акции вверх, вниз, вбок или кругами -- это всё хумера, химера, хумер его поймёт"
... и при этом результат всё тот-же = 50/50
только кроме одного сигнала от паттерна -- есть совокупность разнотипных сигналов (сигналы на вход/выход, фильтрующие), есть манименеджмент -- в итоге вероятность на выходе далеко не 50/50