Знакомство с языком MQL5 (Часть 14): Руководство для начинающих по созданию пользовательских индикаторов (III)
Введение
И еще раз добро пожаловать в нашу серию статей по языку MQL5! В предыдущих статьях этой серии мы изучили, как создавать пользовательские индикаторы на языке MQL5, используя буферы и графические построения (plots). Буферы позволяют нам хранить значения индикатора, в то время как графические построения помогают визуализировать их на чарте. Эти методы эффективны для многих индикаторов, но имеют ограничения при создании сложных визуальных представлений.
В этой статье мы применим новый подход, создавая индикаторы с использованием графических объектов Meta Trader 5. Графические объекты обеспечивают дополнительную гибкость: они позволяют нам создавать метки, фигуры и трендовые линии прямо на графике без использования буферов индикаторов. Эта техника хорошо подходит для разработки индикаторов, которые требуют уникальных графических компонентов, отображения паттернов и выявления важных ценовых уровней.
Для реализации этой техники мы создадим индикатор, который будет выявлять гармонические паттерны. Логика, которую мы будем использовать, может быть изменена для идентификации и отображения различных гармонических паттернов, хотя мы не будем сосредотачиваться на каком-либо конкретном (будь то Gartley, Bat или Butterfly). Вместо создания полнофункционального инструмента для выявления гармонических паттернов, главной целью является изучение того, как использовать графические объекты в языке MQL5 при разработке индикаторов. В Части 9 этой серии мы рассмотрели создание и работу с объектами, такими как трендовая линия, прямоугольник и метки в языке MQL5, что стало первым шагом к использованию графических объектов. Основываясь на этих знаниях, данная статья применяет их в разработке индикаторов. К концу статьи вы будете уверенно понимать, как разрабатывать уникальные визуальные индикаторы, работая с элементами графиков динамически.
В этой статье вы узнаете:
- Как создать пользовательский индикатор, используя графические объекты MetaTrader 5 вместо буферов и графических построений.
- Разбор структуры гармонических паттернов и того, как они выявляются в ценовом движении.
- Как определить ключевые точки свинга на рынке для формирования потенциальных гармонических паттернов.
- Использование уровней коррекции Фибоначчи для проверки сформированных паттернов.
- Как программно отрисовывать геометрические фигуры (такие как треугольники и линии), чтобы визуализировать паттерны на графике.
- Как отфильтровывать недействительные паттерны для повышения точности торговых сигналов.
1. Индикатор Harmonic Pattern
1.1. Разбор индикатора Harmonic Pattern
Ценовые структуры, известные как гармонические паттерны, используют определенные коэффициенты Фибоначчи для определения возможных зон разворота рынка. Паттерны Gartley, Bat и Butterfly, среди всего прочего, зависят от колебаний цен, создающих геометрические формы, которые сигнализируют о высоковероятных торговых возможностях.
Гармонические паттерны требуют определения критических ценовых точек и использования соотношений Фибоначчи для подтверждения их структуры, в то время как стандартные индикаторы создают сигналы с помощью буферов и графических построений. В результате их сложнее идентифицировать и отображать, чем более традиционные индикаторы, такие как RSI или скользящие средние.
Логика, которую мы используем, может быть адаптирована под выявление и отображение различных гармонических паттернов, хоть мы и не будем сосредотачиваться на каком-либо конкретном паттерне (например, Gartley, Bat или Butterfly). Вместо создания полнофункционального инструмента для выявления гармонических паттернов, главной целью является изучение того, как использовать графические объекты в языке MQL5 при разработке индикаторов.
Для этого мы отметим важные точки свинга (максимумы и минимумы) на графике, используем текстовые объекты (XABCD) для указания важных уровней, проиллюстрируем структуру паттерна различными объектами и применим уровни Фибоначчи для повышения точности и проверки паттерна.
1.2. Настройка проекта
Как я часто говорю, прежде чем вы сможете разработать индикатор, сначала вы должны представить, как он будет выглядеть. Знание того, как именно следует настраивать индикатор, облегчает процесс разработки и гарантирует, что конечный продукт будет соответствовать нашим требованиям.
В этом проекте мы будем использовать графические объекты MetaTrader 5 для построения как бычьих, так и медвежьих гармонических паттернов. Индикатор нарисует несколько графических элементов для формирования структуры паттерна, определит важные точки свинга, отметит их текстовыми объектами (XABCD) и добавит уровни Фибоначчи для улучшения представления паттерна.
В дополнение к созданию индикатора, который близок к гармоническим паттернам, этот метод предоставит нам практический опыт работы с графическими объектами языка MQL5, что облегчит создание будущих пользовательских индикаторов. В дополнение к обсуждению того, как создавать эти паттерны с помощью графических объектов, мы также рассмотрим ошибки, которые потенциально могут возникнуть. Могут возникнуть такие проблемы, как неверно размещенные объекты, неправильно расположенные уровни Фибоначчи и неточное выявление точек свинга. Чтобы гарантировать точность представления паттерна и бесперебойную работу индикатора, мы рассмотрим эти проблемы и предложим решения.
1.2.1. Бычий паттерн
Мы будем использовать методический подход для определения важных точек свинга, которые составляют паттерн, чтобы разработать индикатор Bullish Harmonic Pattern. Процесс включает в себя определение конкретных ценовых точек и проверку их соответствия правилам гармонических паттернов.
Определение минимума свинга (X) в качестве начальной точки паттерна будет нашим первым шагом. Далее мы определим максимум свинга (A) и минимум свинга (B). Точке B необходимо откатиться на расстояние от 61,8% до 78,6% от отрезка XA, чтобы паттерн оставался действительным. Затем мы будем искать максимум cвинга (C) и подтверждать минимум cвинга (D). Полная структура бычьего паттерна формируется, когда последняя точка D ниже X.
Когда эти условия будут выполнены, мы будем использовать графические объекты, такие как трендовые линии (соединяющие точки свинга), метки для точек XABCD и уровни коррекции Фибоначчи (для подтверждения структуры), чтобы графически изобразить паттерн на графике. Это обеспечит методичный и прозрачный подход к выявлению возможных бычьих зон разворота. Чтобы вам было проще распознавать важные торговые зоны внутри паттерна, мы также будем использовать графические объекты для указания возможных точек входа, уровней стоп-лосса (SL) и тейк-профита (TP). Это поможет в принятии торговых решений, обеспечивая методичный и прозрачный подход к выявлению возможных бычьих зон разворота.

Псевдокод:
// Шаг 1: Выявление точек свинга
- Обнаружение минимума cвинга (X).
- Выявление максимума свинга (A) после X.
- Обнаружение минимума cвинга (B) после A.
ЕСЛИ коррекция B НЕ составляет от 61,8% до 78,6% от XA:
- Паттерн отменяется и обнаружение запускается заново.
ЕСЛИ коррекция B составляет от 61,8% до 78,6% от XA:
- Выявление максимума cвинга (C) после B.
- Обнаружение минимума cвинга (D) после C.
ЕСЛИ D НЕ меньше чем X:
- ТОГДА паттерн отменяется и обнаружение запускается заново.
// Шаг 2: Отрисовка объектов-чартов
- Отрисовка объектов, соединяющих X → A → B → C → D.
- Точки отмечаются текстовыми объектами: X, A, B, C, D
- Отрисовка уровней коррекции Фибоначчи от X до A для подтверждения.
// Шаг 3: Определение торговых уровней
- Создание графических объектов для обозначения уровня входа, стоп-лосса и тейк-профита.
1.2.2. Медвежий паттерн
Индикатор Bearish Harmonic Pattern работает по аналогии со структурой бычьего паттерна, но в обратном направлении. Чтобы убедиться, что паттерн соответствует правилам гармонических паттернов, мы определим важные точки свинга. Паттерн будет определен путем выявления сначала максимума свинга (X), а затем минимума свинга (A). Затем будет определен максимум свинга (B), который должен откатиться на расстояние от 61,8% до 78,6% от отрезка XA. Затем, после того как мы зафиксируем минимум свинга (C), будет подтвержден финальный максимум свинга (D). Для подтверждения паттерна точка D должна находиться выше X, создавая возможную медвежью зону разворота.
После подтверждения структуры, мы будем использовать графические объекты, такие как трендовые линии, метки XABCD и уровни коррекции Фибоначчи, чтобы визуализировать паттерн. Чтобы помочь вам в определении важных торговых зон внутри паттерна, мы дополнительно укажем уровни входа, стоп-лосса (SL) и тейк-профита (TP).

Псевдокод:
// Шаг 1: Выявление точек свинга
- Обнаружение максимума cвинга (X).
- Обнаружение минимума cвинга (A) после X.
- Обнаружение максимума cвинга (B) после A.
ЕСЛИ коррекция B НЕ составляет от 61,8% до 78,6% от XA:
- Паттерн отменяется и обнаружение запускается заново.
ЕСЛИ коррекция B составляет от 61,8% до 78,6% от XA:
- Обнаружение минимума cвинга (C) после B.
- Обнаружение максимума cвинга (D) после C.
ЕСЛИ D НЕ выше X:
- Паттерн отменяется и обнаружение запускается заново.
// Шаг 2: Отрисовка объектов-чартов
- Отрисовка объектов, соединяющих X → A → B → C → D.
- Точки отмечаются текстовыми объектами: X, A, B, C, D
- Отрисовка уровней коррекции Фибоначчи от X до A для подтверждения.
// Шаг 3: Определение торговых уровней
- Создание графических объектов для обозначения уровня входа, стоп-лосса и тейк-профита.
2. Построение бычьих гармонических паттернов
В этом разделе мы начнем интеграцию индикатора Harmonic Pattern в язык MQL5. Мы уточним функции, необходимые для выявления точек свинга, подтверждения уровней коррекции и визуализации паттерна на графике с помощью графических объектов. Кроме того, мы настроим внешний вид нарисованных объектов для повышения ясности и удобства использования.
2.1. Идентификация максимумов и минимумов cвинга
Прежде чем мы сможем обнаружить гармонический паттерн, нам необходима надежная методика определения свинговых максимумов и минимумов на графике. Структура XABCD строится на основе точек свинга. Чтобы выявить максимум cвинга, мы определяем, является ли максимум свечи самым высоким среди заданного диапазона ближайших свечей. В аналогичном ключе, минимум свинга определяется, когда минимум свечи является самым низким в определенном диапазоне. Размер этого диапазона определяет чувствительность нашего обнаружения – большие значения фиксируют более значительные колебания, в то время как меньшие значения выявляют менее значительные колебания. На следующем этапе мы разработаем алгоритм для сканирования исторических ценовых данных, определения ключевых точек свинга и обеспечения их соответствия принципам формирования паттернов.
2.1.1. Функции определения свинговых максимумов и минимумов
Пример:
#property indicator_chart_window //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; }
Пояснение:
Директива #property indicator_chart_window сообщает MetaTrader 5 о том, что пользовательский индикатор должен отображаться на основном ценовом графике, а не в отдельном подокне. Это полезно при создании основанных на паттернах показаний, таких как индикатор Harmonic Pattern, или индикаторов, которые накладываются на ценовую активность, таких как трендовые линии, уровни поддержки и сопротивления и т.д. В отсутствие этой характеристики индикатор по умолчанию может отображаться в другом окне индикатора, подобно осцилляторам, таким как MACD или RSI.
Цель функции IsSwingLow заключается в определении локальных минимумов свинга на ценовом графике. Когда низ свечи перебивает низы окружающих свечей в рамках определенного диапазона, это называется минимумом свинга. Функция принимает значение окна проверки, которое устанавливает, сколько свечей до и после следует учитывать, массив минимальных цен и индекс оцениваемой свечи. Она определяет, является ли текущее минимальное значение самым низким, перебирая ближайшие свечи. Функция возвращает false, если обнаруживает более низкое значение в окружающем диапазоне, что доказывает, что текущая свеча не является минимумом свинга. В противном случае функция возвращает true, указывая на наличие соответствующего правилам минимума свинга.
По аналогии с этим, максимумы свинга (точки, где максимум свечи превышает максимумы ближайших свечей) определяются с помощью функции IsSwingHigh. Функция использует ту же логику, что и IsSwingLow, за тем исключением, что она гарантирует, что максимум по указанному индексу является наивысшим значением в диапазоне проверки, вместо того, чтобы искать наименьшее значение. Функция возвращает false, если какая-либо из окружающих свечей имеет более высокое значение. Если нет, функция подтверждает максимум свинга и возвращает true.
Для обнаружения значительных моментов изменения цены, которые служат основой для определения гармонических паттернов, требуются обе эти способности. После определения свинговых максимумов и минимумов, можно построить структуру паттерна XABCD путем соединения их трендовыми линиями и использования уровней Фибоначчи для подтверждения. С помощью этого метода индикатор гарантированно будет динамически подстраиваться под свежую ценовую информацию и соответствующим образом обновлять выявленные паттерны.
Аналогия:
Нахождение минимальной точки во впадине – это то, как работает функция IsSwingLow. Представьте, что вы находитесь на туристической тропе и пытаетесь найти самую глубокую впадину по пути. В течение проверочного интервала вы делаете несколько шагов вперед и несколько шагов назад. Текущая точка подтверждается как самая низкая впадина (минимум свинга), если она ниже каждой другой точки в этом диапазоне. Ваше текущее местоположение является лишь еще одной частью склона и фактически не представляет собой самую низкую точку, если какие-либо соседние точки находятся ниже. Потенциальные зоны разворота, где цены могут начать расти, определяются с помощью этой функции.
Функция IsSwingHigh работает по аналогии с поиском наивысшей точки на возвышенности. Считается, что максимум свинга найден, если вы стоите на одном месте и делаете несколько шагов вперед и назад, чтобы убедиться, что ваше текущее положение является самым высоким в этом диапазоне. Расстояние, на котором вы осуществляете проверку перед объявлением о максимуме или минимуме, зависит от интервала проверки; если он слишком короткий, незначительные отклонения могут быть приняты за важные точки, а если слишком длинный, вы можете упустить меньшие, но значительные тренды. Это равновесие гарантирует, что выявленные точки свинга являются значительными, а не просто произвольными колебаниями цены.

2.1.2. Выявление минимума свинга (X)
Следующим шагом после создания функций IsSwingLow и IsSwingHigh является их интеграция в функцию OnCalculate. Здесь ценовые данные, получаемые в реальном времени, будут обработаны логикой обнаружения точки свинга. Первой задачей является нахождение начального минимума свинга (X), с которого начинается гармонический паттерн. Используя созданную функцию, мы можем итеративно исследовать каждую свечу в истории цен OnCalculate, чтобы определить, соответствует ли она критериям для минимума свинга.
Пример:
#property indicator_chart_window // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points // CHART ID long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.) //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_line; // Unique name for the trend line object. string X_letter; // Unique name for the text label object. //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X } } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; } //+------------------------------------------------------------------+
Вывод:

Пояснение:
Мы начинаем с указания некоторых ключевых входных данных, поскольку определение минимума свинга (X) является первым шагом в поиске бычьего гармонического паттерна. Переменная LookbackBars определяет количество баров, которые следует проверить до и после конкретной точки, чтобы убедиться, что эта точка свинга соответствует правилам. Это помогает отсеять небольшие колебания цен, которые не приводят к значительным минимумам. Чтобы убедиться, что скрипт обрабатывает только управляемое количество баров из истории, и обеспечить тем самым эффективность, в аргументе bars_check указывается, сколько баров должно быть проанализировано.
Кроме того, мы используем ChartID() для получения chart_id, что позволяет нам создавать графические объекты, такие как трендовые линии и текстовые метки, и управлять ими. Мы объявляем переменные "X" для хранения цены обнаруженного минимума свинга (X), "X_time" для записи времени его возникновения, а также "X_line" и "X_letter" для присвоения различных имен объектам, которые будут использоваться для идентификации этой точки на графике. Текстовая метка, указывающая на минимум свинга, будет создана с помощью переменной X_letter, а трендовая линия будет создана для улучшенного отображения с помощью переменной X_line.
Затем мы используем эти предварительные конфигурации для определения минимума свинга (X) в функции OnCalculate. Благодаря условию if(rates_total >= bars_check) мы начинаем проверку только тогда, когда доступно достаточное количество ценовых данных. Чтобы избежать проверки неполных ценовых данных, цикл проходит по историческим барам, начиная с rates_total - bars_check и заканчивая rates_total - LookbackBars. Функция IsSwingLow в цикле определяет, является ли данная точка минимумом свинга. Когда обнаруживается соответствующий правилам минимум свинга, мы фиксируем его время и цену, придумываем оригинальные названия объектов, а затем создаем визуальные компоненты для графика.
Текстовый объект создается с помощью ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X) для обозначения минимума свинга буквой "X". Для дальнейшей иллюстрации выявленного минимума свинга мы используем ObjectCreate(chart_id, X_line, OBJ_TREND, 0, X_time, X, time[i+LookbackBars], X) для построения трендовой линии на уровне X. Это гарантирует, что первая важная точка в нашей структуре гармонического паттерна будет точно идентифицирована и отображена на графике, закладывая основу для определения последующих точек паттерна.
2.1.3. Выявление максимума свинга (A)
Нахождение максимума свинга (A) является следующим этапом после успешного выявления минимума свинга (X). Эта точка имеет решающее значение для формирования гармонического паттерна, поскольку это первое заметное движение вверх после X. Точно так же, как мы обнаружили X, для нахождения A мы воспользуемся IsSwingHigh, но на этот раз мы будем искать пик, а не спад.
Мы будем искать точку в ценовых данных после X, где максимум превышает максимумы окружающих баров в пределах заданного диапазона LookbackBars. После определения соответствующего правилам максимума свинга мы зафиксируем его цену и время, дадим ему отличительное имя и используем графические объекты для его корректного описания.
Пример:
#property indicator_chart_window // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points // CHART ID long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.) //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_line; // Unique name for the trend line object. string X_letter; // Unique name for the text label object. //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_line; // Unique name for the trend line object. string A_letter; // Unique name for the text label object. //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A). A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object. A_letter = StringFormat("A%d", j); // Unique name for the text label object. ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green break; } } } } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; } //+------------------------------------------------------------------+
Вывод:

Пояснение:
Сначала мы объявляем переменные, необходимые для определения максимума свинга (A). Переменная A_time регистрирует точный момент, когда наступил максимум свинга, в то время как переменная A хранит ценовое значение этого максимума. Мы используем A_line в качестве названия для объекта трендовой линии и A_letter в качестве текстовой метки, чтобы гарантировать уникальное визуальное представление у каждой обнаруженной точки A. Эти переменные помогают в создании объектов-чартов, которые наглядно отображают точку A, что в свою очередь позволяет проще визуализировать структуру гармонического паттерна.
После выявления точки X, код проходит по ценовым данным, начиная с местоположения X, чтобы найти A. Функция IsSwingHigh, которая определяет, является ли бар локальным максимумом в пределах заданного диапазона LookbackBars, используется для проверки каждого бара. Условие time[j] > X_time используется для гарантии того, что максимум свинга A наступает после X.
Это обеспечивает поддержание правильной последовательности в гармоническом паттерне, выбирая точку A только в том случае, если ее временная метка больше X_time. Записываются цена и время соответствующего правилам максимума свинга, а объектам трендовой линии и текстовой метки присваиваются отличительные идентификаторы, если такой максимум обнаружен. Строится зеленая трендовая линия, чтобы выделить найденный максимум свинга, и на нем размещается текстовая метка с буквой "A". Как только был идентифицирован первый соответствующий правилам максимум свинга (A), цикл прерывается с помощью выражения "break;". Цикл будет продолжать искать новые максимумы свинга без "break;», возможно перезаписывая A более поздним максимумом свинга. Поскольку нам нужен только первый экземпляр A, следующий за X, оператор "break;" гарантирует, что цикл прекратит итерацию, как только будет обнаружен максимум свинга, позволяя сохранить первую действительную точку A и избежать бессмысленных вычислений.
2.1.4. Выявление минимума свинга (B)
Нахождение следующего минимума свинга B является следующим шагом после определения максимума свинга A. Поиск B начинается сразу после того, как выявлена точка A, поскольку точка B должна следовать за A. Это достигается путем итерации по ценовым данным, начиная с позиции A, и использования функции IsSwingLow для сравнения бара с ближайшими барами в диапазоне LookbackBars, чтобы определить, является ли он минимумом свинга. Кроме того, для выявления гармонического паттерна ожидается, что точка B окажется под определенным уровнем коррекции Фибоначчи относительно XA. Однако в настоящее время мы не будем обсуждать проверку с помощью Фибоначчи; нас интересует только нахождение точки B.
Цена и время формирования минимума свинга фиксируются в переменных B и B_time, соответственно, как только минимум будет выявлен. Мы даем трендовой линии (B_line) и текстовой метке (B_letter) отличительные имена, чтобы обозначить это место на графике. Трендовая линия создается для визуального соединения и выделения текстового объекта с меткой "B", который создается в точке минимума свинга. Этот этап гарантирует, что три основных точки (X, A и B), которые теперь будут служить основой для нашей структуры гармонического паттерна, будут отмечены.
Пример:
#property indicator_chart_window // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points // CHART ID long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.) //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_line; // Unique name for the trend line object. string X_letter; // Unique name for the text label object. //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_line; // Unique name for the trend line object. string A_letter; // Unique name for the text label object. //B double B; // Price of the swing low (B). datetime B_time; // Time of the swing low (B). string B_line; // Unique name for the trend line object. string B_letter; // Unique name for the text label object. //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // This function is called when the indicator is removed or the chart is closed. // Delete all objects (lines, text, etc.) from the chart to clean up. ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A). A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object. A_letter = StringFormat("A%d", j); // Unique name for the text label object. ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time) { // If a swing low is found, store its price, time, and create a name for the object. B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_line = StringFormat("BLow%d", k); // Unique name for the trend line object. B_letter = StringFormat("B%d", k); // Unique name for the text label object. ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to green break; } } break; } } } } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; } //+------------------------------------------------------------------+
Вывод:

Пояснение:
Цена минимума свинга, следующего за точкой A, обозначается переменной B. Для ее определения используется цикл, который начинается с точки A и вызывает функцию IsSwingLow для нахождения следующего минимума свинга. Цена, время и отличительные имена для трендовой линии и текстовой метки сохраняются после их нахождения. Затем, чтобы указать B на графике, компьютер генерирует трендовую линию темно-синего цвета и текстовую метку ("B") зеленого цвета. Для обеспечения корректной последовательности точек свинга при генерации паттерна оператор break гарантирует, что будет выбрана только первая действительная точка B. Поскольку точки X и B обе распознаются как минимумы свинга на протяжении итерации цикла, в текущей реализации они могут иногда перекрывать друг друга. Учитывая, что программа оценивает каждую точку свинга по очереди, такое поведение является ожидаемым. Тем не менее, это может привести к избыточным аннотациям на графике, если X и B совпадают.
Вы можете включить условие, чтобы убедиться, что точка B является отдельным минимумом свинга, который следует за точкой A, и что при этом точка B не совпадает с X, если вы хотите избежать перекрытия X и B. Я включу в код возможность выбора того, могут ли X и B перекрывать друг друга. Индикатор будет работать так же, как и сейчас, когда точки X и B могут быть одинаковыми минимумами, если перекрытие допустимо. При этом если перекрытие будет запрещено, индикатор обеспечит, чтобы точка B была минимумом свинга, отдельным от точки X.
Запрет на перекрытие приведет к тому, что будет обнаруживаться меньше гармонических паттернов, поскольку точка B должна быть отдельным минимумом, и совпадение с X не допускается. В зависимости от предпочтений пользователя касательно распознавания паттернов, эта опция предлагает гибкость.
Пример:
input bool overlap = false; //Allow Overlaping
for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time) { // If a swing low is found, store its price, time, and create a name for the object. B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_line = StringFormat("BLow%d", k); // Unique name for the trend line object. B_letter = StringFormat("B%d", k); // Unique name for the text label object. ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to green if(overlap == false) { i = k; } if(overlap == true) { i = i; } break; } }Вывод:

Пояснение:
Могут ли точки X и B относится к одному и тому же минимуму, зависит от входного параметра overlap. Строка i = k; гарантирует, что итерация пропускается после нахождения точки B, что исключает совпадение точек B с X, когда параметр overlap имеет значение false. Это ограничивает количество гармонических паттернов, которые могут быть распознаны, одновременно минимизируя избыточное выявление за счет поддержания четкого разделения между точками свинга.
При этом когда параметр overlap имеет значение false, строка i = i; указывает, что цикл продолжится в обычном режиме, не требуя пропуска итерации. Это позволяет обнаруживать больше гармонических паттернов, если допускается перекрытие точек X и B, когда они естественно возникают на одном и том же уровне цены. Тем не менее, это также может обеспечить некоторую повторяемость в разметке паттернов. Предотвращая перекрытие X и B, код обеспечивает, что эти точка свинга будут отдельными. Как видно на рисунке, точка B теперь появляется независимо от X, сохраняя более отчетливую структуру паттерна.
2.1.5. Выявление максимума свинга (C)
Максимум свинга (C) обнаруживается после установления минимума свинга (X), максимума свинга (A) и последующего минимума свинга (B). Эта точка имеет решающее значение для построения структуры гармонического паттерна, поскольку она определяет общую форму паттерна и возможную зону разворота.
Чтобы визуально обозначить эту точку, и трендовой линии (C_line), и текстовой метке (C_letter) присваиваются свои уникальные имена. Для визуального соединения и выделения текстового элемента с меткой "C", который расположен на максимуме свинга на графике, строится трендовая линия. Этот этап гарантирует размещение четырех основных точек (X, A, B и C), которые составляют структуру гармонического паттерна.
Пример:
//C double C; // Price of the swing high (C). datetime C_time; // Time of the swing high (C). string C_line; // Unique name for the trend line object. string C_letter; // Unique name for the text label object.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A). A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object. A_letter = StringFormat("A%d", j); // Unique name for the text label object. ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time) { // If a swing low is found, store its price, time, and create a name for the object. B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_line = StringFormat("BLow%d", k); // Unique name for the trend line object. B_letter = StringFormat("B%d", k); // Unique name for the text label object. ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object. C_letter = StringFormat("C%d", l); // Unique name for the text label object. ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, C_time, C); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,C_time,C,time[l+LookbackBars],C); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown if(overlap == false) { i = l; } if(overlap == true) { i = i; } break; } } break; } } break; } } } } } //--- return value of prev_calculated for next call return(rates_total); }Вывод:

Пояснение:
После определения минимума свинга (B) данная часть кода отвечает за определение максимума свинга (C), что является следующим важным шагом. Проходя по ценовым данным, цикл for(int l = j; l < rates_total - LookbackBars; l++), начинается с индекса j, на котором был найден предыдущий максимум (A). Чтобы убедиться, что точка C следует за B в последовательности, цикл продолжает поиск, пока не найдет самые последние бары. Для поддержания надлежащей последовательности в паттерне мы проверяем через условие if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time), является ли текущая свеча максимумом свинга в заданном интервале проверки, и проверяем, что ее временная метка расположена позже, чем у точки B.
Цена и время соответствующего правилам максимума сохраняются в переменных C и C_time, соответственно, а объекты трендовой линии и текстовой метки получают отличительные имена (C_line и C_letter). С помощью ObjectCreate(chart_id, C_line, OBJ_TREND, 0, C_time, C, time[l+LookbackBars], C) строится трендовая линия, чтобы выделить текстовую метку "C" на указанном максимуме свинга. Чтобы визуально отличить трендовую линию C от других сегментов, ObjectSetInteger(chart_id, C_line, OBJPROP_COLOR, clrSaddleBrown); задает коричневый цвет линии (SaddleBrown).
Блок с условием в конце гарантирует, что перекрывающиеся паттерны обрабатываются корректно: i = l; предотвращает использование предыдущих точек свинга в другом паттерне, если параметр overlap имеет значение false. Несколько паттернов могут перекрывать друг друга, если параметр overlap имеет значение true, так как i = i; сохраняет текущее значение. Наконец, после определения первого соответствующего правилам максимума (C) оператор break; завершает цикл, обеспечивая эффективность за счет избежания ненужных повторений.
2.1.6. Выявление минимума свинга (D)
Определение минимума свинга (D) происходит после определения максимума свинга (C). Это гарантирует, что ценовое движение будет иметь корректную последовательность для формирования гармонических паттернов. Чтобы найти следующий минимум свинга после C в хронологическом порядке, поиск точки D начинается с места, где была идентифицирована точка C. Для подтверждения соответствия минимума свинга правилам должны быть выполнены два условия. Во-первых, в пределах установленного интервала проверки текущая цена должна определяться как минимум свинга. Во-вторых, для поддержания правильного порядка в паттерне, временная метка этого минимума должна стоять позже, чем у точки C.
Отмечаются цена и время соответствующего правилам минимума свинга, а для трендовой линии и текстовой метки создаются отличительные идентификаторы, которые соответствуют этому минимуму. Чтобы сделать минимум свинга более заметным в паттерне, для его визуального обозначения проводится трендовая линия, и на его месте вставляется текстовая метка.
Пример:
//D double D; // Price of the swing low (D). datetime D_time; // Time of the swing low (D). string D_line; // Unique name for the trend line object. string D_letter; // Unique name for the text label object. //Trend string XA_line; // Unique name for XA trend line object. string AB_line; // Unique name for AB trend line object. string BC_line; // Unique name for BC trend line object. string CD_line; // Unique name for CD trend line object. //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A). A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object. A_letter = StringFormat("A%d", j); // Unique name for the text label object. ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time) { // If a swing low is found, store its price, time, and create a name for the object. B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_line = StringFormat("BLow%d", k); // Unique name for the trend line object. B_letter = StringFormat("B%d", k); // Unique name for the text label object. ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object. C_letter = StringFormat("C%d", l); // Unique name for the text label object. ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, C_time, C); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,C_time,C,time[l+LookbackBars],C); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown for(int m = l; m < rates_total - (LookbackBars / 2); m++) { if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_line = StringFormat("DLow%d", m); // Unique name for the trend line object. D_letter = StringFormat("D%d", m); // Unique name for the text label object. ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,X_time,X,A_time,A); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,A_time,A,B_time,B); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,B_time,B,C_time,C); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,C_time,C,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width if(overlap == false) { i = m; } if(overlap == true) { i = i; } break; } } break; } } break; } } break; } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Вывод:

Пояснение:
Цена минимума в точке D распознанного паттерна представлена переменной D. Точка D является ключевой для торговли по гармоническим паттернам: в этой точке ценовое движение может развернуться и завершить паттерн. Переменная D_time фиксирует временную метку возникновения этого минимума в дополнение к переменной D. Для визуального обозначения этого минимума на графике также создаются и маркируются трендовая линия и текстовый элемент с помощью D_line и D_letter.
Программа проходит по ценовым данным, чтобы найти точку D в цикле, который начинается с for(int m = l; m < rates_total - (LookbackBars / 2); m++).... Чтобы уточнить, является ли текущая цена на индексе m минимумом свинга, используется функция IsSwingLow(), при этом необходимо убедиться, что минимум находится после точки C по времени. Цена и время действительного минимума свинга записываются в переменные D и D_time, соответственно.
Индикатор создает визуальные элементы на графике после нахождения точки D. На месте локального минимума функция ObjectCreate() создает текстовую метку (D_letter) с буквой "D" и окрашивает её в зеленый цвет для удобства. Для поддержания согласованности визуального представления в точке D также проводится трендовая линия (D_line): она растягивается на LookbackBars периодов и имеет коричневый цвет (clrSaddleBrown).
На этом этапе программа также создает трендовые линии, которые связывают выявленные точки свинга. Для визуального соединения соответствующих точек (X с A, A с B, B с C и C с D) создаются объекты XA_line, AB_line, BC_line и CD_line. Эти линии помогают трейдерам анализировать возможные изменения цены, очерчивая гармонический паттерн. Ширина и цвет этих линий регулируются для улучшения видимости на графике.
После успешного распознавания паттерна XABCD крайне важно решить несколько ключевых вопросов. Логика программы заключается в том, что после определения точки X она не будет обновлять X до завершения последовательности ABCD. Это подразумевает, что программа не изменит X, если в промежутке от X до A возникнет новый минимум, который ниже первоначально определенного X. Следовательно, паттерн не сможет точно отразить реальную рыночную структуру.
Мы должны внедрить систему, которая непрерывно проверяет, является ли X по-прежнему самым низким значением в промежутке от X до A, чтобы гарантировать действительность паттерна. Необходимо динамически обновлять X, если в пределах этого диапазона обнаруживается более низкий минимум. Чтобы гарантировать, что в этом сегменте не будут упущены более высокие точки, точка A также должна быть самым высоким максимумом в промежутке от A до B. Аналогично, точка C должна быть самым высоким максимумом в промежутке от C до D, а точка B должна быть самым низким минимумом в промежутке между B и C. Следуя этим требованиям, мы можем улучшить точность обнаружения паттернов и избежать некорректных выявлений, вызванных строгим выбором точек. Этот метод сохраняет целостность структуры гармонического паттерна, позволяя индикатору оставаться адаптивным и отзывчивым к новым ценовым движениям.
Пример:
//X int x_a_bars; // Number of bars between XA int x_lowest_index; // Index of the lowest bar double x_a_ll; // Price of the lowest bar datetime x_a_ll_t; // Time of the lowest bar //A int a_b_bars; // Number of bars between AB int a_highest_index; // Index of the highest bar double a_b_hh; // Price of the highest bar datetime a_b_hh_t; // Time of the highest bar //B int b_c_bars; // Number of bars between BC int b_lowest_index; // Index of the lowest bar double b_c_ll; // Price of the lowest bar datetime b_c_ll_t; // Time of the lowest bar //C int c_d_bars; // Number of bars between CD int c_highest_index; // Index of the highest bar double c_d_hh; // Price of the highest bar datetime c_d_hh_t; // Time of the highest bar //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingLow(low, i, LookbackBars)) { // If a swing low is found, store its price, time, and create a name for the objects to mark the X. X = low[i]; // Price of the swing low (X). X_time = time[i]; // Time of the swing low (X). X_line = StringFormat("XLow%d", i); // Unique name for the trend line object. X_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A). A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object. A_letter = StringFormat("A%d", j); // Unique name for the text label object. for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time) { // If a swing low is found, store its price, time, and create a name for the object. B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_line = StringFormat("BLow%d", k); // Unique name for the trend line object. B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object. C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - (LookbackBars / 2); m++) { if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_line = StringFormat("DLow%d", m); // Unique name for the trend line object. D_letter = StringFormat("D%d", m); // Unique name for the text label object. //D ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //C c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time); c_highest_index = ArrayMaximum(high,l, c_d_bars); c_d_hh = high[c_highest_index]; //C - D Highest High and time c_d_hh_t = time[c_highest_index]; ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //B b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t); b_lowest_index = ArrayMinimum(low,k, b_c_bars); b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time b_c_ll_t = time[b_lowest_index]; ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue //A a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t); a_highest_index = ArrayMaximum(high,j, a_b_bars); a_b_hh = high[a_highest_index]; //A - B Highest High and time a_b_hh_t = time[a_highest_index]; ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green //X x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t); x_lowest_index = ArrayMinimum(low,i, x_a_bars); x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time x_a_ll_t = time[x_lowest_index]; ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width if(overlap == false) { i = m; } if(overlap == true) { i = i; } break; } } break; } } break; } } break; } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Вывод:

Пояснение:
Точка X служит начальной точкой отсчета для выявления возможных паттернов разворота при торговле по гармоническим паттернам. В бычьем паттерне она обозначает значительный минимум свинга; в медвежьем паттерне – максимум свинга. Эта точка является необходимой, поскольку она закладывает основу для измерения уровней коррекции и расширения Фибоначчи, что помогает в подтверждении гармонических формаций. Цель кода заключается в том, чтобы распознать точку X, зафиксировать ее время и цену, а также визуально отобразить ее на графике. Для создания всей структуры паттерна код также соединяет X с другими точками свинга (A, B, C и D).
Для хранения данных о точке X используется ряд важных переменных. Количество баров между X и A фиксируется переменной x_a_bars, что помогает определить относительные расстояния между отрезками паттерна. Чтобы точно определить бар, в котором возникла точка X, переменная x_lowest_index отслеживает индекс массива с самой низкой ценой в этом диапазоне. Временная метка, связанная с наименьшей ценой, сохраняется в x_a_ll_t, в то время как сама цена сохраняется в x_a_ll.
Определение количества баров между X и A с помощью функции Bars() является первым шагом к определению точки X. Эта функция выдает необходимый интервал для анализа, подсчитывая количество баров между этими двумя точками свинга. После определения интервала программа использует ArrayMinimum() для поиска по заданным барам и возвращает индекс самой низкой цены. Затем, используя массивы low[x_lowest_index] и time[x_lowest_index], соответственно, мы получаем фактические цену и время точки X.
После выявления точки X скрипт использует объект MetaTrader для наглядного отображения точки на графике. Чтобы убедиться, что вы можете быстро определить этот ключевой момент, используется ObjectCreate() для создания текстовой метки (X_letter) на точке минимума X. Уточним: текст "X" передается в текстовую метку, которая размещается в точке времени и цены, соответствующей точке X. Для надлежащего обозначения минимума свинга в точке X также формируется трендовая линия (X_line) с помощью ObjectCreate(). Линия растягивается на заранее определенное количество баров (LookbackBars).
Программа создает трендовые линии, чтобы связать точку X после ее определения с другими важными точками (A, B, C и D). Благодаря этим трендовым линиям вы сможете лучше увидеть гармонический паттерн. Функция ObjectCreate() используется для рисования первого соединения – линии XA, которая соединяет точки X и A. Эта линия имеет ширину три пикселя и окрашена в коричневый цвет (clrSaddleBrown) для обеспечения видимости. Аналогичным образом, с помощью дополнительных трендовых линий отображаются остальные отрезки паттерна – линия AB, которая соединяет точки A и B, линия BC, которая соединяет точки B и C, и линия CD, которая соединяет точки C и D. Для создания единого визуального представления каждая из этих линий сохраняет одинаковую ширину и цветовые характеристики.
2.2. Проверка точки B с помощью уровней коррекции Фибоначчи относительно XA
В этом разделе мы проверим минимум B, уточнив, остается ли он выше уровня 78,6%, при этом опустившись ниже уровня коррекции 61,8% относительно XA. Поскольку это гарантирует, что точка B соответствует необходимым соотношениям Фибоначчи, которые помогают определить структуру паттерна, эта фаза проверки является важной для торговли по бычьим паттернам. Вероятность соответствия гармонического построения правилам будет выше, если точка B находится в этом диапазоне. В противном случае, если B слишком высоко или слишком низко, паттерн может быть неподлинным либо потребует дополнительного подтверждения.
Сначала мы определяем уровни коррекции Фибоначчи для отрезка XA, чтобы провести эту проверку. Коррекция Фибоначчи является популярным инструментом технического анализа для выявления возможных разворотов ценового движения.
Помимо проверки B, мы воспользуемся графическими инструментами языка MQL5 для добавления объекта коррекции Фибоначчи на график. Инструмент Фибоначчи будет построен с использованием функции ObjectCreate(), при этом точка X будет служить в качестве начальной, а точка A – в качестве конечной. После создания объекта мы модифицируем его, добавив дополнительные уровни Фибоначчи, которые MetaTrader 5 по умолчанию не поддерживает. Это обеспечит отображение всех соответствующих зон коррекции.
Пример:
//Fibo string XA_fibo; // Unique name for XA fibo double lvl_61_8; // Level 61.8 double lvl_78_6; // Level 78.6 string fibo_618_786; // Unique name for the object that marks 61.8 and 78.6. string fibo_78_6_txt; // Unique name for text "78.6" cause its not available by default
for(int m = l; m < rates_total - (LookbackBars / 2); m++) { if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_line = StringFormat("DLow%d", m); // Unique name for the trend line object. D_letter = StringFormat("D%d", m); // Unique name for the text label object. //D ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //C c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time); c_highest_index = ArrayMaximum(high,l, c_d_bars); c_d_hh = high[c_highest_index]; //C - D Highest High and time c_d_hh_t = time[c_highest_index]; ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //B b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t); b_lowest_index = ArrayMinimum(low,k, b_c_bars); b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time b_c_ll_t = time[b_lowest_index]; ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue //A a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t); a_highest_index = ArrayMaximum(high,j, a_b_bars); a_b_hh = high[a_highest_index]; //A - B Highest High and time a_b_hh_t = time[a_highest_index]; ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green //X x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t); x_lowest_index = ArrayMinimum(low,i, x_a_bars); x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time x_a_ll_t = time[x_lowest_index]; ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width lvl_61_8 = a_b_hh - ((61.8/100) * (a_b_hh - x_a_ll)); // Calculating level 61.8 lvl_78_6 = a_b_hh - ((78.6/100) * (a_b_hh - x_a_ll)); // Calculating level 78.6 //XA FIBO XA_fibo = StringFormat("XA FIB0 %d", i); ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels for(int i = 1; i <= 6; i++) { ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown } fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i); fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i); ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6 ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6 ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size if(overlap == false) { i = m; } if(overlap == true) { i = i; } break; } }Вывод:

Пояснение:
Чтобы подтвердить, что точка B находится внутри заданного диапазона, в данном разделе мы применяем уровни коррекции Фибоначчи к отрезку XA. В техническом анализе уровень Фибоначчи часто используется для определения возможных зон разворота. Важно проверить, соответствует ли точка B ожидаемым уровням коррекции относительно XA. В частности, точка B должна находиться в между уровнями коррекции 61,8% до 78,6% относительно XA. Будет проще определить, находится ли точка B в допустимом диапазоне, если мы создадим объект коррекции Фибоначчи и выделим эту зону на графике. Сначала мы объявляем несколько важных переменных для достижения этой цели. Чтобы гарантировать, что каждое изображение Фибоначчи корректно идентифицируется, переменная XA_fibo будет хранить уникальное имя для объекта коррекции Фибоначчи.
После определения уровней с помощью функции ObjectCreate() создается объект коррекции Фибоначчи относительно отрезка XA. Рассмотрев этот объект, который растягивается от точки X (минимум) до точки A (максимум), вы сможете понять, как цена взаимодействует с уровнями Фибоначчи. Мы установили шесть уровней Фибоначчи и изменили их цвет на SaddleBrown, чтобы обеспечить более явную визуализацию и убедиться, что каждый уровень легко наблюдаем и отличается от других элементов графика.
Мы дополнительно выделяем зону коррекции между 61,8% и 78,6%, создавая прямоугольный объект, который привлекает к ней внимание, поскольку это важная область. Определить, находится ли точка B в ожидаемом диапазоне, достаточно просто, поскольку прямоугольник охватывает уровни 61,8% и 78,6%. Это дает возможность быстрой проверки паттерна без необходимости трудоемких проверок уровней коррекции. Кроме того, прямоугольник имеет цвет SaddleBrown, что поддерживает стилевую согласованность диаграммы.
Мы также включаем текстовую метку для уровня коррекции 78,6%, что является еще одним значительным изменением. Мы вручную создаем текстовый объект для отображения "78.6" на соответствующем уровне цены, поскольку инструмент Фибоначчи в MetaTrader 5 по умолчанию не содержит этот уровень. Это повышает точность выявления гармонических паттернов, гарантируя, что трейдеры смогут увидеть точное местоположение коррекционного уровня 78,6%. Внедряя эти изменения на практике, мы упрощаем проверку того, соответствует ли точка B правилам в рамках гармонического паттерна. Объект коррекции Фибоначчи, отмеченная зона 61,8% – 78,6% и дополнительная текстовая метка обеспечивают точную и визуально интуитивную технику для проверки гармонических паттернов.
Теперь, когда у нас есть все необходимое, мы должны убедиться, что все графические объекты отрисовываются только тогда, когда требования для действительного паттерна удовлетворены. В частности, точка D должна находиться ниже точки X, а точка B должна находиться между уровнями коррекции 61,8% и 78,6% относительно XA. На график не следует добавлять объекты, если эти требования не выполнены.
Примеры:
for(int m = l; m < rates_total - (LookbackBars / 2); m++) { if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_line = StringFormat("DLow%d", m); // Unique name for the trend line object. D_letter = StringFormat("D%d", m); // Unique name for the text label object. //C c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time); c_highest_index = ArrayMaximum(high,l, c_d_bars); c_d_hh = high[c_highest_index]; //C - D Highest High and time c_d_hh_t = time[c_highest_index]; //B b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t); b_lowest_index = ArrayMinimum(low,k, b_c_bars); b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time b_c_ll_t = time[b_lowest_index]; //A a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t); a_highest_index = ArrayMaximum(high,j, a_b_bars); a_b_hh = high[a_highest_index]; //A - B Highest High and time a_b_hh_t = time[a_highest_index]; //X x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t); x_lowest_index = ArrayMinimum(low,i, x_a_bars); x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time x_a_ll_t = time[x_lowest_index]; lvl_61_8 = a_b_hh - ((61.8/100) * (a_b_hh - x_a_ll)); // Calculating level 61.8 lvl_78_6 = a_b_hh - ((78.6/100) * (a_b_hh - x_a_ll)); // Calculating level 78.6 if((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll)) { //D ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //C ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //B ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue //A ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green //X ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width //XA FIBO XA_fibo = StringFormat("XA FIB0 %d", i); ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels for(int i = 1; i <= 6; i++) { ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown } fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i); fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i); ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6 ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6 ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size } if(overlap == false) { i = m; } if(overlap == true) { i = i; } break; } }
Вывод:

Пояснение:
Проверка истинности условия if ((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll)) гарантирует, что все графические объекты отрисовываются только тогда, когда найден правильный гармонический паттерн. Это предотвращает переполнение диаграммы посторонними элементами и ошибочными паттернами. Затем скрипт создает и отображает все необходимые компоненты, такие как уровни коррекции Фибоначчи, трендовые линии, соединяющие точки X, A, B, C и D, а также текстовые метки, обозначающие каждую важную точку, когда требование выполнено. Обеспечивая отображение только корректно сформированных паттернов, данный метод повышает точность и сохраняет четкое, информативное представление графика.
2.3. Визуализация треугольников XAB и BCD
Чтобы лучше визуализировать гармонический паттерн, в этом разделе мы разметим структуры XAB и BCD треугольниками. Эти треугольники визуально помогут упростить распознавание и анализ структуры паттерна на графике.
Это будет достигнуто путем соединения точек X, A и B для первого треугольника и точек B, C и D для второго треугольника с помощью объектов OBJ_TRIANGLE. Треугольник BCD будет отображать последний отрезок паттерна, укрепляя структуру, которая ведет к точке D (возможной зоне разворота), в то время как треугольник XAB будет подчеркивать первый отрезок, подтверждая взаимосвязь между X, A и B.
Пример:
string X_A_B; string B_C_D; if((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll)) { //D ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //C ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //B ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue //A ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green //X ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width //XA FIBO XA_fibo = StringFormat("XA FIB0 %d", i); ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels for(int i = 1; i <= 6; i++) { ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown } fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i); fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i); ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6 ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6 ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,x_a_ll_t,x_a_ll, a_b_hh_t, a_b_hh, b_c_ll_t, b_c_ll); ObjectSetInteger(chart_id,X_A_B,OBJPROP_COLOR,clrCornflowerBlue); ObjectSetInteger(chart_id,X_A_B,OBJPROP_FILL,true); B_C_D = StringFormat("BCD %d", i); ObjectCreate(chart_id,B_C_D,OBJ_TRIANGLE,0,b_c_ll_t,b_c_ll, c_d_hh_t, c_d_hh, D_time, D); ObjectSetInteger(chart_id,B_C_D,OBJPROP_COLOR,clrCornflowerBlue); ObjectSetInteger(chart_id,B_C_D,OBJPROP_FILL,true); }
Вывод:

Пояснение:
Уникальные названия для треугольников, которые составляют сегменты XAB и BCD гармонического паттерна, хранятся в переменных X_A_B и B_C_D. Три точки – X, A и B – используются для генерации треугольника X_A_B. Эти точки определяются их соответствующими временными метками и ценовыми значениями (x_a_ll_t, x_a_ll, a_b_hh_t, a_b_hh, b_c_ll_t, b_c_ll). Треугольник B_C_D, который использует значения (b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, D_time, D), аналогичным образом соединяет точки B, C и D. StringFormat() используется для присвоения каждому треугольнику уникального имени, что позволяет рисовать различные паттерны без проблем с именованием.
Функция ObjectSetInteger() используется для установки визуальных характеристик треугольников после их создания с помощью функции ObjectCreate(). Атрибуту OBJPROP_FILL присваивается значение true, что гарантирует, что треугольники будут заполнены заливкой, а не просто обведены, и оба треугольника будут иметь цвет "васильковый синий" (clrCornflowerBlue). Улучшая ясность и упрощая проверку потенциальных торговых возможностей, эти визуальные компоненты помогают трейдерам быстро идентифицировать структуру паттерна.
2.4. Указание точки входа, стоп-лосса и тейк-профита
В этом разделе мы установим точку входа, уровень стоп-лосса (SL) и уровень тейк-профита (TP) для определения важных торговых уровней для обнаруженного гармонического паттерна. Точка D, наименьший минимум паттерна, является местом, где устанавливается стоп-лосс. Поскольку гармонические паттерны предсказывают развороты цен, установка стоп-лосса на уровне D минимизирует возможные убытки, гарантируя, что паттерн будет аннулирован, если рынок пересечет этот уровень.
Точкой входа будет индекс точки D плюс половина LookbackBars. Это гарантирует небольшую задержку после выявления точки D, что дает возможность подтвердить ценовое движение перед совершением сделки. Поскольку точка C является последним максимумом свинга перед ожидаемым движением вверх, она будет служить уровнем фиксации прибыли. Метки и горизонтальные линии для уровней SL, входа и TP создаются для отображения этих уровней на графике. Эти элементы обеспечивают ясность при торговле на основе паттерна, помогая трейдерам быстро идентифицировать сетап.
Пример:
//Signal string buy_txt; string sell_txt; string entry_line; string tp_line; string sl_line; string tp_txt; string sl_txt;
buy_txt = StringFormat("Buy %d", i); ObjectCreate(chart_id,buy_txt,OBJ_TEXT,0,time[m + (LookbackBars / 2)],open[m + (LookbackBars / 2)]); ObjectSetString(chart_id,buy_txt,OBJPROP_TEXT,"BUY"); ObjectSetInteger(chart_id,buy_txt,OBJPROP_COLOR,clrCornflowerBlue); ObjectSetInteger(chart_id,buy_txt,OBJPROP_FONTSIZE,10); entry_line = StringFormat("Buy Entry Line %d", i); ObjectCreate(chart_id,entry_line,OBJ_TREND,0,time[m + (LookbackBars / 2)],open[m + (LookbackBars / 2)], c_d_hh_t,open[m + (LookbackBars / 2)]); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrCornflowerBlue); sl_txt = StringFormat("Buy SL %d", i); ObjectCreate(chart_id,sl_txt,OBJ_TEXT,0,time[m + (LookbackBars / 2)],D); ObjectSetString(chart_id,sl_txt,OBJPROP_TEXT,"SL"); ObjectSetInteger(chart_id,sl_txt,OBJPROP_COLOR,clrCornflowerBlue); ObjectSetInteger(chart_id,sl_txt,OBJPROP_FONTSIZE,10); sl_line = StringFormat("Buy SL Line %d", i); ObjectCreate(chart_id,sl_line,OBJ_TREND,0,time[m + (LookbackBars / 2)],D,c_d_hh_t,D); ObjectSetInteger(chart_id,sl_line,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,sl_line,OBJPROP_COLOR,clrCornflowerBlue); tp_txt = StringFormat("Buy TP %d", i); ObjectCreate(chart_id,tp_txt,OBJ_TEXT,0,time[m +(LookbackBars / 2)],c_d_hh); ObjectSetString(chart_id,tp_txt,OBJPROP_TEXT,"TP"); ObjectSetInteger(chart_id,tp_txt,OBJPROP_COLOR,clrCornflowerBlue); ObjectSetInteger(chart_id,tp_txt,OBJPROP_FONTSIZE,10); tp_line = StringFormat("Buy TP Line %d", i); ObjectCreate(chart_id,tp_line,OBJ_TREND,0,time[m + (LookbackBars / 2)],c_d_hh,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,tp_line,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,tp_line,OBJPROP_COLOR,clrCornflowerBlue);
Вывод:

Пояснение:
В этом разделе определяются сигнал на покупку, точка входа, уровень стоп-лосса (SL) и уровень тейк-профита (TP) для гармонического паттерна, и затем они воссоздаются в виде визуальных объектов на графике. Чтобы обеспечить легкое управление этими элементами и их обозначение на графике, переменные buy_txt, sell_txt, entry_line, tp_line, sl_line, tp_txt и sl_txt хранят уникальные названия для соответствующих объектов. На точке входа расположен объект buy_txt, который является текстовой меткой, которая гласит "BUY", обозначая направление торговли. Торговый сетап четко визуализируется благодаря entry_line, горизонтальной трендовой линии, которая указывает цену входа в open[m + (LookbackBars / 2)].
Аналогичным образом, метка стоп-лосса и соответствующая ей трендовая линия обозначены как sl_txt и sl_line, соответственно, и расположены в точке D, которая является самым низким минимумом паттерна. Стоп-лосс гарантирует, что сделка будет аннулирована, если цена пересечет этот порог. Метка Tp_txt и линия tp_line, расположенные в точке c_d_hh, предыдущем максимуме свинга, указывают уровень фиксации прибыли. Этим графическим элементам присвоен отличительный цвет (clrCornflowerBlue), чтобы вы могли быстро распознавать важные торговые уровни на графике. Эти компоненты способствуют разработке упорядоченной и прозрачной торговой стратегии, основанной на гармоническом паттерне.
3. Построение медвежьих гармонических паттернов
В этой части та логика, которую мы использовали для выявления бычьих гармонических паттернов, будет модифицирована под медвежьи паттерны. Теперь нет необходимости избыточно описывать каждую особенность, поскольку это всего лишь противоположность бычьему паттерну. Максимум свинга теперь будет обозначаться буквой X, минимум свинга – буквой A, следующий максимум свинга – буквой B, следующий минимум свинга – буквой C, а окончательный максимум свинга – буквой D. Это основное отличие. Это гарантирует, что паттерн будет точно отражать медвежий сценарий, указывая на возможное время для продажи.
Точка B будет по-прежнему проверяться с помощью уровней коррекции Фибоначчи, чтобы убедиться, что она находится в пределах соответствующего диапазона относительно отрезка XA. По аналогии с этим, паттерн будет очерчен трендовыми линиями, треугольниками и другими элементами графика, но их расположение будет изменено в соответствии с медвежьей структурой. Точка входа будет установлена на индексе точки D плюс половина LookbackBars, тейк-профит (TP) – на точке C, а стоп-лосс (SL) – на точке D. Справедлива та же логика, поскольку это действительно инверсия бычьего паттерна; следовательно, мы не будем повторно описывать все аргументы подробно.
Пример:
int x_highest_index; // Stores the index of the highest price point (X) in the pattern double x_a_hh; // Holds the price value of the swing high at point X datetime x_a_hh_t; // Stores the timestamp of when the swing high at X occurred int a_lowest_index; // Stores the index of the lowest price point (A) in the pattern double a_b_ll; // Holds the price value of the swing low at point A datetime a_b_ll_t; // Stores the timestamp of when the swing low at A occurred int b_highest_index; // Stores the index of the highest price point (B) in the pattern double b_c_hh; // Holds the price value of the swing high at point B datetime b_c_hh_t; // Stores the timestamp of when the swing high at B occurred int c_lowest_index; // Stores the index of the lowest price point (C) in the pattern double c_d_ll; // Holds the price value of the swing low at point C datetime c_d_ll_t; // Stores the timestamp of when the swing low at C occurred
if(rates_total >= bars_check) { for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingHigh(high, i, LookbackBars)) { X = high[i]; // Assign the highest price at index 'i' to X X_time = time[i]; // Assign the corresponding time value to X_time X_line = StringFormat("XHigh%d", i); // Create a unique string identifier for the X-high trendline X_letter = StringFormat("XB%d", i); // Create a unique string identifier for the X-high label for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingLow(low, j, LookbackBars) && time[j] > X_time) { A = low[j]; // Assign the lowest price at index 'j' to A A_time = time[j]; // Assign the corresponding time value to A_time A_line = StringFormat("ALow%d", j); // Create a unique string identifier for the A-low trendline A_letter = StringFormat("AB%d", j); // Create a unique string identifier for the A-low label for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingHigh(high, k, LookbackBars) && time[k] > A_time) { B = high[k]; // Assign the highest price at index 'k' to B B_time = time[k]; // Assign the corresponding time value to B_time B_line = StringFormat("BHigh%d", k); // Create a unique string identifier for the B-high trendline B_letter = StringFormat("BB%d", k); // Create a unique string identifier for the B-high label for(int l = k; l < rates_total - LookbackBars; l++) { if(IsSwingLow(low, l, LookbackBars) && time[l] > B_time) { C = low[l]; // Assign the lowest price at index 'l' to C C_time = time[l]; // Assign the corresponding time value to C_time C_line = StringFormat("CLow%d", l); // Create a unique string identifier for the C-low trendline C_letter = StringFormat("CB%d", l); // Create a unique string identifier for the C-low label for(int m = l; m < rates_total - (LookbackBars / 2); m++) { if(IsSwingHigh(high, m, LookbackBars / 2) && time[m] > C_time) { D = high[m]; // Assign the highest price at index 'm' to D D_time = time[m]; // Assign the corresponding time value to D_time D_line = StringFormat("DHigh%d", m); // Create a unique string identifier for the D-high trendline D_letter = StringFormat("DB%d", m); // Create a unique string identifier for the D-high label // C - D Segment: Find the lowest low between C and D c_d_bars = Bars(_Symbol, PERIOD_CURRENT, C_time, D_time); // Count the number of bars between C and D c_lowest_index = ArrayMinimum(low, l, c_d_bars); // Find the index of the lowest low in the range c_d_ll = low[c_lowest_index]; // Store the lowest low (C - D lowest point) c_d_ll_t = time[c_lowest_index]; // Store the corresponding time for C - D // B - C Segment: Find the highest high between B and C b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_ll_t); // Count the number of bars between B and C b_highest_index = ArrayMaximum(high, k, b_c_bars); // Find the index of the highest high in the range b_c_hh = high[b_highest_index]; // Store the highest high (B - C highest point) b_c_hh_t = time[b_highest_index]; // Store the corresponding time for B - C // A - B Segment: Find the lowest low between A and B a_b_bars = Bars(_Symbol, PERIOD_CURRENT, A_time, b_c_hh_t); // Count the number of bars between A and B a_lowest_index = ArrayMinimum(low, j, a_b_bars); // Find the index of the lowest low in the range a_b_ll = low[a_lowest_index]; // Store the lowest low (A - B lowest point) a_b_ll_t = time[a_lowest_index]; // Store the corresponding time for A - B // X - A Segment: Find the highest high between X and A x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_ll_t); // Count the number of bars between X and A x_highest_index = ArrayMaximum(high, i, x_a_bars); // Find the index of the highest high in the range x_a_hh = high[x_highest_index]; // Store the highest high (X - A highest point) x_a_hh_t = time[x_highest_index]; // Store the corresponding time for X - A // Fibonacci Retracement Levels: Calculate 61.8% and 78.6% retracement levels from X to A lvl_61_8 = a_b_ll + ((61.8 / 100) * (x_a_hh - a_b_ll)); // 61.8% retracement level lvl_78_6 = a_b_ll + ((78.6 / 100) * (x_a_hh - a_b_ll)); // 78.6% retracement level if((b_c_hh >= lvl_61_8 && b_c_hh <= lvl_78_6) && (D > x_a_hh)) { //D ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D". ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //C ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_ll_t, c_d_ll); // Create text object for C ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C". ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_ll_t,C,time[c_lowest_index+LookbackBars],c_d_ll); // Create line to mark C ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown //B ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_hh_t, b_c_hh); // Create text object for B ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B". ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,time[b_highest_index+LookbackBars],b_c_hh); // Create line to mark B ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue //A ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_ll_t, a_b_ll); // Create text object for A ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A". ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,time[a_lowest_index+LookbackBars],a_b_ll); // Create line to mark A ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green //X ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_hh_t, x_a_hh); // Create text object for X ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X". ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,time[x_highest_index+LookbackBars],x_a_hh); // Create line to mark X XA_line = StringFormat("XA LineB%d", m); // Unique name for the XA line. ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); // Create line to connect XA ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width AB_line = StringFormat("AB LineB%d", m); // Unique name for the AB line. ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); // Create line to connect AB ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width BC_line = StringFormat("BC LineB%d", m); // Unique name for the BC line. ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll); // Create line to connect BC ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width CD_line = StringFormat("CD LineB%d", m); // Unique name for the CD line. ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_ll_t,c_d_ll,D_time,D); // Create line to connect CD ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width //XA FIBO XA_fibo = StringFormat("XA FIB0 B%d", i); ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); // Create XA fibo ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels for(int i = 1; i <= 6; i++) { ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown } fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 B%d", i); fibo_78_6_txt = StringFormat("Fibo 78.6 Text B%d", i); ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_hh_t, lvl_61_8,b_c_hh_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6 ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_hh_t,lvl_78_6); // Create text for level 78.6 ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size // Create and format the first bearish harmonic pattern (XAB) X_A_B = StringFormat("XAB B%d", i); ObjectCreate(chart_id, X_A_B, OBJ_TRIANGLE, 0, x_a_hh_t, x_a_hh, a_b_ll_t, a_b_ll, b_c_hh_t, b_c_hh); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, clrMistyRose); // Set color for the pattern ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); // Fill the triangle shape // Create and format the second bearish harmonic pattern (BCD) B_C_D = StringFormat("BCD B%d", i); ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_hh_t, b_c_hh, c_d_ll_t, c_d_ll, D_time, D); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, clrMistyRose); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); // Create and format the SELL text at the entry position sell_txt = StringFormat("Sell %d", i); ObjectCreate(chart_id, sell_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], open[m + (LookbackBars / 2)]); ObjectSetString(chart_id, sell_txt, OBJPROP_TEXT, "SELL"); ObjectSetInteger(chart_id, sell_txt, OBJPROP_COLOR, clrMagenta); // Set text color ObjectSetInteger(chart_id, sell_txt, OBJPROP_FONTSIZE, 10); // Set font size // Create and format the SELL entry line entry_line = StringFormat("Sell Entry Line %d", i); ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], open[m + (LookbackBars / 2)], c_d_ll_t, open[m + (LookbackBars / 2)]); ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 3); // Set line thickness ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrMagenta); // Set line color // Create and format the Stop Loss (SL) text sl_txt = StringFormat("Sell SL %d", i); ObjectCreate(chart_id, sl_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], D); ObjectSetString(chart_id, sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id, sl_txt, OBJPROP_COLOR, clrMagenta); ObjectSetInteger(chart_id, sl_txt, OBJPROP_FONTSIZE, 10); // Create and format the Stop Loss (SL) line sl_line = StringFormat("Sell SL Line %d", i); ObjectCreate(chart_id, sl_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], D, c_d_ll_t, D); ObjectSetInteger(chart_id, sl_line, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, sl_line, OBJPROP_COLOR, clrMagenta); // Create and format the Take Profit (TP) text tp_txt = StringFormat("Sell TP %d", i); ObjectCreate(chart_id, tp_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], c_d_ll); ObjectSetString(chart_id, tp_txt, OBJPROP_TEXT, "TP"); ObjectSetInteger(chart_id, tp_txt, OBJPROP_COLOR, clrMagenta); ObjectSetInteger(chart_id, tp_txt, OBJPROP_FONTSIZE, 10); // Create and format the Take Profit (TP) line tp_line = StringFormat("Sell TP Line %d", i); ObjectCreate(chart_id, tp_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], c_d_ll, c_d_ll_t, c_d_ll); ObjectSetInteger(chart_id, tp_line, OBJPROP_WIDTH, 3); ObjectSetInteger(chart_id, tp_line, OBJPROP_COLOR, clrMagenta); if(overlap == false) { i = m; } if(overlap == true) { i = i; } break; } break; } } break; } } break; } } break; } } } } }
Вывод:

Пояснение:
Нахождение максимума свинга в точке X, обозначающей начало паттерна, является первым шагом в процедуре обнаружения. Следующими элементами, которые ищет программа, являются точка минимума (A), точка максимума (B), точка минимума (C) и точка максимума (D). Соответствие структуры паттерна правилам обеспечивается проверкой каждой точки в соответствии с ее положением относительно предыдущей. Проверка паттерна в основном зависит от уровней коррекции, особенно уровней Фибоначчи 61,8% и 78,6% относительно отрезка от X до A. Потенциальный сетап на продажу обнаруживается, если эти требования выполнены, и точка D выше точки X.
Мы не будем повторять подробные объяснения, поскольку медвежьи и бычьи гармонические паттерны схожи. Основное различие заключается в том, что бычий вариант этого паттерна предсказывает рост цены, в то время как медвежий паттерн предсказывает ее снижение. Ориентация паттерна и связанное с ней направление торговли являются единственными отличиями. В остальном основные идеи остаются теми же.
Заключение
В этой статье мы рассмотрели, как создать индикатор, отображающий гармонические паттерны, с помощью объектов-чартов MetaTrader 5. Мы рассмотрели логику обнаружения ключевых точек свинга, структурирования паттерна и его проверки с использованием уровней коррекции Фибоначчи. Реализовав как бычьи, так и медвежьи паттерны, мы продемонстрировали, как выявлять потенциальные торговые возможности на основе движения цен. Используя этот подход, вы можете визуализировать гармонические формации непосредственно на графиках, что позволяет принимать более обоснованные решения без опоры на традиционные буферы индикаторов. Этот метод повышает гибкость и обеспечивает надежную основу для дальнейшей настройки и совершенствования торговых стратегий, основанных на паттернах.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17574
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Алгоритм дифференциального поиска — Differential Search Algorithm (DSA)
Моделирование рынка (Часть 16): Сокеты (X)
Знакомство с языком MQL5 (Часть 15): Руководство для начинающих по созданию пользовательских индикаторов (IV)
Нейросети в трейдинге: Пространственно-временная модель состояния для анализа финансовых данных (E-STMFlow)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Большое спасибо за вашу работу над серией MQL5 .
Вау, это лучшее из того, что я читал в ваших статьях. Продолжайте в том же духе.
Вы отлично справляетесь со своими статьями, наилучшие пожелания.
мы можем поставить в известность, когда покажут синал!
потрясающе, вы отлично справляетесь со своими артикулами, наилучшие пожелания
мы можем поставить в известность, когда покажут синал!
Здравствуйте, спасибо за добрые слова. это можно сделать с помощью функции "PlaySound()"