Разработка инструментария для анализа Price Action (Часть 66): Создание сканера паттерна "голова и плечи" с проверкой структуры на MQL5
Содержание
Введение
Визуально распознать паттерн "голова и плечи" достаточно просто; гораздо сложнее сделать такое распознавание стабильным и пригодным для автоматизации. В условиях реального рынка плечи редко бывают симметричными, линия шеи часто бывает наклонена, а рыночный шум размывает границы фигуры. При реализации этого в коде проблема становится еще заметнее: без строгих правил – что считать плечом, насколько выраженной должна быть голова, какой наклон линии шеи допустим и какой минимальный размер допустим относительно волатильности – детектор либо пропускает корректные сетапы, либо дает слишком много ложных сигналов.
Эта статья решает данную проблему, предлагая структурированную модель обнаружения на основе треугольников. Вместо свободной интерпретации ценовых свингов модель выделяет левое плечо, голову и правое плечо в пределах заданных треугольных областей и проверяет их по измеримым критериям. К ним относятся геометрические ограничения, нормирование размера на основе ATR, проверка симметрии, ограничение наклона линии шеи и подтверждение пробоя.
К концу этой статьи у вас будет готовый индикатор на языке MQL5, способный обнаруживать и отображать паттерны "голова и плечи" в реальном времени.
Знакомство с паттерном "голова и плечи"
Паттерн "голова и плечи" – это классическая формация Price Action, сигнализирующая о возможном развороте рынка. Обычно он формируется после продолжительного тренда и отражает постепенный переход контроля – от покупателей к продавцам в медвежьем сценарии или от продавцов к покупателям в бычьем. Хотя паттерн широко известен, его стабильное определение и проверка – особенно в коде – остаются серьезной проблемой.

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

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

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

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

Перевернутая формация следует тем же структурным принципам, но обычно формируется после нисходящего тренда. Вместо пиков паттерн состоит из трех последовательных впадин, где средняя впадина (голова) ниже двух окружающих впадин (плеч).
Эта формация традиционно интерпретируется как бычий сигнал разворота, указывающий на возможный переход от медвежьей фазы рынка к бычьей. Линия шеи строится путем соединения максимумов между впадинами, и подтверждение происходит, когда цена пробивает этот уровень сверху.
Однако в определенных рыночных условиях, особенно в рамках более широкого восходящего тренда, аналогичная структура может возникать в коррекционной фазе. В таких случаях перевернутый паттерн "голова и плечи" может выступать как паттерн продолжения, сигнализируя о возобновлении преобладающего бычьего тренда, а не о полном развороте.
Что это значит для реализации
Хотя в теории паттерн прост, превращение его в надежную систему обнаружения представляет собой нетривиальную задачу. Основная проблема заключается в том, чтобы жестко задать структуру:
- определение критериев корректного плеча;
- проверка явного доминирования головы;
- построение линии шеи последовательно в разных рыночных условиях.
Без строгих структурных правил обнаружение становится субъективным и подверженным ошибкам. Именно это ограничение и обусловливает структурированный подход, представленный в следующем разделе, где паттерн определяется и проверяется с помощью контролируемой модели на языке MQL5, основанной на четких правилах.
Реализация на MQL5
В этом разделе мы пошагово реализуем теорию из предыдущего раздела в виде индикатора на языке MQL5. Сначала рассмотрим общую схему индикатора, описывающую основную логику системы, а затем – этапы реализации, где концепция переносится в код.
Обзор системы
Индикатор определяет максимумы и минимумы свингов, проверяет структуры-кандидаты по геометрическим правилам и правилам на основе ATR, а также визуализирует подтвержденные паттерны с помощью треугольников и линии шеи. Когда происходит пробой, система опционально отображает стрелку направления и размещает цветные текстовые метки для выделения сигнала.
Логику индикатора можно представить в виде последовательности этапов – от обнаружения свингов до генерации сигнала. Ниже приведена блок-схема, иллюстрирующая, как исходные ценовые данные проходят процедуры обнаружения свингов, проверки паттернов, оценки качества и, наконец, генерации сигнала.

Свойства и базовая настройка
Сначала задайте свойства индикатора, чтобы MetaTrader 5 обеспечивал его корректное поведение при выполнении. Установите #property indicator_chart_window, чтобы отображать индикатор на основном графике. Установите indicator_buffers 0 и indicator_plots 0, чтобы отключить традиционные графические построения и буферы. Вместо этого подход использует графические объекты, такие как метки, трендовые линии и стрелки, для визуализации паттернов "голова и плечи". Такая настройка обеспечивает максимальную гибкость и позволяет создать визуально понятное, настраиваемое графическое наложение, которое помогает распознавать паттерны.
#property copyright "Copyright 2026, MetaQuotes Ltd." #property link "https://www.mql5.com/ru/users/lynnchris" #property version "1.00" #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0
Объявление структур данных
Далее вводятся две основные структуры данных: SwingPoint и Pattern. Структура SwingPoint фиксирует значительные развороты рынка, сохраняя информацию о максимумах и минимумах свинга вместе с их индексом бара, ценой и временной меткой. Эти поворотные точки критически важны, поскольку формирование паттерна зависит от их точного выявления.
//+------------------------------------------------------------------+ //| Structures Definition | //+------------------------------------------------------------------+ struct SwingPoint { int barIndex; double price; bool isHigh; datetime time; }; struct Pattern { int id; bool isBearish; int lsIndex; int headIndex; int rsIndex; int neck1Index; int neck2Index; double neckSlope; double neckIntercept; double headPrice; double neckPriceAtHead; double height; double score; bool signalGenerated; datetime signalTime; int signalBar; datetime detectionTime; }; //+------------------------------------------------------------------+
Структура Pattern содержит все атрибуты обнаруженного паттерна "голова и плечи" или его перевернутой формы. В ней фиксируются тип паттерна (медвежий или бычий), индексы формирующих паттерн точек свинга, наклон и параметр пересечения линии шеи, высота, оценка качества и флаги для управления сигналами. Такая организация данных облегчает систематический анализ, точную визуализацию и эффективную генерацию сигналов, обеспечивая надежную работу с геометрическими и временными характеристиками каждого паттерна.
Пользовательские входные параметры
Гибкость индикатора достигается за счет множества входных параметров, которые можно настраивать под конкретные торговые стратегии или рыночные условия. Например, SwingStrength определяет, сколько баров анализируется по обе стороны при обнаружении свингов, обеспечивая баланс между чувствительностью и устойчивостью к шуму. ShoulderTolerance задает допустимое отклонение в симметрии плеч, влияя на строгость паттерна. Минимальная высота паттерна в единицах ATR задается параметром MinPatternSizeATR, что обеспечивает значимость паттернов относительно текущей волатильности.
//+------------------------------------------------------------------+ //| User Inputs | //+------------------------------------------------------------------+ input int SwingStrength = 3; input double ShoulderTolerance = 0.02; input double MinPatternSizeATR = 1.5; input double MaxNecklineSlopeDeg = 30.0; input bool AllowDescendingNeck = true; input int MinTimeSymmetry = 50; input bool ShowNeckline = true; input bool ShowBreakoutArrow = true; input bool AlertOnNewPattern = true; input int MinSwingDistance = 10; input int MinPatternDistance = 50; input double MinScoreThreshold = 60.0; input color PatternFillColor = clrYellow; input int PatternOpacity = 60; //+------------------------------------------------------------------+
Дополнительные параметры, такие как MaxNecklineSlopeDeg, ограничивают допустимый угол линии шеи, отбраковывая нереалистичные формации. Булевы переключатели, такие как AllowDescendingNeck, ShowNeckline, ShowBreakoutArrow и AlertOnNewPattern, позволяют управлять визуализацией и алертами, настраивая их под разные торговые условия. Эти настройки делают процесс обнаружения адаптивным, точным и удобным для пользователя.
Глобальные переменные и хэндлы
Управление данными и ресурсами индикатора включает объявление глобальных переменных. Массив g_swings хранит все обнаруженные точки свинга, а g_patterns хранит паттерны, выявленные в ходе анализа. Хэндл g_atrHandle используется для получения данных ATR, которые измеряют волатильность рынка и являются ключевым элементом для нормирования размеров паттернов и отбраковки незначительных формаций.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ SwingPoint g_swings[]; Pattern g_patterns[]; int g_atrHandle = INVALID_HANDLE; string g_prefix = "HS_"; int g_nextPatternId = 1; //+------------------------------------------------------------------+
Строковый префикс g_prefix помогает упорядочить все графические объекты, созданные индикатором, и упрощает их управление и удаление. Переменная g_nextPatternId обеспечивает присвоение каждому обнаруженному паттерну уникального идентификатора, что полезно для визуализации и управления сигналами. Вместе эти переменные обеспечивают надежный анализ и визуализацию в реальном времени.
Вспомогательная функция GetATR
Эта специальная функция получает текущее значение ATR с помощью CopyBuffer() и хэндла ATR. Это значение критически важно, поскольку ATR дает нормированную меру волатильности рынка и позволяет подстраивать обнаружение паттернов под текущую волатильность. Если извлечение данных не удается, функция возвращает ноль и тем самым предотвращает ошибочные вычисления. Использование ATR делает обнаружение паттернов адаптивным и позволяет отсеивать формации, которые слишком малы или незначимы на волатильных или спокойных рынках.
//+------------------------------------------------------------------+ //| Get ATR Value | //+------------------------------------------------------------------+ double GetATR() { double atr[1]; return (CopyBuffer(g_atrHandle, 0, 0, 1, atr) == 1) ? atr[0] : 0; } //+------------------------------------------------------------------+
Вспомогательная функция FindLowestSwing
Для выявления самой низкой точки свинга в заданном диапазоне индикатор использует функцию FindLowestSwing(). Эта функция перебирает сохраненные точки свинга, игнорируя максимумы свинга, и сравнивает их цены, чтобы найти минимум. Точное определение этих минимумов критически важно для построения линии шеи и проверки структуры паттерна, чтобы визуализация и сигналы опирались на надежные ключевые точки.
//+------------------------------------------------------------------+ //| Find Lowest Swing | //+------------------------------------------------------------------+ int FindLowestSwing(int startBar, int endBar) { int bestIdx = -1; double bestPrice = DBL_MAX; for(int i = 0; i < ArraySize(g_swings); i++) { if(g_swings[i].isHigh) continue; if(g_swings[i].barIndex >= startBar && g_swings[i].barIndex <= endBar) { if(g_swings[i].price < bestPrice) { bestPrice = g_swings[i].price; bestIdx = i; } } } return bestIdx; } //+------------------------------------------------------------------+
Вспомогательная функция FindHighestSwing
Аналогичным образом функция FindHighestSwing() ищет наибольший максимум свинга в заданном диапазоне. Функция пропускает минимумы свинга и сравнивает значения максимумов, чтобы найти среди них наибольшее. Это особенно важно для определения головы в паттерне "голова и плечи", для которой характерен выраженный максимум. Точное выявление этих пиков обеспечивает надежность визуализации и анализа паттерна.
//+------------------------------------------------------------------+ //| Find highest swing point within a range | //+------------------------------------------------------------------+ int FindHighestSwing(int startBar, int endBar) { int bestIdx = -1; double bestPrice = -DBL_MAX; for(int i = 0; i < ArraySize(g_swings); i++) { if(!g_swings[i].isHigh) continue; if(g_swings[i].barIndex >= startBar && g_swings[i].barIndex <= endBar) { if(g_swings[i].price > bestPrice) { bestPrice = g_swings[i].price; bestIdx = i; } } } return bestIdx; } //+------------------------------------------------------------------+
Вспомогательная функция GetNecklinePrice
Функция GetNecklinePrice() вычисляет ожидаемую цену линии шеи для заданного индекса бара. Расчет основан на наклоне и смещении, полученных из точек свинга, что позволяет точно построить линию шеи на графике. Правильный расчет этой линии критически важен для наглядности и выявления пробоев, поскольку пробой этого уровня часто сигнализирует о возможной смене тренда.
//+------------------------------------------------------------------+ //| Get Neckline Price at Bar | //+------------------------------------------------------------------+ double GetNecklinePrice(const Pattern &pat, int barIndex) { return pat.neckSlope * barIndex + pat.neckIntercept; } //+------------------------------------------------------------------+
Выявление свингов в ценовых данных
Процесс начинается с DetectSwings(): функция анализирует максимумы и минимумы в ценовых данных и, сравнивая каждый бар с соседними, выявляет значимые развороты – локальные максимумы и минимумы. С помощью параметра SwingStrength функция отсеивает незначительные колебания и сосредотачивается только на значимых разворотах.
//+------------------------------------------------------------------+ //| Detect Swings | //+------------------------------------------------------------------+ void DetectSwings(const datetime &time[], const double &high[], const double &low[], int totalBars) { ArrayResize(g_swings, 0); int start = SwingStrength; int end = totalBars - SwingStrength - 1; if(end <= start) return; for(int i = start; i <= end; i++) { bool isHighSwing = true; bool isLowSwing = true; for(int j = i - SwingStrength; j <= i + SwingStrength; j++) { if(high[j] > high[i]) isHighSwing = false; if(low[j] < low[i]) isLowSwing = false; if(!isHighSwing && !isLowSwing) break; } if(isHighSwing) { SwingPoint sp = {i, high[i], true, time[i]}; ArrayResize(g_swings, ArraySize(g_swings) + 1); g_swings[ArraySize(g_swings) - 1] = sp; } else if(isLowSwing) { SwingPoint sp = {i, low[i], false, time[i]}; ArrayResize(g_swings, ArraySize(g_swings) + 1); g_swings[ArraySize(g_swings) - 1] = sp; } } } //+------------------------------------------------------------------+
Выявленные точки свинга сохраняются в массиве g_swings, который служит основой для распознавания паттернов. Этот шаг гарантирует, что последующий анализ будет опираться на надежные рыночные точки разворота.
Расчет оценки качества паттерна
После выявления потенциальных паттернов их качество оценивается с помощью ComputePatternScore(). Эта функция оценивает геометрическую симметрию, высоту паттерна относительно ATR, наклон линии шеи и временную симметрию, объединяя эти факторы в итоговую оценку от 0 до 100. Только паттерны с оценкой качества выше заданного порога (MinScoreThreshold) считаются корректными.
//+------------------------------------------------------------------+ //| Compute Pattern Score | //+------------------------------------------------------------------+ double ComputePatternScore(const Pattern &p, const double &high[], const double &low[], double atr) { double score = 0.0; double leftPrice = (p.isBearish ? high[p.lsIndex] : low[p.lsIndex]); double rightPrice = (p.isBearish ? high[p.rsIndex] : low[p.rsIndex]); double headPrice = p.headPrice; double priceDiff = MathAbs(leftPrice - rightPrice) / headPrice; double priceSym = MathMax(0.0, 1.0 - priceDiff / ShoulderTolerance); score += priceSym * 30.0; if(MinTimeSymmetry > 0) { int leftDist = p.headIndex - p.lsIndex; int rightDist = p.rsIndex - p.headIndex; double timeRatio = (leftDist > 0 && rightDist > 0) ? (double)MathMin(leftDist, rightDist) / MathMax(leftDist, rightDist) : 0; score += timeRatio * (double)MinTimeSymmetry / 100.0 * 20.0; } else score += 20.0; double slopeDeg = MathArctan(p.neckSlope) * 180.0 / M_PI; if(MathAbs(slopeDeg) <= MaxNecklineSlopeDeg) score += 20.0 * (1.0 - MathAbs(slopeDeg) / MaxNecklineSlopeDeg); double sizeRatio = p.height / atr; double sizeScore = MathMin(30.0, (sizeRatio / MinPatternSizeATR) * 30.0); score += sizeScore; return MathMin(100.0, score); } //+------------------------------------------------------------------+
Эта система оценки качества помогает отдавать приоритет наиболее надежным формациям, снижает число ложных сигналов и повышает уверенность в обнаруженных паттернах.
Выявление паттернов – основная логика
В основе системы лежит DetectPatterns(), которая анализирует точки свинга и выявляет формации, соответствующие паттернам "голова и плечи" и их перевернутой форме. Функция ищет определенные последовательности – три пика или три впадины, соответствующие ожидаемым геометрическим соотношениям, – и проверяет их по таким критериям, как высота паттерна, симметрия плеч и наклон линии шеи. Паттерны, удовлетворяющие всем условиям, получают оценку качества через ComputePatternScore() и сохраняются, если она превышает порог.
//+------------------------------------------------------------------+ //| Detect Patterns | //+------------------------------------------------------------------+ void DetectPatterns(const datetime &time[], const double &high[], const double &low[], const double &close[], int totalBars) { if(ArraySize(g_swings) < 5) return; double atr = GetATR(); if(atr <= 0) return; Pattern candidates[]; ArrayResize(candidates, 0); for(int i = 0; i < ArraySize(g_swings) - 4; i++) { //--- Bearish pattern: High, Low, High, Low, High if(g_swings[i].isHigh && !g_swings[i+1].isHigh && g_swings[i+2].isHigh && !g_swings[i+3].isHigh && g_swings[i+4].isHigh) { int ls = i; int n1 = i + 1; int head = i + 2; int n2 = i + 3; int rs = i + 4; if(g_swings[rs].barIndex - g_swings[ls].barIndex < MinSwingDistance) continue; if(g_swings[head].price <= g_swings[ls].price) continue; if(g_swings[rs].price >= g_swings[head].price) continue; double shoulderDiff = MathAbs(g_swings[ls].price - g_swings[rs].price) / g_swings[head].price; if(shoulderDiff > ShoulderTolerance) continue; double x1 = (double)g_swings[n1].barIndex; double y1 = g_swings[n1].price; double x2 = (double)g_swings[n2].barIndex; double y2 = g_swings[n2].price; double slope = (y2 - y1) / (x2 - x1); double intercept = y1 - slope * x1; double neckAtHead = slope * g_swings[head].barIndex + intercept; double height = g_swings[head].price - neckAtHead; if(height < MinPatternSizeATR * atr) continue; if(!AllowDescendingNeck && slope < 0) continue; Pattern pat; pat.id = g_nextPatternId++; pat.isBearish = true; pat.lsIndex = g_swings[ls].barIndex; pat.headIndex = g_swings[head].barIndex; pat.rsIndex = g_swings[rs].barIndex; pat.neck1Index = g_swings[n1].barIndex; pat.neck2Index = g_swings[n2].barIndex; pat.neckSlope = slope; pat.neckIntercept = intercept; pat.headPrice = g_swings[head].price; pat.neckPriceAtHead = neckAtHead; pat.height = height; pat.signalGenerated = false; pat.detectionTime = time[0]; pat.score = ComputePatternScore(pat, high, low, atr); if(pat.score >= MinScoreThreshold) { ArrayResize(candidates, ArraySize(candidates) + 1); candidates[ArraySize(candidates) - 1] = pat; } } //--- Bullish inverse pattern else if(!g_swings[i].isHigh && g_swings[i+1].isHigh && !g_swings[i+2].isHigh && g_swings[i+3].isHigh && !g_swings[i+4].isHigh) { int ls = i; int n1 = i + 1; int head = i + 2; int n2 = i + 3; int rs = i + 4; if(g_swings[rs].barIndex - g_swings[ls].barIndex < MinSwingDistance) continue; if(g_swings[head].price >= g_swings[ls].price) continue; if(g_swings[rs].price <= g_swings[head].price) continue; double shoulderDiff = MathAbs(g_swings[ls].price - g_swings[rs].price) / MathAbs(g_swings[head].price); if(shoulderDiff > ShoulderTolerance) continue; double x1 = (double)g_swings[n1].barIndex; double y1 = g_swings[n1].price; double x2 = (double)g_swings[n2].barIndex; double y2 = g_swings[n2].price; double slope = (y2 - y1) / (x2 - x1); double intercept = y1 - slope * x1; double neckAtHead = slope * g_swings[head].barIndex + intercept; double height = neckAtHead - g_swings[head].price; if(height < MinPatternSizeATR * atr) continue; if(!AllowDescendingNeck && slope < 0) continue; Pattern pat; pat.id = g_nextPatternId++; pat.isBearish = false; pat.lsIndex = g_swings[ls].barIndex; pat.headIndex = g_swings[head].barIndex; pat.rsIndex = g_swings[rs].barIndex; pat.neck1Index = g_swings[n1].barIndex; pat.neck2Index = g_swings[n2].barIndex; pat.neckSlope = slope; pat.neckIntercept = intercept; pat.headPrice = g_swings[head].price; pat.neckPriceAtHead = neckAtHead; pat.height = height; pat.signalGenerated = false; pat.detectionTime = time[0]; pat.score = ComputePatternScore(pat, high, low, atr); if(pat.score >= MinScoreThreshold) { ArrayResize(candidates, ArraySize(candidates) + 1); candidates[ArraySize(candidates) - 1] = pat; } } } //--- Deduplication for(int i = 0; i < ArraySize(candidates) - 1; i++) { for(int j = i + 1; j < ArraySize(candidates); j++) { if(MathAbs(candidates[i].headIndex - candidates[j].headIndex) < MinPatternDistance) { if(candidates[i].score < candidates[j].score) candidates[i].score = -1; else candidates[j].score = -1; } } } int newSize = 0; for(int i = 0; i < ArraySize(candidates); i++) { if(candidates[i].score >= MinScoreThreshold) { if(i != newSize) candidates[newSize] = candidates[i]; newSize++; } } ArrayResize(candidates, newSize); int existingCount = ArraySize(g_patterns); for(int i = 0; i < ArraySize(candidates); i++) { bool exists = false; for(int j = 0; j < existingCount; j++) { if(g_patterns[j].lsIndex == candidates[i].lsIndex && g_patterns[j].headIndex == candidates[i].headIndex && g_patterns[j].rsIndex == candidates[i].rsIndex) { exists = true; break; } } if(!exists) { ArrayResize(g_patterns, existingCount + 1); g_patterns[existingCount] = candidates[i]; existingCount++; DrawPatternTriangles(candidates[i], time, high, low); DrawNeckline(candidates[i], time); if(AlertOnNewPattern) Alert("New ", (candidates[i].isBearish ? "Bearish" : "Bullish"), " pattern on ", _Symbol); } } } //+------------------------------------------------------------------+
Этот процесс преобразует необработанные данные точек свинга в значимые паттерны, пригодные для практического применения, и тем самым формирует основу для визуализации и торговых сигналов.
Отрисовка треугольников паттерна
После подтверждения паттерна индикатор отображает его, рисуя с помощью OBJ_TRIANGLE треугольники, соединяющие ключевые точки свинга – левое плечо, голову и правое плечо. Функция определяет крайние точки свинга и отображает паттерн на графике. Заполненные полупрозрачными цветами, эти треугольники сразу делают структуру паттерна наглядной и ускоряют распознавание и анализ.
//+------------------------------------------------------------------+ //| Draw Pattern Triangles | //+------------------------------------------------------------------+ void DrawPatternTriangles(const Pattern &pat, const datetime &time[], const double &high[], const double &low[]) { string base = g_prefix + "TRI_" + IntegerToString(pat.id); uint argbColor = ColorToARGB(PatternFillColor, (uchar)PatternOpacity); ObjectCreate(0, base+"_LS", OBJ_TRIANGLE, 0, time[pat.lsIndex], pat.isBearish ? high[pat.lsIndex] : low[pat.lsIndex], time[pat.headIndex], pat.isBearish ? high[pat.headIndex] : low[pat.headIndex], time[pat.neck1Index], GetNecklinePrice(pat, pat.neck1Index)); ObjectSetInteger(0, base+"_LS", OBJPROP_FILL, true); ObjectSetInteger(0, base+"_LS", OBJPROP_BGCOLOR, argbColor); } //+------------------------------------------------------------------+
Отрисовка линии шеи
Функция DrawNeckline() выделяет уровень поддержки или сопротивления, определяющий паттерн. Линия шеи рассчитывается путем соединения соответствующих точек свинга, а затем продлевается по всему графику; для наглядности используется пунктирная линия. Рядом с линией шеи также можно добавить текстовые метки, чтобы обозначить тип паттерна. Точно построенная линия шеи критически важна, поскольку ее пробой часто подтверждает паттерн и сигнализирует о возможном развороте тренда.
//+------------------------------------------------------------------+ //| Draw Neckline | //+------------------------------------------------------------------+ void DrawNeckline(const Pattern &pat, const datetime &time[]) { if(!ShowNeckline) return; string name = g_prefix + "NECK_" + IntegerToString(pat.id); double price1 = GetNecklinePrice(pat, pat.neck1Index); double price2 = GetNecklinePrice(pat, pat.neck2Index); ObjectCreate(0, name, OBJ_TREND, 0, time[pat.neck1Index], price1, time[pat.neck2Index], price2); ObjectSetInteger(0, name, OBJPROP_COLOR, clrMagenta); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH); } //+------------------------------------------------------------------+
Отрисовка стрелки пробоя
Когда цена пробивает линию шеи паттерна, для визуального обозначения этого события вызывается DrawBreakoutArrow(). Функция рисует стрелку вниз для медвежьих пробоев и вверх для бычьих, используя цветовое кодирование для мгновенного распознавания.
//+------------------------------------------------------------------+ //| Draw Breakout Arrow | //+------------------------------------------------------------------+ void DrawBreakoutArrow(const Pattern &pat, const datetime &time[], int bar, double price) { if(!ShowBreakoutArrow) return; string name = g_prefix + "SIG_" + IntegerToString(pat.id); if(pat.isBearish) { ObjectCreate(0, name, OBJ_ARROW_DOWN, 0, time[bar], price); ObjectSetInteger(0, name, OBJPROP_COLOR, clrRed); } else { ObjectCreate(0, name, OBJ_ARROW_UP, 0, time[bar], price); ObjectSetInteger(0, name, OBJPROP_COLOR, clrLime); } } //+------------------------------------------------------------------+
Расположенные в точке пробоя, эти стрелки дают четкий визуальный сигнал того, что паттерн подтвержден, и указывают трейдерам на потенциальные возможности для входа.
Проверка пробоев
Индикатор непрерывно отслеживает текущую рыночную цену относительно уровня линии шеи. CheckBreakouts() сравнивает последнюю цену закрытия с линией шеи, и если происходит выраженное движение – например, закрытие ниже линии шеи в медвежьем паттерне, – индикатор помечает паттерн как подтвержденный, рисует соответствующую стрелку и генерирует алерт.
//+------------------------------------------------------------------+ //| Check Breakouts | //+------------------------------------------------------------------+ void CheckBreakouts(const datetime &time[], const double &close[], int currentBar) { for(int i = 0; i < ArraySize(g_patterns); i++) { if(g_patterns[i].signalGenerated) continue; double neck = GetNecklinePrice(g_patterns[i], currentBar); bool breakout = g_patterns[i].isBearish ? (close[currentBar] < neck) : (close[currentBar] > neck); if(breakout) { g_patterns[i].signalGenerated = true; g_patterns[i].signalTime = time[currentBar]; g_patterns[i].signalBar = currentBar; DrawBreakoutArrow(g_patterns[i], time, currentBar, close[currentBar]); } } } //+------------------------------------------------------------------+
Такая оценка в реальном времени обеспечивает своевременную фиксацию завершения паттерна и позволяет трейдерам оперативно реагировать на эти сигналы.
Основной расчет – OnCalculate()
Ключевая функция OnCalculate() координирует весь процесс обнаружения. Она выполняется при каждом поступлении новых рыночных данных и сначала проверяет, был ли уже обработан текущий бар, чтобы избежать лишних вычислений. Затем последовательно вызываются функции для выявления точек свинга (DetectSwings()), распознавания корректных паттернов (DetectPatterns()) и проверки пробоев (CheckBreakouts()). Этот структурированный процесс гарантирует, что все этапы анализа будут выполняться эффективно и точно, обеспечивая постоянно обновляемые визуальные подсказки и алерты.
//+------------------------------------------------------------------+ //| OnCalculate | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { static datetime lastTime = 0; if(time[0] == lastTime) return rates_total; lastTime = time[0]; DetectSwings(time, high, low, rates_total); DetectPatterns(time, high, low, close, rates_total); CheckBreakouts(time, close, 0); return rates_total; } //+------------------------------------------------------------------+
Тем самым индикатор автоматизирует весь процесс распознавания паттернов и поддерживает своевременное принятие решений.
Инициализация и очистка
//+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { g_atrHandle = iATR(_Symbol, PERIOD_CURRENT, 14); if(g_atrHandle == INVALID_HANDLE) return INIT_FAILED; ObjectsDeleteAll(0, g_prefix); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+
Во время инициализации OnInit() создает хэндл индикатора ATR, который необходим для нормирования размеров паттернов и отбраковки незначительных формаций. Кроме того, функция очищает ранее созданные графические объекты, чтобы начать с чистого состояния. При удалении индикатора OnDeinit() освобождает хэндл ATR и удаляет все объекты, созданные во время работы, так что на графике не остается остаточных данных. Такое управление ресурсами поддерживает чистую и эффективную рабочую среду и предотвращает возможные проблемы с производительностью.
//+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(g_atrHandle != INVALID_HANDLE) IndicatorRelease(g_atrHandle); ObjectsDeleteAll(0, g_prefix); Comment(""); } //+------------------------------------------------------------------+
Результаты
Индикатор был протестирован в реальных рыночных условиях, чтобы оценить его способность обнаруживать и отображать паттерны "голова и плечи" в реальном времени. На диаграмме ниже показаны результаты для Step Index (M30): на графике выявлено несколько корректных паттернов с четкой разметкой структуры.

Каждый обнаруженный паттерн отображается с помощью треугольных областей и соответствующей линии шеи, что делает структуру сразу заметной. Система также присваивает оценку качества каждому кандидату и отбраковывает слабые формации, снижая шум и повышая надежность. Подтверждение пробоя отслеживается непрерывно, и когда цена закрывается за линией шеи, индикатор отмечает это событие направленной стрелкой и может сгенерировать алерт. Последний выявленный пример перевернутого паттерна "голова и плечи" выявлен корректно, хотя визуально его можно было бы легко пропустить, что демонстрирует преимущество обнаружения по правилам перед ручным анализом.
В целом индикатор стабильно выявляет паттерны "голова и плечи" и их перевернутые формы, обеспечивая наглядную визуализацию и опираясь на строгие структурные правила. Сочетание автоматического обнаружения, оценки качества и подтверждения обеспечивает практическую основу, которую можно тестировать, настраивать и адаптировать для разных символов и таймфреймов.
Заключение
В этой статье представлена полноценная, основанная на правилах система обнаружения паттерна "голова и плечи" для MetaTrader 5. Индикатор переводит визуальное распознавание паттернов в структурированный и воспроизводимый процесс, сочетая выявление свингов, геометрическую проверку, нормирование размера на основе ATR, проверку симметрии и подтверждение пробоя линии шеи.
Система обнаруживает как стандартные, так и перевернутые паттерны, строит треугольные структуры и линии шеи, присваивает оценки качества и выделяет подтвержденные пробои визуальными маркерами и алертами. Этот подход снижает субъективность и повышает последовательность при анализе паттернов.
В результате получается практичный индикатор, предназначенный для визуализации и генерации сигналов, а не для автоматической торговли. С помощью исходного кода систему можно скомпилировать, протестировать и настроить под разные рыночные условия и стили торговли. Структурированная последовательность этапов – от обнаружения свингов до подтверждения – также создает прочную основу для дальнейших улучшений или интеграции в более широкие торговые системы.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22194
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Торговые инструменты на MQL5 (Часть 29): Пошаговая анимация кривой-бабочки на Canvas
Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть III): Живой граф признаков
Рыночные секреты Ларри Уильямса (Часть 12): Торговля разворотами Smash Day на основе контекста
Рыночные секреты Ларри Уильямса (Часть 11): Индикатор для обнаружения разворотов Smash Day
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования