English
preview
Как обнаруживать и нормализовать объекты графика в MQL5 (Часть 2): Сбор и структурирование данных из сложных аналитических объектов

Как обнаруживать и нормализовать объекты графика в MQL5 (Часть 2): Сбор и структурирование данных из сложных аналитических объектов

MetaTrader 5 — Примеры |
16 0
Clemence Benjamin
Clemence Benjamin

Содержание

  1. Введение
  2. Понимание сложных аналитических объектов
  3. Проблемы извлечения данных из сложных объектов
  4. Проверка вил вручную
  5. Шаг 1 — Создание базового детектора (часть 1) — ChartObjectDetector.mqh
  6. Шаг 2 — Создание сборщика данных сложных объектов (ComplexObjectDataCollector.mqh)
  7. Шаг 3 — Создание тестового советника (TestComplexObjectsEA.mq5)
  8. Тестирование и валидация
  9. Прилагаемые файлы и краткое руководство по установке
  10. Заключение


Введение

В первой части этой серии был представлен детектор, который распознает аналитические объекты (линии тренда, горизонтальные линии, прямоугольники, инструменты Фибоначчи) и возвращает их основные временные и ценовые координаты. Однако сложные объекты, такие как уровни коррекции Фибоначчи, каналы и вилы, содержат более обширную информацию: множество уровней, наклонов и геометрических соотношений. Простых пар координат недостаточно для алгоритмических стратегий, основанных на определённых коэффициентах коррекции, границах каналов или касаниях медианной линии.

В данной статье предлагается расширение возможностей детектора для сбора и структурирования данных из сложных аналитических объектов. Вы узнаете, как извлекать массивы уровней Фибоначчи (коэффициенты и соответствующие цены), получать опорные точки канала и анализировать геометрию вил. Данные нормализуются и помещаются в расширенную структуру, которая сохраняет все значимые показатели и позволяет принимать решения на основе правил, например «открыть позицию при коррекции до уровня 0,618» или «подать сигнал, когда цена коснется медианной линии вил».

Реализация включает в себя OBJ_FIBO (уровни коррекции/расширения), OBJ_FIBOTIMES, OBJ_FIBOFAN, OBJ_FIBOARC, OBJ_CHANNEL (равноудаленный канал) и OBJ_PITCHFORK. Тот же принцип можно применять к циклам и инструментам Ганна.

Вы получите два файла:

  1. ComplexObjectDataCollector.mqh (класс, который наследуется от детектора из части 1 и добавляет возможность извлечения сложных данных) и
  2. TestComplexObjectsEA.mq5 (тестовый советник, работающий по таймеру и записывающий в журнал фактические ценовые уровни Фибоначчи, точки канала и геометрию вил с оповещениями в реальном времени).


Понимание сложных аналитических объектов

Основы анализа Фибоначчи

Всё семейство инструментов Фибоначчи восходит к итальянскому математику XIII века Леонардо Боначчи, более известному как Фибоначчи. В своей книге «Liber Abaci» («Книга вычислений»), изданной в 1202 году, Фибоначчи познакомил Европу с индо-арабской системой счисления. Он также сформулировал знаменитую задачу о разведении кроликов, которая привела к появлению последовательности, ныне носящей его имя: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 …

Последовательность определяется простым рекуррентным соотношением:

формулы Фибоначчи

Удивительным свойством, лежащим в основе всего финансового анализа, основанного на последовательности Фибоначчи, является сходимость отношения двух последовательных членов: по мере увеличения n отношение двух последовательных членов приближается к обратному значению золотого сечения (примерно 0,618), а само это отношение приближается к φ (примерно 1,618).

Эта постоянная φ, также известная как «золотое сечение» или «Фи», удовлетворяет алгебраическому уравнению

и

может быть точно выражена как

Обратная ей величина равна φ минус единица, то есть приблизительно 0,618. Эти соотношения определяют ключевые уровни коррекции Фибоначчи: 23,6 % (0,236, полученное в результате деления одного числа на число, стоящее на три позиции правее), 38,2 % (0,382, полученное в результате деления на число, стоящее на две позиции правее), 50 % (не является истинным коэффициентом Фибоначчи, но широко используется как середина диапазона) и 61,8 % (0,618, обратное значение золотого сечения). Уровень 78,6 % соответствует квадратному корню из 0,618. Уровни расширения, такие как 161,8 %, 261,8 % и 423,6 %, получают, применяя эти соотношения за пределами уровня 100 %.

Уровни коррекции Фибоначчи (OBJ_FIBO): математические основы и принципы торговли

Объект «Уровни коррекции Фибоначчи» определяется двумя опорными точками: точкой A (начало значительного ценового движения) и точкой B (конец этого движения). Затем вертикальная разница цен используется для расчета цены на каждом уровне коррекции с коэффициентом r следующим образом:


 

для движения вверх, или

для движения вниз.

Стандартные коэффициенты — это r из множества {0,0; 0,236; 0,382; 0,500; 0,618; 0,786; 1,0; 1,272; 1,618; 2,618; 4,236}. Основная торговая гипотеза, основанная на наблюдениях ранних технических аналитиков, применявших идеи Фибоначчи к финансовым рынкам, заключается в том, что после сильного ценового движения в одном направлении цена часто корректируется (откатывается) на предсказуемую долю этого движения, прежде чем продолжить движение в исходном направлении. Наиболее часто отслеживаемые уровни — 38,2 % и 61,8 %, поскольку они представляют собой наиболее сильное математическое сопротивление, обусловленное соотношениями «золотого сечения». Когда цена отскакивает от этих уровней, трейдеры расценивают это как подтверждение продолжения тренда; однако пробой уровня 61,8 % часто сигнализирует о возможном развороте тренда.

Веера Фибоначчи (OBJ_FIBOFAN): применение коэффициентов к наклонам линий тренда

Веер Фибоначчи расширяет концепцию коррекции, перенося её с горизонтальных линий на наклонные линии тренда. Сначала инструмент проводит вертикальную «невидимую» линию от второй опорной точки. Затем коэффициенты коррекции (38,2 %, 50 %, 61,8 %) умножаются на высоту ценового движения, чтобы определить три точки на этой вертикальной линии. Формула построения: для движения вверх от минимума T до максимума P вертикальное расстояние D = P – T.

Коэффициенты веера r из множества {0,382; 0,5; 0,618} определяют точки на высотах

над точкой T на вертикальной прямой, проходящей через точку P.

Затем линии веера соединяют точку T с каждой из этих точек, проецируя потенциальные уровни поддержки в будущее. Наконец, линии тренда проводятся от первой опорной точки через каждую из этих трёх точек, образуя «веер» линий, расходящихся из исходной точки. Затем эти линии веера используются для оценки скорости и наклона последующих ценовых движений. Математический принцип заключается в том, что после сильного движения последующее коррекционное движение должно соответствовать тем же пропорциональным соотношениям при измерении под углом. Линии веера Фибоначчи могут выступать в качестве уровней поддержки или сопротивления и давать сигналы, аналогичные горизонтальным уровням Фибоначчи. Они также адаптируются к таймфрейму и волатильности рынка.

Дуги Фибоначчи (OBJ_FIBOARC): криволинейные уровни поддержки и сопротивления

Для построения дуг Фибоначчи используются те же коэффициенты коррекции, но вместо прямых линий проводятся дуги окружности. После построения линии тренда между двумя точками экстремума (например, минимумом и максимумом) проводятся три дуги с центром во второй точке экстремума и радиусами, равными расстояниям от центра до точек на линии тренда, соответствующих коэффициентам Фибоначчи (38,2 %, 50 %, 61,8 %). Затем эти дуги пересекают ценовой график в зонах потенциальной поддержки или сопротивления. Смысл интерпретации состоит в том, что при откате после сильного движения цена может встретить эти дуги в качестве динамических уровней поддержки или сопротивления. В отличие от горизонтальных линий коррекции, которые учитывают только цену, дуги учитывают как время, так и ценовое расстояние, благодаря чему они более чувствительны к геометрии графика. Известное ограничение состоит в том, что дуги строятся как части геометрически правильных окружностей. Поэтому их форма и точки пересечения меняются в зависимости от масштаба графика по осям времени и цены.

Временные зоны Фибоначчи (OBJ_FIBOTIMES): временные проекции

Временные зоны Фибоначчи переносят акцент с цены на время. От значимого локального максимума или минимума вправо проводятся вертикальные линии с интервалами, увеличивающимися в соответствии с последовательностью Фибоначчи (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 …)

Последовательность задаётся условиями F₁ = 1, F₂ = 1, а при n ≥ 3 —


Временные зоны предназначены не для точного определения точек разворота, а для выделения периодов, в которые вероятность значительных ценовых изменений повышается. На практике ранние зоны (1, 2, 3, 5) часто расположены слишком близко друг к другу, чтобы приносить пользу, поэтому трейдеры обычно сосредотачиваются на более поздних зонах, начиная с 21 периода.

Равноудаленный канал (OBJ_CHANNEL): основа анализа трендовых каналов

Равноудаленный канал — один из старейших и наиболее интуитивно понятных аналитических инструментов, появившийся ещё на заре графического анализа. Он задаётся тремя опорными точками: первые две (A и B) определяют базовую линию тренда (направление и наклон канала), а третья точка (C) задаёт ширину — расстояние по вертикали от базовой линии до противоположной границы канала. Третья точка обычно соответствует локальному максимуму в восходящем канале или локальному минимуму в нисходящем канале. Линии канала параллельны по построению: после того как базовая линия проведена через точки A и B, через точку C проводится параллельная ей линия.

Математическое условие параллельности заключается в том, что наклоны двух прямых равны:

Равноудаленный канал может быть восходящим (бычьим, с повышающимися максимумами и минимумами) или нисходящим (медвежьим, с понижающимися максимумами и минимумами). Название «равноудаленный» указывает на постоянное расстояние между двумя линиями канала, измеряемое по перпендикуляру. Торговля с использованием каналов основана на предположении, что рынок будет продолжать колебаться между двумя параллельными границами до тех пор, пока не произойдет прорыв. Стандартные правила торговли по каналу предусматривают вход в позицию на нижней границе (при восходящем тренде) или на верхней границе (при нисходящем тренде), размещение стоп-приказов чуть за противоположной линией канала и установку целевых уровней прибыли путем проекции ширины канала.

Вилы Эндрюса (OBJ_PITCHFORK): анализ медианной линии

Метод «вил Эндрюса», также известный как анализ медианной линии, был разработан доктором Аланом Эндрюсом (инженером и экономистом XX века) на основе более ранних концепций экономиста Роджера Бэбсона и картографа Джорджа Марешаля, сформулированных в 1930-х годах. Этот инструмент определяется тремя точками: A (начальная точка разворота, часто представляющая собой значимый локальный максимум или локальный минимум), B (первый противоположный экстремум) и C (следующий экстремум, соответствующий направлению точки A).

Построение выполняется следующим образом: сначала вычисляется середина M отрезка BC:

.

Затем из точки A через точку M проводится медианная линия, которая продлевается в обоих направлениях. Эта линия обозначает основное равновесие канала. На втором этапе через точки B и C проводятся линии, параллельные медианной линии и имеющие тот же наклон. Наклон медианной линии определяется следующим образом:

Эти три линии образуют фигуру в форме вил (отсюда и название), задающую канал возможного будущего движения цены. Основной принцип, выявленный Эндрюсом в результате обширных эмпирических исследований, заключается в том, что цена имеет тенденцию возвращаться к медианной линии примерно в 80 % случаев. Если цена не достигает медианной линии, это может указывать на скорый разворот. Кроме того, Эндрюс и его последователи сформулировали принцип «действие — противодействие», основанный на третьем законе Ньютона: расстояние, на которое цена удаляется от медианной линии с одной стороны, часто соответствует такому же отклонению с противоположной стороны. В третьей части этой серии мы расширим возможности извлечения данных вил, включив линии действия и противодействия, а также методологию решетчатой матрицы, предложенную Патриком Микулой.

Циклы (OBJ_CYCLES): циклическая теория Херста и спектральный анализ

Аналитический объект «Циклы» отражает периодические колебания ценовых данных. Современная концепция анализа циклов на финансовых рынках во многом связана с именем Дж. М. Херста, американского инженера, который в 1970-х годах опубликовал книгу «Магия прибыли: выбор времени для совершения сделок с акциями» и утверждал, что доля успешных сделок при торговле по его теории циклов составляла 90%. Основная идея Херста заключается в том, что все ценовые колебания на финансовых рынках являются совокупным результатом взаимодействия множества циклов, каждый из которых имеет свой собственный период, амплитуду и фазу. Эти циклы находятся в гармонической взаимосвязи: продолжительность доминирующего цикла зачастую примерно в 2, 4 или 8 раз превышает продолжительность более коротких циклов. Это приводит к модели масштабирования F-степеней, в которой период цикла каждой степени вдвое больше периода цикла следующей, более низкой степени.

Математическое представление одного цикла — это синусоидальная волна:

,

где:

  • A — амплитуда,
  • f — частота (величина, обратная периоду),
  • t — время, а φ — фаза.

Метод Херста, известный как фазовый анализ, заключается в разложении цены на составляющие её циклы с использованием методов цифровой обработки сигналов, таких как полосовые фильтры и спектральная оценка. В современной практике коэффициент Херста (H) используется для классификации временного ряда как возвращающегося к среднему значению (H меньше 0,5) или имеющего тренд (H больше 0,5), тогда как дискретное преобразование Фурье (DFT) или спектральный анализ методом максимальной энтропии (MESA) позволяют определить длину доминирующего цикла. Практическое применение анализа циклов в торговле заключается в прогнозировании точек разворота, когда минимумы или максимумы нескольких циклов совпадают по времени, создавая сильный совокупный сигнал. Графический объект OBJ_CYCLES позволяет трейдеру отмечать на графике такие прогнозируемые максимумы и минимумы циклов в будущем. В этом сборщике мы извлекаем только опорные точки объекта «Циклы». Полная декомпозиция цикла и спектральный анализ выходят за рамки данной статьи; мы вернёмся к ним позднее.


Проблемы извлечения данных из сложных объектов

Количество уровней объектов Фибоначчи может меняться — для извлечения данных мы используем OBJPROP_LEVELS и OBJPROP_LEVELVALUE. Каналы и вилы используют три опорные точки, доступ к которым осуществляется по индексу. Для вил дополнительные уровни извлекаются с помощью OBJPROP_LEVELVALUE (смещение) и OBJPROP_LEVELTEXT (метка). Базовый детектор (часть 1) уже считывает две основные опорные точки для трендовых линий, каналов и прямоугольников, но для инструментов Фибоначчи и вил он оставляет их значения равными нулю — детектор сложных объектов добавит недостающую информацию.

Проверка вил вручную

Прежде чем приступить к написанию кода, полезно понять, откуда именно берутся исходные свойства сложного объекта. Возьмём в качестве примера вилы Эндрюса. Вы можете вручную проверить их опорные точки и дополнительные уровни с помощью встроенного диалогового окна «Свойства объекта» в MetaTrader.

Пошаговая инструкция по ручной проверке:

  1. Нарисуйте вилы Эндрюса на любом графике («Вставка» → «Вилы Эндрюса»). Трижды нажмите кнопку мыши и, удерживая её, перетащите курсор, чтобы задать три опорные точки (A, B, C).
  2. Щелкните вилы правой кнопкой мыши и выберите «Свойства…» (или дважды щелкните по объекту). Появится диалоговое окно.
  3. На вкладке «Параметры» в разделе «Координаты» вы увидите точные координаты времени и цены для всех трёх опорных точек:
    • Точка 1 — начало рукоятки (опорная точка A).
    • Точка 2 — первый противоположный экстремум (опорная точка B).
    • Точка 3 — второй экстремум (опорная точка C), который вместе с точкой B определяет медианную линию.
  4. Если вы добавили дополнительные уровни (например, 361,8 %, 461,8 %), перейдите на вкладку «Уровни». Для каждого уровня вы увидите:
    • Значение — для вил это ценовое смещение (а не коэффициент). MetaTrader автоматически преобразует коэффициент, например 0,618, в соответствующее смещение с учётом геометрии вил.
    • Описание — текстовая метка, например «361.8».

На приведённом ниже снимке экрана показано диалоговое окно свойств вил с тремя выделенными опорными точками и открытой вкладкой дополнительных уровней.

Рис. 1 — Свойства инструмента «Вилы Эндрюса»: опорные точки (вкладка «Параметры») и дополнительные уровни (вкладка «Уровни»).

Каждое число, которое вы видите в этом диалоговом окне — время и цена точки 1, точки 2, точки 3, а также значения уровней — точно соответствует тому, что считывают функции MQL5 ObjectGetInteger(), ObjectGetDouble() и ObjectGetString(). Детектор сложных объектов, который мы создадим позже, просто собирает эти же значения программным способом. Проверив объект вручную, вы сможете убедиться, что советник корректно извлекает данные.

Этот же подход применим к любому аналитическому объекту: у инструмента коррекции Фибоначчи коэффициенты отображаются на вкладке «Уровни», у каналов на вкладке «Параметры» отображаются три точки, а у трендовых линий — две. Если понимать, где хранятся исходные данные, гораздо легче доверять тому, что сообщает советник.


Шаг 1 — Создание базового детектора (часть 1) — ChartObjectDetector.mqh

1. Необходимые условия и настройка папок

Прежде чем приступить к написанию кода, создайте следующую папку в каталоге установки MetaTrader 5:

  • MQL5/Include/ChartObjectsAlgorithms/ — в этой папке будут храниться включаемые файлы детекторов.

Сначала создаем файл ChartObjectDetector.mqh в папке MQL5/Include/ChartObjectsAlgorithms/. Это детектор из части 1, который мы будем дорабатывать. Базовый класс хранит идентификатор графика в защищенном поле, чтобы производные классы могли получить к нему доступ.

Заголовок файла и структура SChartObjectInfo

Каждый файл MQL5 должен начинаться с блока комментариев, в котором указываются его название, автор и веб-сайт, а далее следуют директивы #property. Структура SChartObjectInfo содержит основную информацию, общую для всех аналитических объектов: имя, тип, удобочитаемое название типа и две опорные точки (time1/price1 и time2/price2).

//+------------------------------------------------------------------+
//|                                          ChartObjectDetector.mqh |
//|                                 Copyright 2026, Clemence Benjamin|
//|                                               http://www.mql5.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Clemence Benjamin"
#property link      "https://www.mql5.com"
#property strict

//+------------------------------------------------------------------+
//| Structure to hold normalized object data                         |
//+------------------------------------------------------------------+
struct SChartObjectInfo
  {
   string            name;       // object name as seen on the chart
   int               type;       // ENUM_OBJECT constant (e.g., OBJ_TREND)
   string            type_name;  // human-readable type, e.g., "TREND"
   datetime          time1;      // time coordinate of first anchor point
   datetime          time2;      // time coordinate of second anchor point
   double            price1;     // price coordinate of first anchor point
   double            price2;     // price coordinate of second anchor point
  };

2. Преобразование типа объекта в строку

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

//+------------------------------------------------------------------+
//| Convert Object Type to Readable String                           |
//+------------------------------------------------------------------+
string ObjectTypeToString(int type)
  {
   switch(type)
     {
      case OBJ_TREND:        return("TREND");
      case OBJ_RECTANGLE:    return("RECTANGLE");
      case OBJ_CHANNEL:      return("CHANNEL");
      case OBJ_HLINE:        return("HLINE");
      case OBJ_VLINE:        return("VLINE");
      case OBJ_FIBO:         return("FIBO");
      case OBJ_FIBOTIMES:    return("FIBOTIMES");
      case OBJ_FIBOFAN:      return("FIBOFAN");
      case OBJ_FIBOARC:      return("FIBOARC");
      case OBJ_PITCHFORK:    return("PITCHFORK");
      default:               return("UNKNOWN");
     }
  }

3. Объявление базового класса детектора

Класс CChartObjectDetector объявлен с защищённым членом m_chart_id (чтобы к нему могли обращаться производные классы) и открытыми методами: Init() для установки идентификатора графика, Total() для получения количества объектов и Detect() для заполнения массива структур SChartObjectInfo. Метод ExtractProperties() имеет доступ «protected», поскольку его будут вызывать и производные классы.

//+------------------------------------------------------------------+
//| Chart Object Detection Class                                     |
//+------------------------------------------------------------------+
class CChartObjectDetector
  {
protected:
   long              m_chart_id;               // chart ID (accessible to derived classes)
   void              ExtractProperties(string name, int type, SChartObjectInfo &obj);

public:
   void              Init(long chart_id = 0) { m_chart_id = (chart_id == 0) ? ChartID() : chart_id; }
   int               Total()                { return(ObjectsTotal(m_chart_id)); }
   int               Detect(SChartObjectInfo &out_objects[]);
  };

4. Реализация метода Detect

Метод Detect перебирает все объекты на графике, извлекает имя и тип каждого объекта, а затем вызывает ExtractProperties для заполнения основных координат. Обратите внимание, что в базовом классе мы не выполняем фильтрацию по типу объекта — это будет сделано в детекторе сложных объектов. В конце размер массива изменяется так, чтобы он точно соответствовал количеству найденных объектов.

//+------------------------------------------------------------------+
//| Detects all chart objects (base version)                         |
//+------------------------------------------------------------------+
int CChartObjectDetector::Detect(SChartObjectInfo &out_objects[])
  {
   int total = ObjectsTotal(m_chart_id);
   ArrayResize(out_objects, total);
   int count = 0;

   for(int i = 0; i < total; i++)
     {
      string name = ObjectName(m_chart_id, i);
      if(name == "")
         continue;
      if(ObjectFind(m_chart_id, name) < 0)
         continue;

      int type = (int)ObjectGetInteger(m_chart_id, name, OBJPROP_TYPE);
      SChartObjectInfo obj;
      obj.name      = name;
      obj.type      = type;
      obj.type_name = ObjectTypeToString(type);
      obj.time1 = obj.time2 = 0;
      obj.price1 = obj.price2 = 0.0;

      ExtractProperties(name, type, obj);
      out_objects[count++] = obj;
     }

   ArrayResize(out_objects, count);
   return(count);
  }

5. Извлечение двух опорных точек (включая объекты Фибоначчи)

Метод ExtractProperties использует оператор switch для обработки различных типов объектов. Для линий тренда, каналов, прямоугольников и всех объектов Фибоначчи мы считываем две опорные точки по индексам 0 и 1, используя ObjectGetInteger для времени и ObjectGetDouble для цены. Горизонтальные и вертикальные линии обрабатываются отдельно. Это ключевое исправление, которое позволяет рассчитывать цены уровней Фибоначчи.

//+------------------------------------------------------------------+
//| Extracts properties for supported object types                   |
//+------------------------------------------------------------------+
void CChartObjectDetector::ExtractProperties(string name, int type, SChartObjectInfo &obj)
  {
   switch(type)
     {
      case OBJ_TREND:
      case OBJ_CHANNEL:
      case OBJ_RECTANGLE:
      case OBJ_FIBO:
      case OBJ_FIBOTIMES:
      case OBJ_FIBOFAN:
      case OBJ_FIBOARC:
        {
         obj.time1  = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, 0);
         obj.price1 = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, 0);
         obj.time2  = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, 1);
         obj.price2 = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, 1);
         break;
        }
      case OBJ_HLINE:
        {
         obj.price1 = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, 0);
         break;
        }
      case OBJ_VLINE:
        {
         obj.time1 = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, 0);
         break;
        }
      default:
        //--- For other types (Pitchfork, etc.) we keep zeros; derived classes will add data.
        break;
     }
  }

Сохраните файл. На этом реализация базового детектора завершена.


Шаг 2 — Создание сборщика данных сложных объектов (ComplexObjectDataCollector.mqh)

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

1. Подключение и глобальный фильтр аналитических объектов

Сначала мы выполняем подключение базового детектора, используя двойные кавычки, поскольку файл находится в той же папке (относительный путь). Функция IsAnalyticalObject определяет, какие типы объектов мы считаем «аналитическими», то есть какие объекты мы хотим обрабатывать. Эта функция будет использоваться в методе Detect для отсеивания меток, кнопок и других объектов, не связанных с торговлей.

//+------------------------------------------------------------------+
//|                                   ComplexObjectDataCollector.mqh |
//|                                Copyright 2026, Clemence Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Clemence Benjamin"
#property link      "https://www.mql5.com"

#include "ChartObjectDetector.mqh"

//+------------------------------------------------------------------+
//| Helper: Determine if object type is analytical                   |
//+------------------------------------------------------------------+
bool IsAnalyticalObject(int type)
  {
   switch(type)
     {
      case OBJ_FIBO:
      case OBJ_FIBOTIMES:
      case OBJ_FIBOFAN:
      case OBJ_FIBOARC:
      case OBJ_CHANNEL:
      case OBJ_PITCHFORK:
      case OBJ_TREND:
      case OBJ_RECTANGLE:
         return(true);
      default:
         return(false);
     }
  }

2. Расширенная структура SComplexObjectInfo

Эта структура наследуется от SChartObjectInfo (поэтому она автоматически получает поля name, type, type_name, time1/price1, time2/price2). Она добавляет динамические массивы для коэффициентов Фибоначчи и цен, массивы фиксированного размера для трёх точек канала, а также поля для геометрии вил (рукоятка, медианная точка и дополнительные уровни).

//+------------------------------------------------------------------+
//| Extended structure for complex objects                           |
//+------------------------------------------------------------------+
struct SComplexObjectInfo : public SChartObjectInfo
  {
   //--- Fibonacci specific
   double   fibo_ratios[];      // level ratios (0.236, 0.382, etc.)
   double   fibo_prices[];      // actual price at each ratio
   //--- Channel specific (3 anchor points)
   datetime channel_time[3];
   double   channel_price[3];
   //--- Pitchfork specific
   datetime pitchfork_handle_time[2];
   double   pitchfork_handle_price[2];
   datetime pitchfork_median_time;
   double   pitchfork_median_price;
   //--- Pitchfork additional levels (optional)
   double   pitchfork_level_values[];   // offset from median line (price units)
   string   pitchfork_level_texts[];    // description (e.g., "61.8")
  };

3. Объявление класса

Детектор сложных объектов наследуется от базового детектора. Он переопределяет метод Detect, чтобы возвращать массив структур SComplexObjectInfo, и добавляет закрытые методы для извлечения данных сложных объектов.

//+------------------------------------------------------------------+
//| Complex Object Detector Class                                    |
//+------------------------------------------------------------------+
class CComplexObjectDetector : public CChartObjectDetector
  {
public:
   int Detect(SComplexObjectInfo &out_objects[]);

private:
   void ExtractFibonacciLevels(string name, SComplexObjectInfo &obj);
   void ComputeActualFibonacciPrices(SComplexObjectInfo &obj);
   void ExtractChannelPoints(string name, SComplexObjectInfo &obj);
   void ExtractPitchforkData(string name, SComplexObjectInfo &obj);
  };

4. Основной метод Detect детектора сложных объектов

В этом методе сначала вызывается базовый метод ExtractProperties для заполнения данных двух опорных точек. Затем, в зависимости от типа объекта, он вызывает соответствующие функции извлечения данных сложных объектов. Обратите внимание на использование глобальной функции IsAnalyticalObject для отбора только нужных объектов.

//+------------------------------------------------------------------+
//| Detects all complex analytical objects                           |
//+------------------------------------------------------------------+
int CComplexObjectDetector::Detect(SComplexObjectInfo &out_objects[])
  {
   int total = ObjectsTotal(m_chart_id);
   ArrayResize(out_objects, total);
   int count = 0;

   for(int i = 0; i < total; i++)
     {
      string name = ObjectName(m_chart_id, i);
      if(name == "")
         continue;
      if(ObjectFind(m_chart_id, name) < 0)
         continue;

      int type = (int)ObjectGetInteger(m_chart_id, name, OBJPROP_TYPE);
      if(!IsAnalyticalObject(type))
         continue;

      SComplexObjectInfo obj;
      //--- Initialize base fields
      obj.name      = name;
      obj.type      = type;
      obj.type_name = ObjectTypeToString(type);
      obj.time1 = obj.time2 = 0;
      obj.price1 = obj.price2 = 0.0;

      //--- Extract base two points (using protected base method)
      ExtractProperties(name, type, obj);

      //--- Complex extractions
      if(type == OBJ_FIBO || type == OBJ_FIBOTIMES || type == OBJ_FIBOFAN || type == OBJ_FIBOARC)
        {
         ExtractFibonacciLevels(name, obj);
         ComputeActualFibonacciPrices(obj);
        }
      if(type == OBJ_CHANNEL)
         ExtractChannelPoints(name, obj);
      if(type == OBJ_PITCHFORK)
         ExtractPitchforkData(name, obj);

      out_objects[count++] = obj;
     }

   ArrayResize(out_objects, count);
   return(count);
  }

5. Извлечение уровней Фибоначчи и расчёт фактических цен

Метод ExtractFibonacciLevels считывает общее количество уровней с помощью OBJPROP_LEVELS, изменяет размер динамических массивов, а затем перебирает уровни, получая коэффициент каждого из них через OBJPROP_LEVELVALUE. Затем функция ComputeActualFibonacciPrices рассчитывает фактическую цену для каждого уровня, используя цены опорных точек (price1, price2), заполненные базовым детектором, и направление движения (положительная дельта — движение вверх, отрицательная — вниз).

//+------------------------------------------------------------------+
//| Extracts Fibonacci level ratios                                  |
//+------------------------------------------------------------------+
void CComplexObjectDetector::ExtractFibonacciLevels(string name, SComplexObjectInfo &obj)
  {
   int levels = (int)ObjectGetInteger(m_chart_id, name, OBJPROP_LEVELS);
   if(levels <= 0)
      return;
   ArrayResize(obj.fibo_ratios, levels);
   ArrayResize(obj.fibo_prices, levels);
   for(int i = 0; i < levels; i++)
     {
      obj.fibo_ratios[i] = ObjectGetDouble(m_chart_id, name, OBJPROP_LEVELVALUE, i);
      obj.fibo_prices[i] = 0.0;
     }
  }

//+------------------------------------------------------------------+
//| Computes actual prices from ratios and anchor points             |
//+------------------------------------------------------------------+
void CComplexObjectDetector::ComputeActualFibonacciPrices(SComplexObjectInfo &obj)
  {
   double delta = obj.price2 - obj.price1;
   if(delta == 0.0)
      return;
   int size = ArraySize(obj.fibo_ratios);
   for(int i = 0; i < size; i++)
     {
      if(delta > 0)
         obj.fibo_prices[i] = obj.price1 + delta * obj.fibo_ratios[i];
      else
         obj.fibo_prices[i] = obj.price1 - fabs(delta) * obj.fibo_ratios[i];
     }
  }

6. Извлечение точек канала

В равноудаленном канале хранятся три опорные точки: точки с индексами 0 и 1 определяют базовую линию, а точка с индексом 2 — противоположную границу. Мы считываем время и цену для каждого индекса, обращаясь к свойствам OBJPROP_TIME и OBJPROP_PRICE по индексу.

//+------------------------------------------------------------------+
//| Extracts three channel points                                    |
//+------------------------------------------------------------------+
void CComplexObjectDetector::ExtractChannelPoints(string name, SComplexObjectInfo &obj)
  {
   for(int i = 0; i < 3; i++)
     {
      obj.channel_time[i]  = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, i);
      obj.channel_price[i] = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, i);
     }
  }

7. Извлечение данных о вилах (рукоятка, медианная точка, дополнительные уровни)

Три опорные точки вил хранятся следующим образом: начало рукоятки (индекс 0), конец рукоятки (индекс 1) и медианная точка (индекс 2). Мы также считываем все дополнительные уровни, которые пользователь мог добавить (например, линии, соответствующие коэффициентам Фибоначчи). Для каждого дополнительного уровня мы извлекаем значение смещения (в единицах цены относительно медианной линии) и описательный текст (например, «61.8»).

//+------------------------------------------------------------------+
//| Extracts pitchfork handle, median point, and additional levels   |
//+------------------------------------------------------------------+
void CComplexObjectDetector::ExtractPitchforkData(string name, SComplexObjectInfo &obj)
  {
   //--- Handle points (indices 0 and 1)
   for(int i = 0; i < 2; i++)
     {
      obj.pitchfork_handle_time[i]  = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, i);
      obj.pitchfork_handle_price[i] = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, i);
     }
   //--- Median point (index 2)
   obj.pitchfork_median_time  = (datetime)ObjectGetInteger(m_chart_id, name, OBJPROP_TIME, 2);
   obj.pitchfork_median_price = ObjectGetDouble(m_chart_id, name, OBJPROP_PRICE, 2);

   //--- Additional levels (if any)
   int levels = (int)ObjectGetInteger(m_chart_id, name, OBJPROP_LEVELS);
   if(levels > 0)
     {
      ArrayResize(obj.pitchfork_level_values, levels);
      ArrayResize(obj.pitchfork_level_texts, levels);
      for(int i = 0; i < levels; i++)
        {
         obj.pitchfork_level_values[i] = ObjectGetDouble(m_chart_id, name, OBJPROP_LEVELVALUE, i);
         obj.pitchfork_level_texts[i]  = ObjectGetString(m_chart_id, name, OBJPROP_LEVELTEXT, i);
        }
     }
  }

Сохраните файл. Детектор сложных объектов готов.


Шаг 3 — Создание тестового советника (TestComplexObjectsEA.mq5)

Тестовый советник демонстрирует, как использовать детектор сложных объектов. Он выполняет обнаружение каждые 5 секунд и выводит все собранные данные, включая ценовые уровни Фибоначчи, опорные точки каналов, значения медианной линии вил и дополнительные линии уровней. Теперь советник также выводит данные по линиям тренда, прямоугольникам и горизонтальным/вертикальным линиям, что делает его полноценным диагностическим инструментом для всех аналитических объектов.

1. Заголовок файла, подключение и глобальные переменные

Мы подключаем детектор сложных объектов, используя угловые скобки, поскольку файл находится в подпапке ChartObjectsAlgorithms стандартного каталога Include. Затем мы объявляем экземпляр детектора и динамический массив для хранения обнаруженных объектов.

//+------------------------------------------------------------------+
//|                                         TestComplexObjectsEA.mq5 |
//|                                Copyright 2026, Clemence Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Clemence Benjamin"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <ChartObjectsAlgorithms/ComplexObjectDataCollector.mqh>

CComplexObjectDetector detector;
SComplexObjectInfo objects[];

2. OnInit — инициализация детектора

Функция OnInit вызывается при прикреплении советника к графику. Мы инициализируем детектор значением 0 (что означает текущий график). В журнал «Эксперты» выводится сообщение с подтверждением.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   detector.Init(0);   // current chart
   Print("Complex Object Test EA Initialized – detecting all analytical objects");
   return(INIT_SUCCEEDED);
  }

3. Вспомогательные функции — вычисление значения линии

Мы определяем две вспомогательные функции: одну для медианной линии вил (с использованием начала рукоятки и медианной точки), а другую — универсальную, для вычисления текущей цены любой линии, заданной двумя точками.

//+------------------------------------------------------------------+
//| Computes median line value at a given time (pitchfork)           |
//+------------------------------------------------------------------+
double PitchforkMedianValue(datetime t, datetime t0, double p0, datetime t1, double p1)
  {
   if(t1 == t0)
      return(p0);
   double slope = (p1 - p0) / (double)(t1 - t0);
   return(p0 + slope * (t - t0));
  }

//+------------------------------------------------------------------+
//| Computes current price of a line defined by two points           |
//+------------------------------------------------------------------+
double LineValueAtTime(datetime t, datetime t0, double p0, datetime t1, double p1)
  {
   if(t1 == t0)
      return(p0);
   double slope = (p1 - p0) / (double)(t1 - t0);
   return(p0 + slope * (t - t0));
  }

4. OnTick — обнаружение с ограничением частоты и полное ведение журнала

Функция OnTick вызывается при каждом ценовом тике. В этой статье мы ограничиваем частоту обнаружения до одного раза в 5 секунд. Это позволяет сохранить читаемость журнала и избежать перегрузки потока пользовательского интерфейса на этапе разработки. После вызова detector.Detect(objects) мы перебираем элементы возвращённого массива и выводим данные для всех типов объектов: горизонтальных линий, вертикальных линий, прямоугольников, линий тренда, объектов Фибоначчи, каналов и вил.

//+------------------------------------------------------------------+
//| Expert tick function – throttled to 5 seconds                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime last_run = 0;
   if(TimeCurrent() - last_run < 5)
      return;
   last_run = TimeCurrent();

   int total = detector.Detect(objects);
   Print("------ DETECTED COMPLEX OBJECTS: ", total, " ------");

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   datetime now = TimeCurrent();

   for(int i = 0; i < total; i++)
     {
      Print("Object: ", objects[i].name, " | Type: ", objects[i].type_name);

      //--- Horizontal Line
      if(objects[i].type == OBJ_HLINE)
        {
         Print("  Horizontal level: ", objects[i].price1);
         if(MathAbs(bid - objects[i].price1) <= 5 * point)
            Print("    >>> ALERT: Price near horizontal line");
        }

      //--- Vertical Line
      if(objects[i].type == OBJ_VLINE)
        {
         Print("  Vertical line time: ", TimeToString(objects[i].time1));
        }

      //--- Rectangle
      if(objects[i].type == OBJ_RECTANGLE)
        {
         Print("  Rectangle corners:");
         PrintFormat("    Corner 1: %s @ %.5f", TimeToString(objects[i].time1), objects[i].price1);
         PrintFormat("    Corner 2: %s @ %.5f", TimeToString(objects[i].time2), objects[i].price2);
         double range_pips = MathAbs(objects[i].price2 - objects[i].price1) / point;
         PrintFormat("  Vertical range: %.1f pips", range_pips);
        }

      //--- Trendline
      if(objects[i].type == OBJ_TREND)
        {
         Print("  Trendline anchor points:");
         PrintFormat("    Point 1: %s @ %.5f", TimeToString(objects[i].time1), objects[i].price1);
         PrintFormat("    Point 2: %s @ %.5f", TimeToString(objects[i].time2), objects[i].price2);
         double line_now = LineValueAtTime(now, objects[i].time1, objects[i].price1, objects[i].time2, objects[i].price2);
         double distance = (bid - line_now) / point;
         PrintFormat("  Current value: %.5f (distance %.1f pips)", line_now, distance);
         if(MathAbs(distance) <= 5.0)
            Print("    >>> ALERT: Price near trendline");
        }

      //--- Fibonacci
      int fibLevels = ArraySize(objects[i].fibo_prices);
      if(fibLevels > 0)
        {
         Print("  Fibonacci levels (ratio -> price):");
         for(int l = 0; l < fibLevels; l++)
           {
            PrintFormat("    %.3f -> %.5f", objects[i].fibo_ratios[l], objects[i].fibo_prices[l]);
            if(MathAbs(bid - objects[i].fibo_prices[l]) <= 5 * point)
               Print("    >>> ALERT: Price near Fibonacci level ", objects[i].fibo_prices[l]);
           }
        }

      //--- Equidistant Channel
      if(objects[i].type == OBJ_CHANNEL)
        {
         Print("  Channel anchor points:");
         for(int p = 0; p < 3; p++)
            PrintFormat("    Point %d: %s @ %.5f", p, TimeToString(objects[i].channel_time[p]), objects[i].channel_price[p]);

         double t0 = (double)objects[i].channel_time[0];
         double t1 = (double)objects[i].channel_time[1];
         double p0 = objects[i].channel_price[0];
         double p1 = objects[i].channel_price[1];
         if(t1 != t0)
           {
            double slope = (p1 - p0) / (t1 - t0);
            double base_now = p0 + slope * ((double)now - t0);
            PrintFormat("  Current value of base line: %.5f (distance %.1f pips)", base_now, MathAbs(bid - base_now) / point);
           }
        }

      //--- Andrews Pitchfork
      if(objects[i].type == OBJ_PITCHFORK)
        {
         datetime t_handle = objects[i].pitchfork_handle_time[0];
         double p_handle = objects[i].pitchfork_handle_price[0];
         datetime t_median = objects[i].pitchfork_median_time;
         double p_median = objects[i].pitchfork_median_price;
         double median_now = PitchforkMedianValue(now, t_handle, p_handle, t_median, p_median);
         double dist_median = (bid - median_now) / point;
         PrintFormat("  Pitchfork median line at current time: %.5f (distance %.1f pips)", median_now, dist_median);
         if(MathAbs(dist_median) <= 5.0)
            Print("    >>> ALERT: Price within 5 pips of median line");

         int addLevels = ArraySize(objects[i].pitchfork_level_values);
         if(addLevels > 0)
           {
            Print("  Additional pitchfork levels (offset from median):");
            for(int l = 0; l < addLevels; l++)
              {
               double level_price = median_now + objects[i].pitchfork_level_values[l];
               double dist_level = (bid - level_price) / point;
               PrintFormat("    %s : offset %.2f -> line at %.5f (distance %.1f pips)",
                           objects[i].pitchfork_level_texts[l],
                           objects[i].pitchfork_level_values[l],
                           level_price, dist_level);
               if(MathAbs(dist_level) <= 5.0)
                  Print("    >>> ALERT: Price near additional pitchfork level");
              }
           }
        }
     }
  }

Сохраните файл. Скомпилируйте все файлы (F7). Прикрепите советник к графику со сложными объектами и следите за сообщениями на вкладке «Эксперты».


Тестирование и валидация

Тестовая среда — после компиляции прикрепите советник к графику. Нарисуйте как минимум:

  • Коррекцию Фибоначчи (щелчок правой кнопкой мыши → Фибоначчи → Коррекция) с уровнями по умолчанию.
  • Равноудаленный канал (Вставка → Каналы → Равноудаленный канал), задав его тремя щелчками мыши.
  • Вилы Эндрюса (Вставка → Вилы Эндрюса), задав их тремя щелчками мыши. При желании добавьте дополнительные уровни: щелкните правой кнопкой мыши по вилам → Свойства → Уровни → Добавить → Значение = 0.62, Описание = «61.8%».
  • Линию тренда, прямоугольник, горизонтальную линию и вертикальную линию — для проверки всех типов объектов.

Проверка результатов — советник выведет в журнал:

  • Для горизонтальных линий: уровень цены.
  • Для вертикальных линий: временная координата.
  • Для прямоугольников: два противоположных угла и вертикальный диапазон в пунктах.
  • Для линий тренда: две опорные точки, значение линии в текущий момент времени и расстояние в пунктах.
  • Для объектов Фибоначчи: каждый коэффициент Фибоначчи и рассчитанная цена.
  • Для каналов: три опорные точки и текущее значение базовой линии.
  • Для вил: значение медианной линии, дополнительные уровни (если имеются).

Журнал успешного запуска (пример по паре USDJPY, таймфрейм M5 — 28 мая 2026 года) демонстрирует правильное извлечение данных для всех типов объектов:

2026.05.28 09:21:16.638  TestComplexObjectsEA (USDJPY,M5)  Object: M5 Trendline 4675 | Type: TREND
2026.05.28 09:21:16.638    Trendline anchor points:
2026.05.28 09:21:16.639      Point 1: 2026.05.29 10:05 @ 159.39158
2026.05.28 09:21:16.639      Point 2: 2026.05.29 13:50 @ 159.19819
2026.05.28 09:21:16.639    Current value: 155.81926 (distance 3594.7 pips)
2026.05.28 09:21:22.305  ------ DETECTED COMPLEX OBJECTS: 3 ------
2026.05.28 09:21:22.305  Object: M5 Andrews Pitchfork 64573 | Type: PITCHFORK
2026.05.28 09:21:22.305    Pitchfork median line at current time: 159.48209 (distance -66.1 pips)
2026.05.28 09:21:22.305    Additional pitchfork levels (offset from median):
2026.05.28 09:21:22.305      61.8 : offset 0.62 -> line at 160.10009 (distance -684.1 pips)
2026.05.28 09:21:22.305      100.0 : offset 1.00 -> line at 160.48209 (distance -1066.1 pips)
2026.05.28 09:21:22.305      161.8 : offset 1.62 -> line at 161.10009 (distance -1684.1 pips)
2026.05.28 09:21:22.305      261.8 : offset 2.62 -> line at 162.10009 (distance -2684.1 pips)
2026.05.28 09:21:22.305  Object: M5 Fibo 54872 | Type: FIBO
2026.05.28 09:21:22.305    Fibonacci levels (ratio -> price):
2026.05.28 09:21:22.305      0.000 -> 159.35200
2026.05.28 09:21:22.305      0.236 -> 159.27780
2026.05.28 09:21:22.305      0.382 -> 159.23190
2026.05.28 09:21:22.305      0.500 -> 159.19480
2026.05.28 09:21:22.305      0.618 -> 159.15770
2026.05.28 09:21:22.305      1.000 -> 159.03761
2026.05.28 09:21:22.305      1.618 -> 158.84331
2026.05.28 09:21:22.305      2.618 -> 158.52892
2026.05.28 09:21:22.305      4.236 -> 158.02023


Прилагаемые файлы и краткое руководство по установке

Имя файла Тип Версия Описание
ChartObjectDetector.mqh
Включаемый файл
1.00 Базовый детектор для объектов графика (линии тренда, горизонтальные линии, прямоугольники и т. д.)
ComplexObjectDataCollector.mqh
Включаемый файл
1.00 Расширенный детектор для уровней Фибоначчи, каналов и вил
TestComplexObjectsEA.mq5
Советник
1.00 Тестовый советник, который каждые 5 секунд записывает в журнал все обнаруженные аналитические объекты

Порядок установки:

  1. Откройте MetaEditor (F4).
  2. Создайте каждый файл, выбрав «Файл» → «Создать» → «Файл» (.mqh для включаемых файлов, .mq5 для советников), и вставьте соответствующий код в точности так, как показано.
  3. Нажмите клавишу F7, чтобы скомпилировать каждый файл.
  4. Прикрепите TestComplexObjectsEA к любому графику, содержащему сложные объекты.
  5. Просматривайте результаты на вкладке «Эксперты» («Вид» → «Тестер стратегий» / «Эксперты»).


Заключение

Вы расширили возможности детектора из части 1, добавив поддержку многоуровневых объектов Фибоначчи, каналов и вил. Извлеченные данные включают коэффициенты, фактические ценовые уровни, опорные точки канала, рукоятку вил, медианную точку и смещения дополнительных уровней. Все данные хранятся в структуре SComplexObjectInfo и готовы к использованию в алгоритмической торговле.

Теперь ваш советник может автоматически реагировать на уровни коррекции Фибоначчи (вход на уровне 0,618, цель — 1,272), прорывы каналов (покупка, когда цена закрывается выше верхней границы) и касания медианной линии вил (продажа, когда цена касается медианной линии сверху). Тестовый советник содержит конкретный код для расчета расстояний и формирования оповещений.

В этой статье мы ограничиваем частоту обнаружения до одного раза в 5 секунд. Это позволяет сохранить читаемость журнала и избежать перегрузки потока пользовательского интерфейса на этапе разработки. В системах, используемых в рабочей среде, можно легко перенести вызов detector.Detect() в OnChartEvent() (для обработки событий CHARTEVENT_OBJECT_DRAG и CHARTEVENT_OBJECT_CREATE), чтобы структура обновлялась только при изменении объекта пользователем, а OnTick() выполнялась на каждом тике для проверки цен с минимальной задержкой.

В текущей реализации данные хранятся в массиве структур, содержащих динамические массивы (fibo_ratios[], pitchfork_level_values[]). Для высокочастотных конфигураций или конфигураций с несколькими графиками используйте коллекцию на основе указателей. Оберните SComplexObjectInfo в класс, унаследованный от CObject, и храните экземпляры в CArrayObj. Это устраняет копирование данных и улучшает локальность доступа к памяти. В третьей части этой серии будет продемонстрирована эта архитектура, ориентированная на обработку событий и оптимизированная с точки зрения использования памяти.

В третьей части мы заменим опрос обнаружением на основе событий с помощью OnChartEvent (создание, удаление и изменение объектов) и добавим функции для программного создания и изменения аналитических объектов, что позволит советнику самостоятельно рисовать линии тренда, каналы и вилы в зависимости от рыночной ситуации.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22563

Прикрепленные файлы |
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Изолирующий лес средствами MQL5: Обнаружение аномалий качества исполнения заявок Изолирующий лес средствами MQL5: Обнаружение аномалий качества исполнения заявок
Пошаговое руководство по созданию изолирующего леса (Isolation Forest), реализованного непосредственно на MQL5 и ориентированного на показатели исполнения, а не на цену. В руководстве подробно описаны пять признаков, построение деревьев и расчет оценки аномальности по длине пути, обучение на скользящем окне, ведение CSV-журнала, а также сохранение и восстановление модели с использованием FILE_COMMON. Все это интегрировано в OnTradeTransaction(). Полученный механизм защитной остановки торговли (circuit breaker) в реальном времени выявляет необычные исполнения заявок и применяет контролируемые меры реагирования, чтобы стабилизировать реальную торговлю при изменении условий исполнения.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Автоматизация классических рыночных методов в MQL5 (Часть 4): Трендовый шаблон Марка Минервини Автоматизация классических рыночных методов в MQL5 (Часть 4): Трендовый шаблон Марка Минервини
В данной статье представлен TrendTemplateEA — советник, реализующий трендовый шаблон Марка Минервини из восьми условий для дневных графиков Forex. Он проверяет все условия на каждом баре и входит в рынок только при их одновременном выполнении, используя условие превышения индексом относительной силы (RSI) уровня 50 вместо рейтинга относительной силы (RS) фондового рынка. Сигнал входа — пробой максимума за 20 баров на фоне растущего объема; все правила реализованы в коде MQL5 и могут быть протестированы.