Знакомство с языком MQL5 (Часть 16): Создание советников с использованием паттернов технического анализа
Введение
И снова приветствуем вас в Части 16 серии "Знакомство с языком MQL5 (Часть 16)"! Эта часть обещает быть особенно интересной, поскольку мы продолжаем работать со всем тем, что мы узнали до сих пор, используя проектный подход, как обычно. Мы с вами поработаем над практическим проектом, который объединяет в себе паттерны технического анализа и написание кода на языке MQL5 для создания советника, что поможет вам углубить свои навыки через практический опыт.
Мы сосредоточимся на фигуре "Голова и плечи" — одном из самых популярных технических паттернов, позволяющих определить возможный разворот тренда. Наш проект будет реализован в виде советника, который сможет автоматически распознавать эту формацию и совершать сделки на её основе. Кроме того, он будет выполнять роль индикатора, визуально выделяя фигуру "Голова и плечи" на графике, что поможет вам быстрее замечать и понимать её в реальных торговых условиях.
В этой статье вы узнаете:
- Как автоматизировать торговлю по графическим паттернам
- Как правильно распознавать фигуру "Голова и плечи"
- Как программно находить и строить свинг-точки
- Как работать с графическими объектами в MQL5
- Как определять точку входа, стоп-лосс и тейк-профит
-
Как избежать повторяющихся сигналов
1. Что такое графические паттерны
Графическими паттернами называются визуальные формы на ценовых графиках, которые можно использовать для прогнозирования будущих движений рынка. Эти тенденции, которые часто являются индикаторами того, будет ли тренд продолжаться или разворачиваться, являются продуктом непрерывной борьбы между покупателями и продавцами. Паттерн "голова и плечи", к примеру, обычно сигнализирует о возможном развороте от восходящего тренда к нисходящему, в то время как паттерн "флаг" во время восходящего тренда часто подразумевает, что тренд, вероятно, продолжится. Ваша способность выявлять потенциальные торговые возможности на основе прошлого поведения цен улучшится, если вы сможете выявлять такие графические паттерны, как треугольники, прямоугольники и двойные вершины.
Аналогия
Подобно следам на песке, графические паттерны являются очевидными подсказками, оставленными постоянным конфликтом между покупателями и продавцами. Графические паттерны помогают вам понять, что произошло на рынке, и предсказать, что может произойти дальше, так же как тропы животных могут помочь опытному следопыту определить, какое животное прошло здесь, и куда оно направляется. Такие графические паттерны на ценовых графиках показывают, как участники рынка коллективно ведут себя и часто указывают на то, будет ли текущий тренд продолжаться или развернется.
1.1. Категории паттернов
Графические паттерны обычно делятся на три основные категории:
1.1.1. Паттерны разворота
Графические паттерны, известные как паттерны разворота, могут быть использованы для выявления возможных точек разворота на рынке. Они указывают на возможное начало нового тренда в противоположном направлении и на возможное завершение существующего тренда. Например, паттерн разворота может указывать на то, что цена готовится перейти в восходящий тренд, если рынок находится в состоянии снижения. Аналогичным образом, эти паттерны могут указывать на потенциальный разворот растущего рынка. Паттерн "голова и плечи" и паттерн "двойное дно" являются типичными паттернами разворота.
Аналогия
Паттерны разворота выступают в качестве индикаторов возможного изменения направления рынка. Эти паттерны указывают на то, что тренд, будь то восходящий или нисходящий, может ослабевать и быть готовым к развороту, подобно тому как поворачивающие следы на пляже сигнализируют о том, что кто-то изменил направление. Они помогают трейдерам выявлять потенциальные изменения в движении цены, указывая, когда покупатели или продавцы начинают набирать силу.

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

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

2. Настройка проекта
2.1. Как работает советник
Советник в этом проекте создан для автоматического выявления паттерна "голова и плечи" на рынке и исполнения сделок на основе этой структуры. Будь то стандартный паттерн "голова и плечи" или "перевернутые голова и плечи", советник выявит действительный паттерн, выберет наилучшую точку входа и исполнит сделку в ожидаемом направлении пробоя.
Но советник на этом не останавливается. Чтобы сделать паттерн более видимым и понятным на графике, мы также будем использовать графические объекты для обозначения левого плеча, головы и правого плеча. Эти визуальные маркеры помогают трейдеру подтвердить паттерн и повышают ясность при просмотре графика или тестировании стратегии на исторических данных. Этот метод предлагает видимый этап подтверждения в дополнение к автоматизации и распознаванию паттернов, что может улучшить отладку, обучение и даже мониторинг в реальном времени.
2.1.1. Логика для покупки
Чтобы инициировать покупку, советник будет выявлять паттерн "перевернутая голова и плечи", обнаруживая шесть особых точек свинга, расположенных последовательно:
- Максимум свинга (X) – начальный максимум; отмечается буквой X.
- Минимум свинга (A) – более низкий минимум после X; отмечается буквой A.
- Максимум свинга (B) – отскок, формирующий максимум, который ниже X, но выше A; отмечается буквой B.
- Минимум свинга (C) – минимум, который ниже точки A; отмечается буквой C (это "голова").
- Максимум свинга (D) – максимум свинга, находящийся примерно на том же уровне, что и точка B; отмечается буквой D (это часть "линии шеи").
- Минимум свинга (E) – более высокий минимум по сравнению с точкой C и примерно на том же уровне, что и точка A (это второе "плечо").
Как только эта структура будет определена:
- Советник дождется, когда свеча закроется выше точки D (линии шеи).
- Когда это произойдет, советник исполняет покупку.
- Стоп-лосс (SL) будет установлен на минимум в точке E.
- Тейк-профит (TP) будет изначально установлен в точке X (первый максимум свинга).
Однако если расстояние между точкой входа и TP (X) меньше 1x расстояния до стоп-лосса, советник проигнорирует X в качестве цели и вместо этого установит фиксированную цель по соотношению риска и вознаграждения 1:3 относительно расстояния до SL. Это обеспечивает поддержание стратегией минимального соотношения риска и вознаграждения и позволяет избежать заключения сделок с низким вознаграждением, которые не стоят риска.

2.1.1. Логика для продажи
Чтобы инициировать продажу, советник будет выявлять стандартный паттерн "голова и плечи", обнаруживая шесть особых точек свинга, расположенных последовательно:
- Минимум свинга (X) – начальный минимум; отмечается буквой X.
- Максимум свинга (A) – более высокий максимум после X; отмечается буквой A.
- Минимум свинга (B) – отскок, формирующий минимум, который выше X, но ниже A; отмечается буквой B.
- Максимум свинга (C) – максимум, который выше точки A; отмечается буквой C (это "голова").
- Минимум свинга (D) – минимум свинга, находящийся примерно на том же уровне, что и точка B; отмечается буквой D (это часть "линии шеи").
- Максимум свинга (E) – более низкий максимум по сравнению с точкой C и примерно на том же уровне, что и точка A (это второе "плечо").
Как только эта структура будет подтверждена:
- Советник дождется, когда свеча закроется ниже точки D (линии шеи).
- Когда это произойдет, советник исполняет продажу.
- Стоп-лосс (SL) будет установлен на максимум в точке E.
- Тейк-профит (TP) будет изначально установлен в точке X (первый минимум свинга).
Если расстояние между точкой входа и TP (X) меньше расстояния до стоп-лосса, советник проигнорирует X в качестве цели и вместо этого установит фиксированную цель по соотношению риска и вознаграждения 1:3 относительно расстояния до SL.

Примечание: Основная цель торговой стратегии, которая будет исследована в этом проекте, заключается в развитии ваших знаний о языке MQL5, особенно о том, как работать с графическими паттернами и создавать полезные советники. Данная стратегия не предназначена для использования с реальными деньгами или для торговли в реальном времени. Прежде чем внедрять любую стратегию на реальном рынке, всегда проводите всестороннее тестирование на исторических данных и проконсультируйтесь со своим финансовым советником.
3. Выявление паттернов "голова и плечи" на графике
На этом этапе я предполагаю, что вы хорошо понимаете идею графических паттернов и точно знаете, что мы хотим от нашего советника. Даже до того, как советник совершит сделку, критически важно увидеть паттерн "голова и плечи" на графике. Это поможет вам выявить любые ошибки исполнения или проблемы в логике на ранних стадиях тестирования, а также убедиться, что логика советника соответствует реальному ценовому действию.
В этой главе будет рассмотрено, как вручную выделить и проверить структуру "голова и плечи" на графике, используя различные графические компоненты, включая трендовые линии, текстовые метки и фигуры. Позже это поможет создать прочную основу для автоматизации обнаружения в нашем советнике.
3.1. Получение данных о свечах
Первый шаг – получить данные свечей для нахождения паттерна "голова и плечи" на графике. Они содержит подробную информацию о каждом баре: ценах открытия, максимума, минимума, закрытия и времени. Эти значения имеют решающее значение, поскольку то, как цена колеблется в течение нескольких свечей, определяет структуру паттерна Однако мы собираем эту информацию не только для того, чтобы выявлять тренды. Мы также хотим, чтобы советник работал как индикатор, поскольку мы хотим создать советник, который сможет распознавать эти паттерны и самостоятельно совершать сделки. Это подразумевает, что он должен уметь обращать внимание на предыдущие паттерны "голова и плечи" на графике, чтобы вы могли повторно их изучать и тестировать.
Говоря кратко, советник, который мы разрабатываем, будет иметь две функции: он будет торговым ботом, который распознает паттерны "голова и плечи" и автоматически исполняет сделки, а также будет служить индикатором паттернов, выделяя аналогичные структуры в исторических данных. В дополнение к возможности автоматической торговли, эта двойная функциональность позволяет трейдерам визуально проверять сигналы и исследовать, как паттерн развивался и показывал себя в прошлом.
Пример:input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points // Variable to store how many bars are available on the chart for the selected timeframe int rates_total; double open[]; // Array for opening prices double close[]; // Array for closing prices double low[]; // Array for lowest prices double high[]; // Array for highest prices datetime time[]; // Array for time (timestamps) of each bar //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); }
Пояснение:
В начале функции есть две входные переменные, определяемые пользователем. Первая переменная timeframe позволяет пользователю выбрать точный период времени, который будет использовать советник. PERIOD_CURRENT является настройкой по умолчанию. Таким образом, советник будет использовать тот же таймфрейм, что и график, к которому он привязан. Благодаря этой гибкости вы можете исследовать несколько временных периодов, не изменяя код. Советник получает указание от второго параметра bars_check о том, сколько исторических свечей (или баров) учитывать при анализе поведения цены. Советник будет искать возможные структуры паттернов на последних 1000 свечах, поскольку в нашем случае имеет значение 1000.
Код объявляет несколько массивов и переменную для хранения рыночных данных после определения входных данных. Общее количество баров (свечей), доступных для выбранного символа и таймфрейма, будет храниться в переменной rates_total. Ценовые данные каждой свечи хранятся в массивах: open[], close[], low[], и high[]. Мы также можем определить точное время каждой свечи, используя временные метки для каждого бара, которые хранятся в массиве time[]. Поскольку они предоставляют советнику информацию, необходимую для анализа графика и поиска паттернов, таких как "голова и плечи", эти массивы имеют решающее значение.
Этот проект требует от нас вручную воспроизводить данные свечей с помощью таких функций, как CopyOpen(), CopyHigh() и других, потому что он разработан как советник (EA), а не как пользовательский индикатор. Эта информация уже была бы предоставлена автоматически через параметры функции, если бы это был индикатор, который использует метод OnCalculate(), избавляя нас от необходимости копировать эту информацию.
Три общие функции языка MQL5 – OnInit(), OnDeinit() и OnTick() – также включены в структуру советника. Когда советник загружается, метод OnInit() вызывается один раз. Он просто возвращает INIT_SUCCEEDED, сигнализируя о том, что советник готов к исполнению. Функция OnDeinit(), которая еще не имеет логики очистки, вызывается, когда терминал закрывается или советник удаляется.
Функция OnTick(), которая выполняется каждый раз, когда происходит обновление цены (тик), является местом, где происходит фактическая активность. Функция Bars() изначально определяет количество баров, которые в настоящее время доступны на графике внутри этой функции. Данные о цене и времени затем загружаются в соответствующие массивы с помощью пяти функций копирования: Цены открытия заполняют массив open[] с помощью функции CopyOpen(), цены закрытия заполняют массив close[] с помощью функции CopyClose(), минимальные и максимальные цены сохраняются с помощью функций CopyLow() и CopyHigh(), соответственно, а временные метки каждой свечи сохраняются с помощью CopyTime(). Эта настройка необходима, поскольку она подготавливает все исторические данные, которые советник будет использовать для поиска графических паттернов и анализа рыночной активности.
3.2. Выявление свингов
Нахождение важных точек свинга X, A, B, C, D и E, которые составляют структуру паттерна "голова и плечи" или "перевернутая голова и плечи", происходит после того, как мы успешно собрали исторические данные по свечам. Советник должен точно выявлять эти колебания, чтобы определять действительные паттерны на графике, которые являются точками разворота в движении цены. Советник поможет определить паттерн шаг за шагом, выявляя основные точки разворота, исследуя максимумы и минимумы за выбранное количество баров.
Пример://+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; // If the current high is not the highest, return false. } return true; }
Пояснение:
Мы используем две функции, IsSwingLow() и IsSwingHigh(), чтобы определить точки свинга цены. Эти функции определяют, является ли данная свеча по отношению к своим ближайшим свечам максимумом свинга или минимумом свинга. В соответствии со значением интервала проверки, функция гарантирует, что минимум текущей свечи будет ниже минимумов заранее определенного количества свечей до и после нее в случае минимума свинга. Аналогичным образом, для максимума свинга проверяется, что максимум текущей свечи больше максимумов окружающих свечей. Поскольку эта идея ранее была подробно рассмотрена в Части 14 этой серии, мы не будем углубляться в детали здесь.
3.2.1. Выявление XABCDE
В эссе подчеркивается важность точного определения основных точек свинга – X, A, B, C и D – для распознавания паттерна "голова и плечи" как для покупки, так и для продажи. Эти точки обозначают значимые максимумы и минимумы, которые влияют на паттерн и руководят торговыми действиями советника.
Пример:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. break; } } break; } } break; } } break; } } break; } } } } } } } }Пояснение:
//X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). 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_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_letter; // Unique name for the text label object. //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object.
В этом коде используются три разных типа переменных для представления каждой из основных точек свинга паттерна, которые обозначаются как X, A, B, C, D и E. Точные ценовые уровни этих точек свинга сначала сохраняются в переменных типа double. Переменная double A хранит цену максимума свинга с меткой A, в то время как переменная double X хранит цену минимума свинга с меткой X. Эта настройка необходима, каждая точка свинга использует переменную даты и времени для записи времени, когда точка свинга наступила. Это позволяет советнику точно располагать точки свинга в хронологическом порядке на графике. Переменная datetime X_time фиксирует время минимума свинга X, а переменная datetime A_time фиксирует время максимума свинга A.
Наконец, для каждой точки свинга генерируется отличительное имя с помощью строковой переменной. Чтобы визуально указать местоположение каждой точки свинга, эти метки – такие как X_letter или A_letter – используются для создания текстовых объектов на графике. Советник способен лучше организовывать и отображать эти точки, чтобы трейдеры могли заметить развивающийся паттерн благодаря этой системе меток. Цена, время и метка – это три элемента информации, которые советник использует для описания каждой точки свинга, чтобы иметь возможность корректно выявлять и отображать паттерн "голова и плечи" на графике. Эта техника обеспечивает выявление паттернов и размещение интуитивно понятных визуальных подсказок для трейдеров.
if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); break; } } break; } } break; } } break; } } break; } } } } } } }
Вывод:

Пояснение:
Чтобы определить, настроена ли программа на обнаружение паттернов продаж, код сначала определяет, имеет ли переменная show_sell значение true. Когда обнаружение паттерна продажи не требуется, этот простое условие предотвращает бессмысленные вычисления. Затем советник проверяет, что общее количество баров на графике (rates_total) больше или равно минимальному порогу bars_check. Это гарантирует, что имеется достаточно исторических данных для точного распознавания паттернов.
Переменная z, которая обычно используется как размер окна или интервал проверки для обнаружения точек свинга, используется для перебора ограниченного диапазона значений во внешнем цикле. Данный прием направлен на улучшение гибкости и точности обнаружения свинга путем экспериментов с несколькими интервалами проверки, работая в диапазоне от z = 7 до z = 10. Этот цикл позволяет алгоритму искать паттерны при немного разных значениях разрешения или чувствительности.
Проходя итерации от rates_total - bars_check до rates_total - z, следующий цикл for проходит по самым последним барам на графике. Указанный диапазон сосредоточен на месте, в котором возможные паттерны более актуальны для текущего ценового действия, ограничивая область поиска самыми последними барами. Функция IsSwingLow(low, i, z) используется в этом цикле для поиска минимума свинга и определения, является ли бар на позиции i локальным минимумом на основе окна проверки z.
Когда минимум свинга обнаруживается на позиции i, код использует функцию StringFormat("X%d", i) для генерации уникальной метки, называемой X_letter, сохраняет соответствующую цену в переменную X, а временную метку – в переменную X_time. Благодаря этим меткам советнику будет проще визуально выделить это место на графике и присвоить ему уникальный идентификатор для дальнейшей обработки. Паттерн "голова и плечи" начинается с этой точки X в качестве точки отсчета.
Затем, чтобы определить следующий максимум свинга A, который наступает по времени после точки X, код начинает другой вложенный цикл, начинающийся с i. Чтобы соблюдать хронологическую последовательность, требуемую для паттерна, он использует IsSwingHigh(high, j, z), чтобы определить, является ли бар с индексом j максимумом свинга, и проверяет, что его время time[j] строго больше X_time. Цена, время и метка для точки A документируются аналогично точке X, если обнаружен действительный максимум свинга.
Следующие точки – B, C, D и E – последовательно определяются этой структурой вложенных циклов. Проходя по барам, советник находит каждую точку свинга и проверяет, что она является действительным минимумом или максимумом свинга, и что ее временная метка строго больше времени предыдущей точки. Код создает отличительную метку и сохраняет цену и продолжительность для каждого подтвержденного свинга. Благодаря этой строгой последовательной проверке точки паттерна сохраняются в надлежащей последовательности.
Цикл, вложенный глубже всех, использует несколько более узкое окно проверки (z/2), чтобы найти точку E, последний максимум свинга в паттерне. Благодаря этому отличию алгоритм допускает регулирование чувствительности обнаружения для последней точки. Когда точка E найдена, код мгновенно выходит из цикла, чтобы избежать ненужных дополнительных поисков, что повышает эффективность. Затем назначаются цена, время и метка точки E.
Паттерн "голова и плечи" точно определяется в правильном порядке точек свинга в рамках этого процесса благодаря вложенным циклам и проверкам времени. Использование отличительных меток, таких как "X%d" или "A%d", позволяет советнику генерировать текстовые или графические элементы на графике и управлять ими, тем самым предоставляя трейдерам визуальное представление паттерна. Благодаря этой системной, структурированной методике компьютер способен надежно выявлять сложные паттерны ценового действия для использования в торговых решениях.
Цель вышеприведенного примера кода заключалась в выявлении шести ключевых точек свинга – X, A, B, C, D и E – которые необходимы для выявления паттерна "голова и плечи". Тем не менее, текущая реализация пока не накладывает точных логических ограничений, необходимых для проверки структуры "голова и плечи"; вместо этого, она лишь определяет эти точки свинга на основе колебаний цены и их хронологического порядка. Алгоритм собирает цены, время и метки этих точек, но не проверяет, действительно ли они представляют собой отчетливый паттерн.
Для проверки действительности паттерна "голова и плечи" будут использованы несколько важных критериев, основанных на структуре цены. Точка A должна быть выше точки X, точка B должна находиться между ними (выше X, но ниже A), а точка C (голова) должна быть выше точки B. Пик правого плеча формируется точкой E, выровненной с точкой A, в то время как впадина правого плеча представлена точкой D, находящейся рядом вблизи уровня точки B. Более того, перед отправкой любых торговых сигналов структура отдельно взятого паттерна должна быть проверена путем наблюдения за определенной последовательностью максимумов и минимумов между этими точками.
Пример:
input color txt_clr = clrBlue; // Texts color //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_letter; // Unique name for the text label object. int x_a_bars; int x_lowest_index; double x_a_ll; datetime x_a_ll_t; //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_letter; // Unique name for the text label object. int a_b_bars; int a_highest_index; double a_b_hh; datetime a_b_hh_t; string A_zone; double A_low; //B double B; // Price of the swing low (B). datetime B_time; // Time of the swing low (B). string B_letter; // Unique name for the text label object. int b_c_bars; int b_lowest_index; double b_c_ll; datetime b_c_ll_t; string B_zone; double B_high; //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. int c_d_bars; int c_highest_index; double c_d_hh; datetime c_d_hh_t; //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. int d_e_bars; int d_lowest_index; double d_e_ll; datetime d_e_ll_t; double D_3bar_high; //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object. double E_3bar_low; string xa; // Unique name for the trendline for XA. string ab; // Unique name for the trendline for AB. string bc; // Unique name for the trendline for BC. string cd; // Unique name for the trendline for CD. string de; // Unique name for the trendline for DE. string ex; // Unique name for the trendline for EX. //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); } break; } } break; } } break; } } break; } } break; } } } } } } }
Вывод:

Пояснение:
Код содержит объявления переменных, которые используются для хранения важной информации о структуре и компонентах пользовательского паттерна XABCDE в ценовом действии. Каждая переменная играет определенную роль при выявлении и визуализации свинговых максимумов и минимумов, составляющих паттерн, а также при размещении меток и отрисовке структуры графика. Первая группа переменных относится к сегменту от точки X до точки A. Переменная x_a_bars хранит количество баров между этими двумя точками. Переменная x_lowest_index хранит индекс бара с минимальной ценой в этом сегменте, а переменная x_a_ll хранит фактическую наименьшую цену ("более низкий минимум"), в то время как переменная x_a_ll_t хранит соответствующее время, когда этот более низкий минимум наступил.
Участок от A до B обрабатывается следующей группой переменных. Количество баров в этом разделе представлено переменной a_b_bars. Индекс максимального бара определяется переменной a_highest_index, в то время как фактический максимум (или "более высокий максимум"), хранится в переменной a_b_hh, а переменная a_b_hh_t фиксирует время, когда его наступления. Это поддерживает идею о том, что точка B является более высоким минимумом, а точка A – максимумом свинга. Переменные b_c_bars, b_lowest_index, b_c_ll и b_c_ll_t затем сохраняют количество баров, индекс минимума, цену минимума и время этой цены, соответственно, для сегмента от B до C. Это служит для подтверждения последующего более низкого минимума структуры.
Строковые переменные A_zone и B_zone, вероятно, будут использоваться для хранения отличительных имен объектов для создания прямоугольных зон рядом с точками A и B. Ценовые уровни A_low и B_high, которые служат визуальными подсказками или областями принятия решений на графике, обозначают нижние и верхние границы этих зон. Аналогично предыдущим переменным, d_e_bars, d_lowest_index, d_e_ll и d_e_ll_t хранят количество баров, индекс, цену и временные данные для отрезка от D до E, что помогает в нахождении последнего отрезка паттерна.
D_3bar_high и E_3bar_low используются для хранения более высокого максимума вблизи точки D и более низкого минимума вблизи точки E, которые обычно рассчитываются на основе трехбарных структур. Это помогает проверить подлинность точек свинга и уменьшить количество ложных срабатываний. Наконец, строковые идентификаторы для трендовых линий, которые будут созданы между каждой из точек разворота паттерна – от X до A, от A до B и так далее – представлены переменными xa, ab, bc, cd, de и ex. Эти строки гарантируют, что каждый объект линии тренда будет иметь отличительное имя, что позволит точно и гармонично отображать весь паттерн на графике.
При изучении движения цены между шестью ключевыми точками, которые обозначены буквами X, A, B, C, D и E, предполагается, что эти точки соответствуют максимумам и минимумам свинга, которые образуют определенные структурные взаимосвязи. Нахождение наименьшего минимума (d_e_ll) между точками D и E является первым шагом в скрипте. Затем он получает время и эталонный максимум (D_3bar_high), который находится на три бара раньше этого минимума. Аналогично, он находит более высокий максимум (c_d_hh) между точками C и этой точкой d_e_ll. Затем он собирает все соответствующие максимумы и минимумы свинга вместе с их соответствующими временными метками и повторяет процесс в обратном направлении до точки X. Таким образом, код использует анализ баров для построения всей свинговой структуры X-A-B-C-D-E.
Между двумя временными метками подсчитывается количество свечей с помощью функции Bars(). Функции ArrayMaximum() и ArrayMinimum() помогают разделить определенные диапазоны между точками свинга так, чтобы код мог проанализировать максимумы и минимумы внутри этих диапазонов. Чтобы найти наибольший максимум или наименьший минимум, эти функции сканируют определенное количество баров из заданного смещения (i, j, k и т.д.). Это помогает в нахождении точек свинга. Функция ArrayMaximum(high, l, c_d_bars), например, находит наибольший максимум между C и D, в результате чего определяется точка C. Скрипт проходит с той же процедурой по всей структуре, достигая точки X.
Структурная связь между точками проверяется путем проверки нескольких условий. Эти критерии сравнивают максимумы и минимумы сегментов; например, определяется, находится ли точка A выше точки X, находится ли точка B ниже точки A, но все еще выше точки X, находится ли точка C выше точки A, и так далее. Перед построением графика ценового действия это позволяет подтвердить, действительно ли оно формирует ожидаемый паттерн. Условия E > A_low и E_3bar_low < a_b_hh являются примерами сравнений, которые гарантируют, что отрезок до точки E находится в правильном положении относительно остальной части структуры.
Как только паттерн удовлетворяет всем требованиям, метод использует функцию ObjectCreate() для графического обозначения распознанных точек на графике с помощью текстовых меток ("X", "A", "B" и т.д.) и трендовых линий, которые их соединяют. Эти трендовые линии используют объекты OBJ_TREND для рисования отрезков паттерна XA, AB, BC, CD, DE и EX, а также объекты OBJ_TEXT для текстового обозначения точек. Читаемость улучшается за счет цветов, ширины линий и визуального наслоения (через OBJPROP_BACK).
Наконец, код выделяет зону A и зону B – две ключевые ценовые зоны – с помощью прямоугольников. Зона A простирается от максимума в точке A до минимума в точке E, в то время как зона B простирается от минимума в точке B до максимума в точке D. При принятии торговых решений, таких как выставление точки входа, стопов или целей, эти прямоугольники, вероятно, будут использоваться в качестве визуального эталона для выявления реакций или конвергенции внутри определенных областей. Трейдеры смогут легче понимать сложные паттерны, которые программа автоматически находит благодаря этому отображению.
3.2.2. Выделение структуры паттерна с помощью фигур треугольника
Рисование треугольных фигур для выделения структуры паттерна, особенно когда появляются такие формации, как "голова и плечи", происходит после того, как мы нашли и пометили все важные точки свинга, которые обозначаются как X, A, B, C, D и E. Эти треугольники будут указывать на пики и впадины, которые являются "головой" и "плечами" в каждом паттерне, вместо того, чтобы следовать полным ценовым отрезкам. Паттерн "голова и плечи" с линией, не опускающейся ниже линии шеи, визуально представлен треугольником XAB, который соединяет X, A и B.
Треугольник BCD, который соединяет точки B, C и D и подчеркивает вторую волну структуры, является следующим шагом в этом методе. Треугольник DEX, который визуально завершает фигуру, соединяет точки D, E и X. Выступая в качестве визуальных помощников, эти треугольные фигуры позволяют трейдерам выявлять ключевые точки разворота и геометрию паттерна быстрее, не загромождая график линиями.
Пример:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points input bool show_sell = true; // Display sell signals input bool show_buy = true; // Display buy signals input color txt_clr = clrBlue; // Texts color input color head_clr = clrCornflowerBlue; // Head color input color shoulder_clr = clrLightSeaGreen; // Shoulder color
if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); 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_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); }
Вывод:

Пояснение:
Этот раздел кода сосредоточен на использовании треугольников и прямоугольников для отображения структур паттернов, в частности XAB, BCD и DEX, на графике MetaTrader 5. Чтобы сохранить динамические имена для объектов-треугольников, объявлены три строковые переменные – X_A_B, B_C_D и D_E_X. Специфические временные координаты извлекаются с использованием двух переменных типа datetime (xa_line_t и ex_line_t), чтобы закрепить части треугольников в соответствующих местах на графике.
Чтобы улучшить понимание трейдера, код начинается с установления двух прямоугольных областей, называемых "A ZONE" и "B ZONE". Эти прямоугольники, которые подчеркивают значимые области ценовой структуры, создаются с помощью функции ObjectCreate(). Зона A простирается до точки E, от максимума между точками A и B (a_b_hh) до минимума в точке A (A_low). Аналогичным образом, зона B простирается от позиции минимума (b_c_ll) между точками B и C до максимума (B_high), завершаясь во временной координате отрезка от D до E.
В следующем разделе вызывается функция ObjectGetTimeByValue(), чтобы определить временные координаты необходимые для закрепления частей треугольников XAB и DEX. Эта функция обеспечивает соответствие визуальных маркеров ценовым уровням, которые эти маркеры должны подсвечивать, путем поиска определенного значения цены на протяжении объекта и последующего возврата соответствующего времени.
Затем каждый треугольник отображается как отдельный отрезок паттерна. Хотя он не соединяет отрезок полностью, треугольник XAB указывает на первую свинговую структуру. Чтобы предотвратить визуальный беспорядок и сосредоточиться на структуре, он привлекает внимание только к важным разворотным точкам, которые напоминают плечи. Аналогично этому, треугольник BCD, который соединяет минимум (B), пик (C) и коррекцию (D), подчеркивает голову паттерна. И наконец, завершает структуру треугольник DEX, который изображает разворотный свинг, отражающий первоначальное плечо.
Такие свойства, как OBJPROP_FILL и OBJPROP_BACK, используются для цветовой заливки всех трех треугольников и их размещения позади других элементов графика. Трейдеры смогут легче распознавать паттерн с первого взгляда благодаря используемым цветам, которые хранятся в таких переменных, как shoulder_clr и head_clr. Чтобы обрабатывать и распознавать множественные экземпляры таких паттернов на одном графике, индекс i включен в наименование каждого элемента.
3.2.3. Указание точки входа, стоп-лосса и тейк-профита
Следующий этап заключается в том, чтобы указать параметры торговли, которые включают точку входа, стоп-лосс (SL) и тейк-профит (TP), после того как структуры XAB, BCD и DEX будут отмечены треугольниками, подчеркивающими форму головы и плеч. Эти компоненты необходимы для преобразования распознанного паттерна в комплексную торговую стратегию. Когда свеча закрывается ниже точки D, осуществляется вход. Это демонстрирует, что цена могла отскочить от линии шеи и движется в предсказанном паттерном направлении. Сделка теперь рассматривается как действительная, и возможно исполнение.
Прямо над точкой E устанавливается уровень стоп-лосса. Установка стоп-лосса (SL) в этой точке помогает защитить сделку, если паттерн не сработает, и цена неожиданно развернется, поскольку точка E является самым последним максимумом свинга перед завершением паттерна. Кроме того, такой стоп-лосс поддерживает разумность сетапа, аннулируя сделку только в случае нарушения паттерна. Поскольку точка X, начало паттерна, является надежным эталонным уровнем, где цена ранее разворачивалась, изначально тейк-профит устанавливается там. При этом TP растягивается дальше, чтобы достичь соотношение риска и вознаграждения минимум 1:2, если расстояние от точки входа до точки X не дает соотношение риска и вознаграждения (RRR) как минимум 1:1. Ключевым элементом любой устойчивой торговой стратегии является обеспечение того, чтобы сделка имела благоприятный потенциал доходности в соотношении с суммой, подверженной риску.
Пример:int n_bars; int n_bars_2; string sl_t; string tp_t; double sl_price; double tp_price;
for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 2)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } } } break; } }
Вывод:

Пояснение:
Логика в этой области кода сосредоточена на определении действительного сетапа для торговли с помощью структуры "голова и плечи", которая была ранее выделена. Затем используются графические объекты для графического обозначения точки входа, стоп-лосса (SL) и тейк-профита (TP) на графике. Количество свечей между значимыми точками рассчитывается и сравнивается с помощью двух целочисленных переменных – n_bars и n_bars_2. Это помогает определить, находится ли текущее ценовое действие все еще в пределах приемлемого окна сетапа. В то время как переменные sl_price и tp_price хранят свои соответствующие уровни цен, строки sl_t и tp_t используются для динамического именования текстовых объектов SL и TP.
Начиная с точки завершения паттерна (n), задействуется основная логика в цикле, который проходит по барам. Свеча, которая закрывается ниже уровня шеи (точки D) и указывает на возможный вход, проверяется условием (close[o] < d_e_ll && time[o] >= time[n+(z/2)]). Затем, если это требование выполнено, определяется количество баров между точками X и E (n_bars) и между серединной точкой паттерна и текущей свечой (n_bars_2). Таким образом, сделка гарантированно будет действительна в пределах разумного интервала.
Затем код сравнивает цену входа с точками E и X, соответственно, чтобы определить суммы возможного стоп-лосса (sl_zone) и тейк-профита (tp_zone). Чтобы подтвердить эту точку входа как первичный пробой, дополнительно выполняется цикл, чтобы убедиться, что ни одна свеча, предшествующая текущей свече, не закрылась ниже точки D. Скрипт продолжается, если эта проверка будет пройдена успешно.
Для визуального отображения зон стоп-лосса и тейк-профита на графике, код использует OBJ_RECTANGLE для создания графических прямоугольников, если потенциальная прибыль (TP) больше или равна риску (SL). Кроме того, рядом с точкой входа располагается стрелка продажи (OBJ_ARROW_SELL). Переменные sl_price и tp_price содержат фактические значения цен SL и TP. Метки создаются с помощью OBJ_TEXT, чтобы ярко отображать эти уровни на графике, используя выбранные цвета и шрифты. Если соотношение риска и вознаграждения меньше 1:1 (т.е. расстояние до TP меньше, чем до SL), чтобы обеспечить более благоприятное соотношение риска и вознаграждения (не менее 1:2), скрипт корректирует уровни, вычисляя новую зону TP, которая в три раза больше расстояния до SL. Затем для этой альтернативной конфигурации создаются и модифицируются идентичные графические объекты.
4. Исполнение сделок на основе паттерна
Наша цель в этой части заключается в чем-то большем, нежели просто распознать и показать торговые ситуации. Этот раздел посвящен фактическому выполнению сделки на основе выявленного паттерна, в то время как предыдущая часть сосредоточилась на размещении уровней входа, стоп-лосса (SL) и тейк-профита (TP) на графике. Советник должен автоматически инициировать сделку, когда требования выполнены, например, когда цена закрывается ниже точки D для сетапа на продажу. Тейк-профит должен быть установлен на уровне точки X или изменен для достижения соотношения риска и вознаграждение не менее 1:2, а стоп-лосс должен быть установлен на уровне точки E. Переходя к этому шагу, советник преобразуется из визуального инструмента в полностью автоматизированную систему, которая может входить в сделки и управлять ими без необходимости участия человека.
Пример:
#include <Trade/Trade.mqh> CTrade trade; int MagicNumber = 5122025; datetime lastTradeBarTime = 0; double ask_price; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
ask_price = ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); datetime currentBarTime = iTime(_Symbol, timeframe, 0); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); 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_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 3)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(time[o] == time[rates_total-2] && currentBarTime != lastTradeBarTime) { trade.Sell(lot_size,_Symbol,ask_price, sl_price,tp_price); lastTradeBarTime = currentBarTime; } } } break; } } } break; } } break; } } break; } } break; } } break; } } } } } } }
Вывод:

Пояснение:
Этот блок кода управляет выполнением сделок с помощью встроенного в язык MQL5 класса CTrade, который упрощает торговые задачи, включая размещение, редактирование и закрытие ордеров. Торговая библиотека, которая предоставляет доступ к классу CTrade, включается в начале кода директивой #include <Trade/Trade.mqh>. Затем создается экземпляр этого класса в строчке CTrade trade;, а переменная int MagicNumber = 5122025; используется для присвоения уникальных идентификаторов сделкам, которые выполняет тот советник. Поскольку переменная MagicNumber помогает отличать сделки, совершенные этим советником, от сделок, совершенных вручную или другими советниками, она имеет решающее значение. Объект trade используется для установки его MagicNumber; SetExpertMagicNumber;.
Вызов SymbolInfoDouble(_Symbol, SYMBOL_ASK); используется для инициализации переменной ask_price текущей ценой Ask символа. Ордер на продажу будет исполняться по этой цене. Затем код использует проверку time[o] == time[rates_total-2], чтобы определить, является ли текущая свеча самой последней. Кроме того, условие currentBarTime!= lastTradeBarTime используется для подтверждения того, что на текущем баре еще не была совершена сделка. Благодаря этой проверке советник не может разместить более одного ордера на одном баре.
Если выполнены оба требования, советник осуществляет транзакцию выставления ордера на продажу. Функция Sell(). Ask_price – это цена входа, sl_price – уровень стоп-лосса, tp_price – уровень тейк-профита, lot_size – количество лотов для торговли, а _Symbol – это текущий торговый символ (например, EURUSD). Советник добавляет значение currentBarTime к переменной lastTradeBarTime после успешного размещения сделки. Это обновление имеет решающее значение, поскольку оно гарантирует, что по каждому действительному паттерну совершается только одна сделка, предотвращая выполнение советником многочисленных транзакций на основе одной и той же свечи.
Важно помнить, что те же соображения, которые используются для выявления и отображения сетапа на продажу, могут быть равным образом использованы для выявления и отображения сетапа на покупку, просто если выполнять обратные действия на каждом этапе. Стратегия покупки будет ждать, когда свеча закроется выше линии шеи (точки D), а не ниже нее. После этого тейк-профит будет установлен на максимуме (X), а стоп-лосс на минимуме (E). Соображения о соотношении риска и вознаграждения, визуальные подсказки и условия остаются теми же; они просто инвертируются под бычью структуру. Исходный код, который будет включен в публикацию, полностью реализует эту логику для всех, кто захочет дополнительно изучить или изменить ее.
Заключение
В этой статье мы рассмотрели, как создать советник (EA) на языке MQL5, который выявляет паттерны технического анализа (в частности, паттерн "голова и плечи") и торгует на их основе. Мы начали с обнаружения и маркировки важных точек свинга, затем использовали такие графические инструменты, как треугольники и прямоугольники, чтобы визуально представить структуры XAB, BCD и DEX. В продолжение этого, мы задали логику определения конкретных параметров сделки, включая точку входа, уровень стоп-лосса и уровень тейк-профита, при этом также обеспечив соответствие соотношения риска и вознаграждения разумным стандартам. И наконец, мы реализовали логику исполнения сделок, которая размещает реальные ордера на основе подтвержденных сигналов, сделав советник полностью автоматизированным.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18147
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Пространственно-временная модель состояния для анализа финансовых данных (Окончание)
Нейросети в трейдинге: Пространственно-временная модель состояния для анализа финансовых данных (STSSM-блок)
Быстрая интеграция большой языковой модели и MetaTrader 5 (Часть II): Файнтьюн на реальных данных, бэктест и онлайн-торговля модели
Знакомство с языком MQL5 (Часть 15): Руководство для начинающих по созданию пользовательских индикаторов (IV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Спасибо, именно поэтому я всегда жду ваших статей. Очень толковые!
Вы великолепны, Израиль! Я регулярно перевожу ваши статьи на испанский официально, и мне всегда очень приятно работать над ними.
Продолжайте фантастическую работу! ❤️
Вы великолепны, Израиль! Я регулярно перевожу ваши статьи на испанский официально, и мне всегда очень приятно работать над ними.
Продолжайте фантастическую работу! ❤️
Здравствуйте, Мигель.
Спасибо за теплые слова, они много значат для меня ❤️
Я не программист, поэтому понимание этого заняло некоторое время, но то, как это было разбито на части, и пошаговый процесс дали мне мотивацию. 💯
Привет, Дэниелс.
Рад слышать это от тебя! ❤️