Автоматизация торговых стратегий на MQL5 (Часть 15): Гармонический паттерн «Шифр» (Cypher) ценового действия с визуализацией
Введение
В нашей предыдущей статье (Часть 14) мы разработали стратегию лейеринга с использованием Индикатора схождения-расхождения скользящих средних (MACD) и Индикатора относительной силы (RSI) со статистическими методами для динамичного масштабирования позиций на трендовых рынках. Теперь, в части 15, мы сосредоточимся на автоматизации гармонического паттерна «Шифр» (Cypher), разворотного паттерна на основе Фибоначчи, с помощью советника (EA), который обнаруживает, визуализирует и торгует этой структурой на MetaQuotes Language 5 (MQL5). В статье рассмотрим следующие темы:
- Изучение архитектуры паттерна «Шифр»
- Реализация средствами MQL5
- Тестирование на истории и оптимизация
- Заключение
К концу настоящей статьи у вас будет полностью функциональная программа, которая распознает паттерны «Шифр», аннотирует графики с помощью четких визуальных элементов и точно исполняет сделки. Давайте погрузимся в работу!
Изучение архитектуры паттерна «Шифр»
Паттерн «Шифр» — это гармоничная торговая формация, определяемая пятью ключевыми точками колебания — X, A, B, C и D - и существующая в двух формах: бычий паттерн и медвежий паттерн. При бычьем «Шифре» структура образует последовательность «минимум-максимум-минимум-максимум-минимум», где точка X - минимум колебаний, точка A - максимум колебаний, точка B - минимум колебаний, точка C - максимум колебаний и точка D - минимум колебаний (при этом D расположена ниже X). И наоборот, медвежий «Шифр» формирует последовательность "максимум-минимум-максимум-минимум-максимум", где точка X является максимумом колебаний, а точка D расположена над X. Ниже приведены визуализируемые типы паттернов.
Бычий Гармонический паттерн «Шифр» (Cypher):

Медвежий Гармонический паттерн «Шифр» (Cypher):

Для выявления паттернов ниже представлен наш структурированный подход:
- Определение стороны "XA": Начальное движение от точки X к точке A устанавливает контрольное расстояние для паттерна, задавая направление (вверх для медвежьего тренда, вниз для бычьего).
- Создание стороны "AB": Для обоих типов паттернов, точка В должна выполнить возврат от 38,2% до 61,8% расстояния XA, подтверждая умеренную коррекцию первоначального движения.
- Анализ стороны "BC": Эта сторона должна распространяться от 127,2% до 141,4% стороны AB, обеспечивая сильное встречное движение перед последней стороной.
- Настройка стороны "CD": Последняя сторона должна выполнить обратный ход примерно 78,6% хода XC (от X до C), обозначая потенциальную зону разворота.
Применяя эти геометрические критерии и критерии, основанные на Фибоначчи, наша торговая система будет систематически выявлять достоверные паттерны «Шифр» в исторических ценовых данных. Как только паттерн будет подтвержден, программа визуализирует образование на графике с помощью аннотированных треугольников, линий тренда и меток для точек X, A, B, C и D, а также уровней сделок. Эта настройка позволяет автоматически совершать сделки на основе рассчитанных уровней входа, стоп-лосса и тейк-профита, используя возможности паттерна для прогнозирования разворотов рынка.
Реализация средствами MQL5
Чтобы создать программу на MQL5, откройте MetaEditor, перейдите в Навигатор, найдите папку «Индикаторы» (Indicators), перейдите на вкладку "Создать" (New) и следуйте инструкциям по созданию файла. Как только это будет сделано, в среде программирования нам нужно будет объявить некоторые глобальные переменные, которые будем использовать во всей программе.
//+------------------------------------------------------------------+ //| 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 Cypher Strategy with visualization" #property strict //--- Forces strict coding rules to catch errors early //--- Include the Trade library from MQL5 to handle trading operations like buying and selling #include <Trade\Trade.mqh> //--- Create an instance (object) of the CTrade class to use for placing trades CTrade obj_Trade; //--- Input parameters let the user customize the EA without editing the code input int SwingHighCount = 5; // How many bars to check on the left to find a swing point (high or low) input int SwingLowCount = 5; // How many bars to check on the right to confirm a swing point input double FibonacciTolerance = 0.10; // Allowed error margin (10%) for Fibonacci ratios in the pattern input double TradeVolume = 0.01; // Size of the trade (e.g., 0.01 lots is small for testing) input bool TradingEnabled = true; // True = EA can trade; False = only visualize patterns //--- Define the Cypher pattern rules as a comment for reference //--- Bullish Cypher: X (low), A (high), B (low), C (high), D (low) //--- XA > 0; AB = 0.382-0.618 XA; BC = 1.272-1.414 AB; CD = 0.786 XC; D < X //--- Bearish Cypher: X (high), A (low), B (high), C (low), D (high) //--- XA > 0; AB = 0.382-0.618 XA; BC = 1.272-1.414 AB; CD = 0.786 XC; D > X //--- Define a structure (like a custom data type) to store swing point info struct SwingPoint { datetime TimeOfSwing; //--- When the swing happened (date and time of the bar) double PriceAtSwing; //--- Price at the swing (high or low) bool IsSwingHigh; //--- True = swing high; False = swing low }; //--- Create a dynamic array to hold all detected swing points SwingPoint SwingPoints[];
Здесь мы приступаем к реализации торговой системы паттерн «Шифр» на языке MetaQuotes Language 5, включив библиотеку "Trade.mqh" для обеспечения торговых операций и создав экземпляр класса "CTrade" с именем "obj_Trade" для исполнения сделок.
Определяем входные параметры — "SwingHighCount" и "SwingLowCount" (оба по 5) для определения точки колебания, "FibonacciTolerance" (0,10) для определения гибкости соотношения Фибоначчи, "TradeVolume" (0,01 лота) для определения размера сделки и "TradingEnabled" (true) для переключения торговли, что обеспечит возможность осуществлять пользовательские настройки.
Структура "SwingPoint" определена с помощью "TimeOfSwing" (datetime), "PriceAtSwing" (double) и "IsSwingHigh" (boolean) для хранения сведений о точках колебания, а динамический массив "SwingPoints" содержит все обнаруженные точки колебания для анализа шаблона.
Далее мы можем определить функции, которые помогут нам визуализировать паттерны на графике.
//+------------------------------------------------------------------+ //| Helper: Draw a filled triangle | //+------------------------------------------------------------------+ //--- Function to draw a triangle on the chart to highlight pattern segments void DrawTriangle(string TriangleName, datetime Time1, double Price1, datetime Time2, double Price2, datetime Time3, double Price3, color LineColor, int LineWidth, bool FillTriangle, bool DrawBehind) { //--- Create a triangle object using three points (time, price) on the chart if(ObjectCreate(0, TriangleName, OBJ_TRIANGLE, 0, Time1, Price1, Time2, Price2, Time3, Price3)) { ObjectSetInteger(0, TriangleName, OBJPROP_COLOR, LineColor); //--- Set the triangle’s color (e.g., blue or red) ObjectSetInteger(0, TriangleName, OBJPROP_STYLE, STYLE_SOLID); //--- Use a solid line style ObjectSetInteger(0, TriangleName, OBJPROP_WIDTH, LineWidth); //--- Set the line thickness ObjectSetInteger(0, TriangleName, OBJPROP_FILL, FillTriangle); //--- Fill the triangle with color if true ObjectSetInteger(0, TriangleName, OBJPROP_BACK, DrawBehind); //--- Draw behind candles if true } }
Здесь мы реализуем функцию "DrawTriangle" для улучшения визуализации паттерна «Шифр», рисуя треугольник с заливкой на графике MetaTrader 5, выделяя определенные сегменты паттерна для лучшего понимания трейдером. Функция принимает несколько параметров для определения внешнего вида и положения треугольника: "TriangleName" (строка) предоставляет уникальный идентификатор для объекта, "Time1", "Time2" и "Time3" (datetime) задают временные координаты трех вершин треугольника, а "Price1", "Price2" и "Price3" (double) задают соответствующие уровни цен.
Дополнительные параметры включают "LineColor" (color) для определения цвета контура (например, синий для бычьих паттернов, красный - для медвежьих), "LineWidth" (int) для задания толщины границ треугольника, "FillTriangle" (bool) для определения того, будет ли треугольник иметь цветную заливку, а также "DrawBehind" (bool) позволяет контролировать, отображается ли треугольник за свечами графика, чтобы избежать искажения ценовых данных.
В рамках этой функции мы используем функцию ObjectCreate для создания треугольного объекта на графике, с указанием типа объекта как OBJ_TRIANGLE и передачей предоставленных временных и ценовых координат для трех точек. Функция проверяет, прошло ли создание объекта успешно, прежде чем приступить к настройке его свойств.
Если создание завершается успешно, вызываем функцию ObjectSetInteger несколько раз, чтобы задать атрибуты треугольника: OBJPROP_COLOR присваивает значение "LineColor", "OBJPROP_STYLE" имеет значение "STYLE_SOLID" для сплошного контура, "OBJPROP_WIDTH" применяет значение "LineWidth", "OBJPROP_FILL" использует логическое значение "FillTriangle" для включения или отключения заливки, а OBJPROP_BACK использует логическое значение "DrawBehind" чтобы гарантировать, что треугольник появляется позади свечей при значении true.
Теперь мы можем определить остальные вспомогательные функции с помощью той же логики.
//+------------------------------------------------------------------+ //| Helper: Draw a trend line | //+------------------------------------------------------------------+ //--- Function to draw a straight line between two points on the chart void DrawTrendLine(string LineName, datetime StartTime, double StartPrice, datetime EndTime, double EndPrice, color LineColor, int LineWidth, int LineStyle) { //--- Create a trend line object connecting two points (start time/price to end time/price) if(ObjectCreate(0, LineName, OBJ_TREND, 0, StartTime, StartPrice, EndTime, EndPrice)) { ObjectSetInteger(0, LineName, OBJPROP_COLOR, LineColor); //--- Set the line color ObjectSetInteger(0, LineName, OBJPROP_STYLE, LineStyle); //--- Set line style (e.g., solid or dashed) ObjectSetInteger(0, LineName, OBJPROP_WIDTH, LineWidth); //--- Set line thickness ObjectSetInteger(0, LineName, OBJPROP_BACK, true); } } //+------------------------------------------------------------------+ //| Helper: Draw a dotted trend line | //+------------------------------------------------------------------+ //--- Function to draw a horizontal dotted line (e.g., for entry or take-profit levels) void DrawDottedLine(string LineName, datetime StartTime, double LinePrice, datetime EndTime, color LineColor) { //--- Create a horizontal line from start time to end time at a fixed price if(ObjectCreate(0, LineName, OBJ_TREND, 0, StartTime, LinePrice, EndTime, LinePrice)) { ObjectSetInteger(0, LineName, OBJPROP_COLOR, LineColor); //--- Set the line color ObjectSetInteger(0, LineName, OBJPROP_STYLE, STYLE_DOT); //--- Use dotted style ObjectSetInteger(0, LineName, OBJPROP_WIDTH, 1); //--- Thin line } } //+------------------------------------------------------------------+ //| Helper: Draw anchored text label (for pivots and levels) | //+------------------------------------------------------------------+ //--- Function to place text labels (e.g., "X" or "TP1") on the chart void DrawTextLabel(string LabelName, string LabelText, datetime LabelTime, double LabelPrice, color TextColor, int FontSize, bool IsAbove) { //--- Create a text object at a specific time and price if(ObjectCreate(0, LabelName, OBJ_TEXT, 0, LabelTime, LabelPrice)) { ObjectSetString(0, LabelName, OBJPROP_TEXT, LabelText); //--- Set the text to display ObjectSetInteger(0, LabelName, OBJPROP_COLOR, TextColor); //--- Set text color ObjectSetInteger(0, LabelName, OBJPROP_FONTSIZE, FontSize); //--- Set text size ObjectSetString(0, LabelName, OBJPROP_FONT, "Arial Bold"); //--- Use bold Arial font //--- Position text below if it’s a high point, above if it’s a low point ObjectSetInteger(0, LabelName, OBJPROP_ANCHOR, IsAbove ? ANCHOR_BOTTOM : ANCHOR_TOP); ObjectSetInteger(0, LabelName, OBJPROP_ALIGN, ALIGN_CENTER); //--- Center the text } }
Здесь мы реализуем функцию "DrawTrendLine", чтобы нарисовать прямую линию, соединяющую паттерн «Шифр» с точками поворота на графике, используя параметры "LineName" (string), "StartTime", "EndTime" (datetime), "StartPrice", "EndPrice" (double), "LineColor" (color), "LineWidth" (int) и "LineStyle" (int). Используем функцию ObjectCreate для создания линии "OBJ_TREND" и, в случае успешного выполнения, задаем "OBJPROP_COLOR", "OBJPROP_STYLE", "OBJPROP_WIDTH" и "OBJPROP_BACK" (true) с помощью "ObjectSetInteger" для видимости за свечами.
Функция "DrawDottedLine" рисует горизонтальную пунктирную линию для торговых уровней, используя "LineName" (string), "StartTime", "EndTime" (datetime), "LinePrice" (double) и "LineColor" (color). Создаем объект OBJ_TREND с помощью "ObjectCreate" по фиксированной цене и устанавливаем значение "OBJPROP_COLOR", "OBJPROP_STYLE" для "STYLE_DOT", а для "OBJPROP_WIDTH" значение 1, используя ObjectSetInteger в качестве незаметного маркера.
Функция "DrawTextLabel" помещает текстовые метки для точек колебания или торговых уровней, используя "LabelName", "LabelText" (string), "LabelTime" (datetime), "LabelPrice" (double), "TextColor" (color), "FontSize" (int) и "IsAbove" (bool). Создаем объект OBJ_TEXT с помощью "ObjectCreate" и используем "ObjectSetString" для "OBJPROP_TEXT" и "OBJPROP_FONT" ("Arial Bold"), а также "ObjectSetInteger" для "OBJPROP_COLOR", "OBJPROP_FONTSIZE", "OBJPROP_ANCHOR" ("ANCHOR_BOTTOM" или "ANCHOR_TOP") и "OBJPROP_ALIGN" (ALIGN_CENTER) для обеспечения прозрачных аннотаций.
Вооружившись этими переменными и функциями, мы можем перейти к обработчику событий OnTick и начать распознавание паттернов. Однако, поскольку нам не нужно будет обрабатывать что-либо на каждом тике, необходимо определить логику, которую мы можем использовать для обработки идентификации один раз за бар.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ //--- Main function that runs every time a new price tick arrives void OnTick() { //--- Use a static variable to track the last bar’s time so we only process new bars static datetime LastProcessedBarTime = 0; //--- Get the time of the second-to-last bar (latest complete bar) datetime CurrentBarTime = iTime(_Symbol, _Period, 1); //--- If no new bar has formed, exit to avoid over-processing if(CurrentBarTime == LastProcessedBarTime) return; LastProcessedBarTime = CurrentBarTime; //--- Update to the current bar }
В обработчике событий OnTick, который служит основным обработчиком событий, который задействуется каждый раз, когда на платформе MetaTrader 5 появляется новый ценовой тик, объявляем статическую переменную "LastProcessedBarTime" для отслеживания временной метки последнего обработанного бара, гарантируя, что функция обрабатывает только новые бары для оптимизации эффективности.
Используя функцию iTime, извлекаем время предпоследнего бара (последнего полного бара) и сохраняем его в "CurrentBarTime". Затем сравниваем "CurrentBarTime" с "LastProcessedBarTime", чтобы проверить, сформировался ли новый бар. Если они равны, выходим из функции с помощью оператора return, чтобы избежать избыточной обработки.
При обнаружении нового бара обновляем "LastProcessedBarTime" на "CurrentBarTime", позволяя функции перейти к последующей логике для анализа ценовых данных и обнаружения паттернов «Шифр». Далее нам нужно определить переменные, которые помогут определить уровни точек колебания.
//--- Clear the SwingPoints array to start fresh each time ArrayResize(SwingPoints, 0); //--- Get the total number of bars on the chart int TotalBars = Bars(_Symbol, _Period); int StartBarIndex = SwingHighCount; //--- Start checking swings after SwingHighCount bars int EndBarIndex = TotalBars - SwingLowCount; //--- Stop before the last SwingLowCount bars
Используем функцию ArrayResize, чтобы очистить массив "SwingPoints", установив его размер равным 0, что обеспечивает новый старт для сохранения новых точек колебания на каждом новом баре. Затем извлекаем общее количество баров на графике с помощью функции Bars, сохраняя результат в "TotalBars", который определяет объем исторических данных для анализа.
Чтобы сосредоточиться на соответствующих барах для определения колебаний, устанавливаем для параметра "StartBarIndex" значение "SwingHighCount", отмечая самый ранний бар для проверки колебаний, и вычисляем "EndBarIndex" как "TotalBars" минус "SwingLowCount", гарантируя, что остановимся перед последними несколькими барами, чтобы получить достаточно данных для подтверждения точек колебаний.
С их помощью мы можем выполнять цикл и собирать данные о точках колебаний.
//--- Loop through bars to find swing highs and lows (swing points) for(int BarIndex = EndBarIndex - 1; BarIndex >= StartBarIndex; BarIndex--) { bool IsSwingHigh = true; //--- Assume it’s a high until proven otherwise bool IsSwingLow = true; //--- Assume it’s a low until proven otherwise double CurrentBarHigh = iHigh(_Symbol, _Period, BarIndex); //--- Get the high of this bar double CurrentBarLow = iLow(_Symbol, _Period, BarIndex); //--- Get the low of this bar //--- Check bars to the left and right to confirm it’s a swing point for(int NeighborIndex = BarIndex - SwingHighCount; NeighborIndex <= BarIndex + SwingLowCount; NeighborIndex++) { if(NeighborIndex < 0 || NeighborIndex >= TotalBars || NeighborIndex == BarIndex) //--- Skip invalid bars or current bar continue; if(iHigh(_Symbol, _Period, NeighborIndex) > CurrentBarHigh) //--- If any bar is higher, not a high IsSwingHigh = false; if(iLow(_Symbol, _Period, NeighborIndex) < CurrentBarLow) //--- If any bar is lower, not a low IsSwingLow = false; } //--- If it’s a high or low, store it in the SwingPoints array if(IsSwingHigh || IsSwingLow) { SwingPoint NewSwing; NewSwing.TimeOfSwing = iTime(_Symbol, _Period, BarIndex); //--- Store the bar’s time NewSwing.PriceAtSwing = IsSwingHigh ? CurrentBarHigh : CurrentBarLow; //--- Store high or low price NewSwing.IsSwingHigh = IsSwingHigh; //--- Mark as high or low int CurrentArraySize = ArraySize(SwingPoints); //--- Get current array size ArrayResize(SwingPoints, CurrentArraySize + 1); //--- Add one more slot SwingPoints[CurrentArraySize] = NewSwing; //--- Add the swing to the array } }
Здесь мы реализуем логику определения точки колебания, чтобы определить максимумы и минимумы колебания для паттерна «Шифр». Мы используем цикл for для перебора баров от "EndBarIndex - 1" до "StartBarIndex" в порядке убывания, при этом "BarIndex" отслеживает текущий бар. Для каждого бара инициализируем значения "IsSwingHigh" и "IsSwingLow" как true, предполагая, что бар является точкой колебания, пока это не будет опровергнуто, и извлекаем максимальную и низкую цены бара, используя функции iHigh и iLow, сохраняя их в "CurrentBarHigh" и "CurrentBarLow". Вложенный цикл for проверяет соседние бары от "BarIndex - SwingHighCount" до "BarIndex + SwingLowCount", используя "NeighborIndex" для пропуска недопустимых индексов или самого текущего бара, с помощью оператора continue.
Если максимум какого-либо соседнего бара превышает "CurrentBarHigh" или минимум падает ниже "CurrentBarLow" (через iHigh и iLow), мы устанавливаем значение "IsSwingHigh" или "IsSwingLow" равным false соответственно. Если какой-либо из них остается true, создаем экземпляр "SwingPoint" с именем "NewSwing", присваивая значение "TimeOfSwing" со временем бара из iTime, "PriceAtSwing" как "CurrentBarHigh" или "CurrentBarLow" на основании "IsSwingHigh" и "IsSwingHigh" соответственно.
Затем используем функцию "ArraySize", чтобы получить текущий размер "SwingPoints", увеличиваем его на единицу с помощью ArrayResize и сохраняем "NewSwing" в массиве "SwingPoints" с новым индексом, создавая коллекцию точек колебания для анализа паттернов. При выводе данных с помощью функции ArrayPrint(SwingPoints), мы получаем следующий результат.

С помощью этих данных мы можем выделить точки разворота, и если у нас будет достаточно пивотом, сможем проанализировать и обнаружить паттерны. Вот логика, которую мы реализуем для достижения этой цели.
//--- Check if we have enough swing points (need 5 for Cypher: X, A, B, C, D) int TotalSwingPoints = ArraySize(SwingPoints); if(TotalSwingPoints < 5) return; //--- Exit if not enough swing points //--- Assign the last 5 swing points to X, A, B, C, D (most recent is D) SwingPoint PointX = SwingPoints[TotalSwingPoints - 5]; SwingPoint PointA = SwingPoints[TotalSwingPoints - 4]; SwingPoint PointB = SwingPoints[TotalSwingPoints - 3]; SwingPoint PointC = SwingPoints[TotalSwingPoints - 2]; SwingPoint PointD = SwingPoints[TotalSwingPoints - 1]; //--- Variables to track if we found a pattern and its type bool PatternFound = false; string PatternDirection = ""; //--- Check for Bearish Cypher pattern if(PointX.IsSwingHigh && !PointA.IsSwingHigh && PointB.IsSwingHigh && !PointC.IsSwingHigh && PointD.IsSwingHigh) { double LegXA = PointX.PriceAtSwing - PointA.PriceAtSwing; //--- Calculate XA leg (should be positive) if(LegXA > 0) { double LegAB = PointB.PriceAtSwing - PointA.PriceAtSwing; //--- AB leg double LegBC = PointB.PriceAtSwing - PointC.PriceAtSwing; //--- BC leg double LegXC = PointX.PriceAtSwing - PointC.PriceAtSwing; //--- XC leg double LegCD = PointD.PriceAtSwing - PointC.PriceAtSwing; //--- CD leg //--- Check Fibonacci rules and D > X for bearish if(LegAB >= 0.382 * LegXA && LegAB <= 0.618 * LegXA && LegBC >= 1.272 * LegAB && LegBC <= 1.414 * LegAB && MathAbs(LegCD - 0.786 * LegXC) <= FibonacciTolerance * LegXC && PointD.PriceAtSwing > PointX.PriceAtSwing) { PatternFound = true; PatternDirection = "Bearish"; } } } //--- Check for Bullish Cypher pattern else if(!PointX.IsSwingHigh && PointA.IsSwingHigh && !PointB.IsSwingHigh && PointC.IsSwingHigh && !PointD.IsSwingHigh) { double LegXA = PointA.PriceAtSwing - PointX.PriceAtSwing; //--- Calculate XA leg (should be positive) if(LegXA > 0) { double LegAB = PointA.PriceAtSwing - PointB.PriceAtSwing; //--- AB leg double LegBC = PointC.PriceAtSwing - PointB.PriceAtSwing; //--- BC leg double LegXC = PointC.PriceAtSwing - PointX.PriceAtSwing; //--- XC leg double LegCD = PointC.PriceAtSwing - PointD.PriceAtSwing; //--- CD leg //--- Check Fibonacci rules and D < X for bullish if(LegAB >= 0.382 * LegXA && LegAB <= 0.618 * LegXA && LegBC >= 1.272 * LegAB && LegBC <= 1.414 * LegAB && MathAbs(LegCD - 0.786 * LegXC) <= FibonacciTolerance * LegXC && PointD.PriceAtSwing < PointX.PriceAtSwing) { PatternFound = true; PatternDirection = "Bullish"; } } }
Здесь мы продолжаем проверку паттерна «Шифр», проверяя наличие достаточных точек колебания и анализируя последние пять для формирования паттерна. Используем функцию ArraySize, чтобы определить количество элементов в массиве "SwingPoints", сохранив его в "TotalSwingPoints", и завершаем работу с оператором return, если "TotalSwingPoints" меньше 5, поскольку для паттерна «Шифр» требуется пять точек (X, A, B, C, D). Если точек достаточно, присваиваем последним пяти точкам колебания значения "PointX", "PointA", "PointB", "PointC" и "PointD" из массива "SwingPoints" с индексами от "TotalSwingPoints - 5" до "TotalSwingPoints - 1", где "PointD" - это самый последний.
Затем инициализируем "PatternFound" как значение false, чтобы отслеживать, обнаружен ли допустимый паттерн, и "PatternDirection" как пустую строку для хранения типа паттерна. Для проверки наличия медвежьего «Шифра», проверяем, что "PointX.IsSwingHigh" имеет значение true, "PointA.IsSwingHigh" - false, "PointB.IsSwingHigh" - true, "PointC.IsSwingHigh" - false и "PointD.IsSwingHigh" - true, обеспечивая последовательность максимум-минимум-максимум-минимум-максимум.
Если это значение равно true, мы вычисляем длины сторон: "LegXA" как "PointX.PriceAtSwing" минус "PointA.PriceAtSwing" (положительное для медвежьего), "LegAB" как "PointB.PriceAtSwing" минус "PointA.PriceAtSwing", "LegBC" как "PointB.PriceAtSwing" минус "PointC.PriceAtSwing", "LegXC" как "PointX.PriceAtSwing" минус "PointC.PriceAtSwing" и "LegCD" как "PointD.PriceAtSwing" минус "PointC.PriceAtSwing".
Проверяем соотношения Фибоначчи, чтобы убедиться, что "LegAB" составляет от 38,2% до 61,8% от "LegXA", "LegBC" — от 127,2% до 141,4% от "LegAB", "LegCD" находится в пределах ""FibonacciTolerance", равного 78,6% от "LegXC", используя функцию "MathAbs", и "PointD.PriceAtSwing" превышает "PointX.PriceAtSwing" — при выполнении всех условий для параметра "PatternFound" устанавливается значение true, а для параметра "PatternDirection" - значение "Медвежий".
Для бычьего «Шифра» проверяем противоположную последовательность: "PointX.IsSwingHigh" - false, "PointA.IsSwingHigh" - true, "PointB.IsSwingHigh" - false, "PointC.IsSwingHigh" - true и "PointD.IsSwingHigh" - false.
Рассчитываем "LegXA" как "PointA.PriceAtSwing" минус "PointX.PriceAtSwing" (положительное для бычьего), "LegAB" как "PointA.PriceAtSwing" минус "PointB.PriceAtSwing", "LegBC" как "PointC.PriceAtSwing" минус "PointB.PriceAtSwing", "LegXC" как "PointC.PriceAtSwing" минус "PointX.PriceAtSwing" и "LegCD" как "PointC.PriceAtSwing" минус "PointD.PriceAtSwing".
Применяются те же проверки Фибоначчи, когда "PointD.PriceAtSwing" меньше, чем "PointX.PriceAtSwing", при этом значение "PatternFound" изменяется на true, а значение "PatternDirection" - на "Bullish" (Бычий), если оно допустимо, что обеспечивает последующую визуализацию и логику торговли. Если паттерн найден, можно приступить к его отображению на графике.
//--- If a pattern is found, visualize it and trade if(PatternFound) { //--- Log the pattern detection in the Experts tab Print(PatternDirection, " Cypher pattern detected at ", TimeToString(PointD.TimeOfSwing, TIME_DATE|TIME_MINUTES)); //--- Create a unique prefix for all chart objects using D’s time string ObjectPrefix = "CY_" + IntegerToString(PointD.TimeOfSwing); //--- Set triangle color: blue for bullish, red for bearish color TriangleColor = (PatternDirection == "Bullish") ? clrBlue : clrRed; //--- **Visualization Steps** //--- 1. Draw two filled triangles to highlight the pattern DrawTriangle(ObjectPrefix + "_Triangle1", PointX.TimeOfSwing, PointX.PriceAtSwing, PointA.TimeOfSwing, PointA.PriceAtSwing, PointB.TimeOfSwing, PointB.PriceAtSwing, TriangleColor, 2, true, true); DrawTriangle(ObjectPrefix + "_Triangle2", PointB.TimeOfSwing, PointB.PriceAtSwing, PointC.TimeOfSwing, PointC.PriceAtSwing, PointD.TimeOfSwing, PointD.PriceAtSwing, TriangleColor, 2, true, true); }
Переходим к обработке визуализации обнаруженного паттерна «Шифр», когда значение "PatternFound" равно true. Используем функцию Print для регистрации обнаружения паттерна на вкладке Experts, выводя "PatternDirection", за которым следует сообщение, указывающее, что был обнаружен паттерн «Шифр», а время отформатировано функцией TimeToString с использованием флагов "PointD.TimeOfSwing" и "TIME_DATE|TIME_MINUTES" для удобства чтения.
Чтобы упорядочить объекты диаграммы, создаем уникальный префикс "ObjectPrefix", объединяя "CY_" со строковым представлением "PointD.TimeOfSwing", полученным с помощью функции IntegerToString, гарантируя, что у объектов каждого паттерна будут разные имена. Затем устанавливаем "TriangleColor", используя троичный оператор, присваивая "clrBlue" для "бычьего" паттерна или "clrRed" для "медвежьего" паттерна на основе "PatternDirection".
Для визуализации вызываем функцию "DrawTriangle" дважды: сначала, чтобы нарисовать треугольник с именем "ObjectPrefix + '_Triangle1'", соединяющий "PointX", "PointA" и "PointB", используя их значения "TimeOfSwing" и "PriceAtSwing", а затем "ObjectPrefix + '_Triangle2'", соединяющий "PointB", "PointC" и "PointD", оба с "TriangleColor", шириной линии 2 и "true" для заливки и рисования за свечами, выделяя структуру паттерна на графике. Получаем промежуточный результат.

На изображении видно, что мы можем корректно отобразить и визуализировать обнаруженный паттерн. Теперь нам нужно продолжить нанесение линий тренда на график, чтобы полностью сделать их видимыми в пределах границ, и добавить к ним метку для облегчения идентификации уровней.
//--- 2. Draw six trend lines connecting the swing points DrawTrendLine(ObjectPrefix + "_Line_XA", PointX.TimeOfSwing, PointX.PriceAtSwing, PointA.TimeOfSwing, PointA.PriceAtSwing, clrBlack, 2, STYLE_SOLID); DrawTrendLine(ObjectPrefix + "_Line_AB", PointA.TimeOfSwing, PointA.PriceAtSwing, PointB.TimeOfSwing, PointB.PriceAtSwing, clrBlack, 2, STYLE_SOLID); DrawTrendLine(ObjectPrefix + "_Line_BC", PointB.TimeOfSwing, PointB.PriceAtSwing, PointC.TimeOfSwing, PointC.PriceAtSwing, clrBlack, 2, STYLE_SOLID); DrawTrendLine(ObjectPrefix + "_Line_CD", PointC.TimeOfSwing, PointC.PriceAtSwing, PointD.TimeOfSwing, PointD.PriceAtSwing, clrBlack, 2, STYLE_SOLID); DrawTrendLine(ObjectPrefix + "_Line_XB", PointX.TimeOfSwing, PointX.PriceAtSwing, PointB.TimeOfSwing, PointB.PriceAtSwing, clrBlack, 2, STYLE_SOLID); DrawTrendLine(ObjectPrefix + "_Line_BD", PointB.TimeOfSwing, PointB.PriceAtSwing, PointD.TimeOfSwing, PointD.PriceAtSwing, clrBlack, 2, STYLE_SOLID); //--- 3. Draw labels for each swing point (X, A, B, C, D) double LabelOffset = 15 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Offset in points for label placement DrawTextLabel(ObjectPrefix + "_Label_X", "X", PointX.TimeOfSwing, PointX.PriceAtSwing + (PointX.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, 11, PointX.IsSwingHigh); DrawTextLabel(ObjectPrefix + "_Label_A", "A", PointA.TimeOfSwing, PointA.PriceAtSwing + (PointA.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, 11, PointA.IsSwingHigh); DrawTextLabel(ObjectPrefix + "_Label_B", "B", PointB.TimeOfSwing, PointB.PriceAtSwing + (PointB.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, 11, PointB.IsSwingHigh); DrawTextLabel(ObjectPrefix + "_Label_C", "C", PointC.TimeOfSwing, PointC.PriceAtSwing + (PointC.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, 11, PointC.IsSwingHigh); DrawTextLabel(ObjectPrefix + "_Label_D", "D", PointD.TimeOfSwing, PointD.PriceAtSwing + (PointD.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, 11, PointD.IsSwingHigh); //--- 4. Draw a central label to identify the pattern datetime CenterTime = (PointX.TimeOfSwing + PointB.TimeOfSwing) / 2; //--- Middle time between X and B double CenterPrice = PointD.PriceAtSwing; //--- Place it at D’s price level if(ObjectCreate(0, ObjectPrefix + "_Label_Center", OBJ_TEXT, 0, CenterTime, CenterPrice)) { ObjectSetString(0, ObjectPrefix + "_Label_Center", OBJPROP_TEXT, "Cypher"); //--- Label as "Cypher" ObjectSetInteger(0, ObjectPrefix + "_Label_Center", OBJPROP_COLOR, clrBlack); ObjectSetInteger(0, ObjectPrefix + "_Label_Center", OBJPROP_FONTSIZE, 11); ObjectSetString(0, ObjectPrefix + "_Label_Center", OBJPROP_FONT, "Arial Bold"); ObjectSetInteger(0, ObjectPrefix + "_Label_Center", OBJPROP_ALIGN, ALIGN_CENTER); }
Продолжаем процесс визуализации, чтобы еще раз проиллюстрировать паттерн «Шифр» на графике. Вызываем функцию "DrawTrendLine" шесть раз, чтобы нарисовать сплошные черные линии, соединяющие точки поворота, каждая из которых имеет "ObjectPrefix" и уникальный суффикс (например, "_Line_XA"). Эти линии связывают "PointX" с "PointA", "PointA" с "PointB", "PointB" с "PointC", "PointC" с "PointD", "PointX" с "PointB" и "PointB" с "PointD", используя соответствующие значения "TimeOfSwing" и "PriceAtSwing" с шириной линии 2 и STYLE_SOLID для четкого определения структуры паттерна.
Затем добавляем текстовые метки для каждой точки поворота, вычисляя "LabelOffset" как 15-кратный размер панкта символа, полученный с помощью функции SymbolInfoDouble с помощью SYMBOL_POINT, чтобы расположить метки соответствующим образом. Вызываем функцию "DrawTextLabel" пять раз, чтобы поставить метку "PointX", "PointA", "PointB", "PointC" и "PointD" такими именами, как "ObjectPrefix + '_Label_X'" и текстом "X", "A", "B", "C", "D". Для каждой метки используются значения "TimeOfSwing" и "PriceAtSwing" точки, скорректированные с помощью параметра "LabelOffset" (добавляется, если значение "IsSwingHigh" равно true, вычитается, если значение "false"), при этом цвет "clrBlack", размер шрифта 11 и "IsSwingHigh" определяют расположение выше или ниже точки.
Наконец, создаем центральную метку для идентификации паттерна, вычисляя "CenterTime" как среднее значение "PointX.TimeOfSwing" и "PointB.TimeOfSwing" и устанавливаем для "CenterPrice" значение "PointD.PriceAtSwing". Используем функцию ObjectCreate для создания текстового объекта с именем "ObjectPrefix + '_Label_Center'" типа "OBJ_TEXT" в этих координатах. В случае успешного выполнения настраиваем его с помощью "ObjectSetString", чтобы установить "OBJPROP_TEXT" значение "Cypher", а "OBJPROP_FONT" на "Arial Bold", а также ObjectSetInteger, чтобы установить для "OBJPROP_COLOR" значение "clrBlack", "OBJPROP_FONTSIZE" - на 11, "OBJPROP_ALIGN" установим на значение "ALIGN_CENTER", четко отмечая паттерн на графике. После компиляции получаем следующий результат.

На изображении видно, что мы добавили края и метки на паттерн, сделав его более выразительным и наглядным. Что нам нужно сделать дальше, так это определить торговые уровни для данного паттерна.
//--- 5. Draw trade levels (entry, take-profits) as dotted lines datetime LineStartTime = PointD.TimeOfSwing; //--- Start at D’s time datetime LineEndTime = PointD.TimeOfSwing + PeriodSeconds(_Period) * 2; //--- Extend 2 bars to the right double EntryPrice, StopLossPrice, TakeProfitPrice, TakeProfit1Level, TakeProfit2Level, TakeProfit3Level, TradeDistance; if(PatternDirection == "Bullish") { EntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Buy at current ask price StopLossPrice = PointX.PriceAtSwing; //--- Stop-loss at X (below entry for bullish) TakeProfitPrice = PointC.PriceAtSwing; //--- Take-profit at C (target level) TakeProfit3Level = PointC.PriceAtSwing; //--- Highest TP at C TradeDistance = TakeProfit3Level - EntryPrice; //--- Distance to TP3 TakeProfit1Level = EntryPrice + TradeDistance / 3; //--- First TP at 1/3 of the distance TakeProfit2Level = EntryPrice + 2 * TradeDistance / 3; //--- Second TP at 2/3 } else { EntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Sell at current bid price StopLossPrice = PointX.PriceAtSwing; //--- Stop-loss at X (above entry for bearish) TakeProfitPrice = PointC.PriceAtSwing; //--- Take-profit at C TakeProfit3Level = PointC.PriceAtSwing; //--- Lowest TP at C TradeDistance = EntryPrice - TakeProfit3Level; //--- Distance to TP3 TakeProfit1Level = EntryPrice - TradeDistance / 3; //--- First TP at 1/3 TakeProfit2Level = EntryPrice - 2 * TradeDistance / 3; //--- Second TP at 2/3 } DrawDottedLine(ObjectPrefix + "_EntryLine", LineStartTime, EntryPrice, LineEndTime, clrMagenta); //--- Entry line DrawDottedLine(ObjectPrefix + "_TP1Line", LineStartTime, TakeProfit1Level, LineEndTime, clrForestGreen); //--- TP1 line DrawDottedLine(ObjectPrefix + "_TP2Line", LineStartTime, TakeProfit2Level, LineEndTime, clrGreen); //--- TP2 line DrawDottedLine(ObjectPrefix + "_TP3Line", LineStartTime, TakeProfit3Level, LineEndTime, clrDarkGreen); //--- TP3 line //--- 6. Draw labels for trade levels datetime LabelTime = LineEndTime + PeriodSeconds(_Period) / 2; //--- Place labels further right string EntryLabelText = (PatternDirection == "Bullish") ? "BUY (" : "SELL ("; EntryLabelText += DoubleToString(EntryPrice, _Digits) + ")"; //--- Add price to label DrawTextLabel(ObjectPrefix + "_EntryLabel", EntryLabelText, LabelTime, EntryPrice, clrMagenta, 11, true); string TP1LabelText = "TP1 (" + DoubleToString(TakeProfit1Level, _Digits) + ")"; DrawTextLabel(ObjectPrefix + "_TP1Label", TP1LabelText, LabelTime, TakeProfit1Level, clrForestGreen, 11, true); string TP2LabelText = "TP2 (" + DoubleToString(TakeProfit2Level, _Digits) + ")"; DrawTextLabel(ObjectPrefix + "_TP2Label", TP2LabelText, LabelTime, TakeProfit2Level, clrGreen, 11, true); string TP3LabelText = "TP3 (" + DoubleToString(TakeProfit3Level, _Digits) + ")"; DrawTextLabel(ObjectPrefix + "_TP3Label", TP3LabelText, LabelTime, TakeProfit3Level, clrDarkGreen, 11, true);
Здесь продолжаем визуализировать торговые уровни для паттерна «Шифр», рисуя пунктирные линии и метки. Устанавливаем для "LineStartTime" значение "PointD.TimeOfSwing", а для "LineEndTime" - на два бара дальше, используя функцию PeriodSeconds, умноженную на 2, определяя временной диапазон для горизонтальных линий.
Для "бычьего" паттерна (когда "PatternDirection" является "Bullish") устанавливаем "EntryPrice" на текущую цену ask с помощью SymbolInfoDouble с помощью SYMBOL_ASK, "StopLossPrice" на "PointX.PriceAtSwing", "TakeProfitPrice" и "TakeProfit3Level" на "PointC.PriceAtSwing", вычисляем "TradeDistance" как "TakeProfit3Level" минус "EntryPrice" и вычисляем "TakeProfit1Level" и "TakeProfit2Level" как одну треть и две трети от "TradeDistance", добавленного к "EntryPrice".
Для медвежьего паттерна мы используем SYMBOL_BID для "EntryPrice", устанавливаем "StopLossPrice" и "TakeProfit3Level" аналогично, рассчитываем "TradeDistance" как "EntryPrice" минус "TakeProfit3Level" и вычитаем одну треть и две трети значения "TradeDistance" из "EntryPrice" для "TakeProfit1Level" и "TakeProfit2Level".
Затем вызываем функцию "DrawDottedLine" четыре раза, чтобы нарисовать горизонтальные линии: "ObjectPrefix + '_EntryLine'" в "EntryPrice" в "clrMagenta" и "ObjectPrefix + '_TP1Line'", "ObjectPrefix + '_TP2Line'", "ObjectPrefix + '_TP3Line'" в "TakeProfit1Level", "TakeProfit2Level" и "TakeProfit3Level" в "clrForestGreen", "clrGreen" и "clrDarkGreen", соответственно, от "LineStartTime" до "LineEndTime".
Для нанесения меток устанавливаем для "LabelTime" значение "LineEndTime" плюс длительность половины бара, используя "PeriodSeconds". Создаем "EntryLabelText" как "BUY" или ("SELL") на основе "PatternDirection", добавляя "EntryPrice", отформатированный с помощью "DoubleToString", с использованием "_Digits", и вызываем "DrawTextLabel" для "ObjectPrefix + '_EntryLabel'" в "EntryPrice" в "clrMagenta".
Аналогично определяем "TP1LabelText", "TP2LabelText" и "TP3LabelText" с отформатированными ценами "TakeProfit1Level", "TakeProfit2Level" и "TakeProfit3Level", вызывая "DrawTextLabel" для каждого из соответствующих уровней в "clrForestGreen", "clrGreen" и "clrDarkGreen", все с размером шрифта 11 и размещенными над ценой, что повышает ясность уровня сделки. Ниже представлен результат.
Медвежий паттерн:

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

На изображениях видно, что мы корректно нанесли торговые уровни. Что нам нужно сделать сейчас, так это инициировать актуальные торговые позиции, и это все.
//--- **Trading Logic** //--- Check if trading is allowed and no position is already open if(TradingEnabled && !PositionSelect(_Symbol)) { //--- Place a buy or sell order based on pattern type bool TradeSuccessful = (PatternDirection == "Bullish") ? obj_Trade.Buy(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Buy") : obj_Trade.Sell(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Sell"); //--- Log the result of the trade attempt if(TradeSuccessful) Print(PatternDirection, " order opened successfully."); else Print(PatternDirection, " order failed: ", obj_Trade.ResultRetcodeDescription()); } //--- Force the chart to update and show all drawn objects ChartRedraw();
Здесь мы реализуем торговую логику для исполнения сделок по паттерну «Шифр» при выполнении условий. Проверяем, имеет ли "TradingEnabled" значение true и не открыта ли какая-либо существующая позиция по текущему инструменту, используя функцию PositionSelect с помощью _Symbol, гарантируя, что сделки будут размещаться только тогда, когда это разрешено, и не будет конфликтующих позиций. Если оба условия выполнены, используем троичный оператор для размещения сделки на основе "PatternDirection": для "бычьего" паттерна вызываем функцию "obj_Trade.Buy" с параметрами "TradeVolume", _Symbol, "EntryPrice", "StopLossPrice", "TakeProfitPrice" и комментарием "Cypher Buy", в то время как для медвежьего паттерна вызываем "obj_Trade.Sell" с теми же параметрами, но комментарием "Cypher Sell", сохраняя результат в "TradeSuccessful".
Затем регистрируем результат, используя функцию Print, выводя "PatternDirection" и "ордер открыт успешно", если значение "TradeSuccessful" равно true, или "ордер не выполнен" с описанием ошибки из "obj_Trade.ResultRetcodeDescription", если значение равно false. Наконец, вызываем функцию ChartRedraw, чтобы принудительно обновить график в MetaTrader 5, гарантируя, что все нарисованные объекты, такие как треугольники, линии и метки будут сразу видны пользователю.
Наконец, при удалении программы нам просто нужно удалить паттерны с графика.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ //--- Runs when the EA stops (e.g., removed from chart) void OnDeinit(const int reason) { //--- Remove all objects starting with "CY_" (our Cypher pattern objects) ObjectsDeleteAll(0, "CY_"); }
В обработчике событий OnDeinit используем функцию ObjectsDeleteAll, чтобы удалить все объекты графика с именами, начинающимися с префикса "CY_", гарантируя, что все связанные с паттерном «Шифр» визуализации, такие как треугольники, линии тренда и метки, будут удалены с графика, сохраняя чистое рабочее пространство, когда система больше не активна. После компиляции получаем следующий результат.

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

Чтобы решить эту проблему, нами реализован механизм блокировки паттерна, используя глобальные переменные "g_patternFormationBar" и "g_lockedPatternX", чтобы заблокировать паттерн при обнаружении и подтвердить его на следующем баре, гарантируя, что точка поворота X остается неизменной. Это исправление значительно сократило перерисовку, что подтверждается последующими тестами, показавшими более стабильное обнаружение паттернов и меньшее количество недействительных сделок. Вот фрагмент образца кода для блокировки паттерна, чтобы убедиться, что мы подождем, пока паттерн не станет стабильным, прежде чем торговать по нему.
//--- If the pattern has changed, update the lock g_patternFormationBar = CurrentBarIndex; g_lockedPatternX = PointX.TimeOfSwing; Print("Cypher pattern has changed; updating lock on bar ", CurrentBarIndex, ". Waiting for confirmation."); return;
Добавляем логику подтверждения, чтобы всегда ждать, пока паттерн не подтвердится и не станет достаточно стабильным для появления дополнительного бара, чтобы мы не открывали позицию раньше времени для осознания, что это начало формирования паттерна. После добавления паттерна блокировки мы видим, что проблема теперь решена.

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

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

Заключение
В заключение, нами успешно разработан советник на MetaQuotes Language 5, который с высокой точностью распознает Гармонический паттерн «Шифр» (Cypher) и торгует им. Объединив определение точки поворота, проверку на основе Фибоначчи, всестороннюю визуализацию и механизм блокировки паттернов для предотвращения перерисовки, мы создали надежную систему, которая динамично адаптируется к рыночным условиям.
Отказ от ответственности: Содержание настоящей статьи предназначено только для целей обучения. Торговля сопряжена со значительным финансовым риском, а рыночные условия могут быть непредсказуемыми. Хотя описываемая стратегия обеспечивает структурированный подход к гармонической торговле, она не гарантирует прибыльность. Перед внедрением этой программы в реальной среде необходимо провести всестороннее тестирование на истории и выполнить надлежащий контроль рисков.
Применяя эти методы, вы сможете усовершенствовать свои навыки торговли по гармоническим паттернам, усовершенствовать свой технический анализ и усовершенствовать свои алгоритмические торговые стратегии. Желаем удачи в вашем торговом путешествии!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17865
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От начального до среднего уровня: Struct (VI)
Нейросети в трейдинге: Адаптивное восприятие рыночной динамики (STE-FlowNet)
Нейросети в трейдинге: Спайково-семантический подход к пространственно-временной идентификации (Окончание)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Отличная статья. Спасибо за проделанную работу. Не могли бы вы поделиться готовым файлом кода? Прикрепить его в конце статьи?
Добро пожаловать. Вы хоть проверили? Спасибо.
Спасибо, что поделились этой статьей. Полезный код для реализации других гармонических паттернов.
Конечно. Добро пожаловать.