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

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

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

Содержание:

  1. Введение
  2. Реализация
  3. Тестирование
  4. Вложения
  5. Заключение



Введение

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

Цена ручного мониторинга

Представим себе трейдера, который проводит линию тренда, соединяющую локальные минимумы на паре EURUSD. Каждый раз, когда цена приближается к этой линии, трейдер ожидает отскока или пробоя. Точно так же горизонтальная линия сопротивления требует постоянного внимания: касание, после которого цена отскакивает, может сигнализировать о возможности открытия короткой позиции, тогда как уверенное закрытие выше нее может подтвердить пробой. Прямоугольник определяет диапазон; поведение цены на его верхней или нижней границе зачастую определяет дальнейшее движение. При ручной торговле пропуск быстрого разворота или внезапного ценового всплеска из-за отвлечения внимания, усталости или просто взгляда в сторону означает упущенные возможности или неожиданные убытки. Многие трейдеры сталкивались с ситуацией, когда цена идеально касалась линии тренда, после чего начинался рост на 50 пунктов, но они открывали позицию слишком поздно, поскольку не получили автоматического оповещения. Автоматическое обнаружение этих объектов позволило бы мгновенно уведомлять трейдера или даже совершать сделки на основе заранее заданных правил. Без систематизированного подхода к сбору данных об объектах программным способом каждую такую возможность приходится отслеживать вручную, что не является ни масштабируемым, ни надёжным.

Проблема масштабирования

Проблема усугубляется, когда трейдер отслеживает сразу несколько инструментов или таймфреймов. На одном графике могут быть десятки аналитических объектов — линии тренда, построенные по разным ценовым движениям, горизонтальные уровни прошлой поддержки/сопротивления, а также прямоугольники, обозначающие фазы консолидации. Просматривать их все вручную, одновременно реагируя на движение цены в реальном времени, для человека просто невозможно. Алгоритмический подход, позволяющий систематически собирать координаты и свойства каждого объекта, решает эту проблему масштабируемости, превращая хаотичную визуальную картину в структурированный набор данных, готовый к автоматизированному анализу.

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

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

Концептуальная схема

Рис. 1. Концептуальная схема обработки аналитических объектов.

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

К концу этой статьи у вас будут два готовых к использованию файла: ChartObjectDetector.mqh (класс, инкапсулирующий логику обнаружения) и ObjectDetectionTestEA.mq5 (тестовый советник, который каждые несколько секунд выводит в журнал все обнаруженные объекты). Вы можете подключить этот детектор к любому советнику (EA), чтобы получить доступ ко всем нарисованным аналитическим объектам в виде структурированных данных, что позволит реализовывать автоматизированные стратегии, реагирующие на линии тренда, уровни поддержки/сопротивления и зоны Фибоначчи.


Реализация

Обзор объектной модели MQL5 (CChartObject, низкоуровневые функции)

MQL5 предоставляет два API для объектов графика: низкоуровневый функциональный интерфейс (ObjectGetInteger, ObjectGetDouble, ObjectGetString, ObjectsTotal, ObjectName) и иерархию классов CChartObject. Для механизма обнаружения низкоуровневые функции обладают рядом преимуществ: они работают без создания экземпляров объектов-обёрток, обеспечивают прямой индексированный доступ к свойствам с несколькими точками (например, OBJPROP_TIME с индексами 0 и 1) и не создают накладных расходов, связанных с виртуальными методами. Мы воспользуемся низкоуровневым подходом для создания лёгкого детектора без зависимостей, который можно подключить к любому советнику (EA) или скрипту без необходимости подключать всю библиотеку CChartObject. Основные функции:

  • ObjectsTotal() — возвращает общее количество объектов на графике, при необходимости отфильтрованных по подокну. Мы передаём идентификатор графика и используем параметры по умолчанию для подсчёта объектов во всех окнах.
  • ObjectName() — возвращает имя объекта по его индексу, начиная с нуля. Это основной способ пройти по всем объектам, не зная их имён заранее.
  • ObjectGetInteger() / ObjectGetDouble() — чтение целочисленных свойств и свойств типа double. Индексированная перегрузка (с четвёртым параметром) имеет решающее значение для объектов, имеющих несколько точек, таких как линии тренда (две опорные точки) или инструменты Фибоначчи (две опорные точки плюс массивы уровней).

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

// Basic iteration over all chart objects
int total = ObjectsTotal(chart_id);
for(int i = 0; i < total; i++)
  {
   string name = ObjectName(chart_id, i);
   int obj_type = (int)ObjectGetInteger(chart_id, name, OBJPROP_TYPE);
   // ... extract further properties
  }

Проектирование механизма обнаружения (фильтрация, итерация, извлечение)

Механизм обнаружения следует трёхэтапному конвейеру: фильтрация (оставить только типы аналитических объектов), извлечение (считать координаты времени и цены с использованием логики, специфичной для каждого типа) и сохранение (нормализовать в единую структуру). Эта нормализация имеет решающее значение, поскольку позволяет единому потребителю — советнику (EA) или скрипту — одинаково обрабатывать все типы объектов без написания отдельной логики для линий тренда, горизонтальных линий, прямоугольников или инструментов Фибоначчи. Мы инкапсулируем этот конвейер в классе CChartObjectDetector с единственным открытым методом Detect(), который заполняет массив структур SChartObjectInfo. Класс содержит закрытое поле m_chart_id, позволяющее указать конкретный график (значение 0 означает текущий график). Метод Init() устанавливает этот идентификатор; если аргумент не передан, по умолчанию используется значение ChartID(). Такая архитектура позволяет повторно использовать один и тот же экземпляр детектора на нескольких графиках или разных таймфреймах без повторной компиляции. Ниже приведены объявление класса и метод Init(), каждый из которых сопровождается необходимым декоративным заголовком.

//+------------------------------------------------------------------+
//| Chart Object Detection Class                                     |
//+------------------------------------------------------------------+
class CChartObjectDetector
  {
private:
   long               m_chart_id;
   void               ExtractProperties(string name, int type, SChartObjectInfo &obj);
   bool               IsAnalyticalObject(int type);

public:
//+------------------------------------------------------------------+
//| Initializes the detector with a chart ID (0 = current chart)     |
//+------------------------------------------------------------------+
   void               Init(long chart_id = 0)
                       {
                        //--- If chart_id is 0, use the current chart's ID; otherwise use the provided ID.
                        m_chart_id = (chart_id == 0) ? ChartID() : chart_id;
                       }
//+------------------------------------------------------------------+
//| Detects all analytical objects and returns them in an array      |
//+------------------------------------------------------------------+
   int                Detect(SChartObjectInfo &out_objects[]);
  };

Создание переиспользуемого дескриптора объекта (SChartObjectInfo)

Линия тренда имеет две опорные точки: (время1, цена1) и (время2, цена2). Горизонтальная линия содержит только цену. Вертикальная линия содержит только время. У инструмента коррекции Фибоначчи есть две опорные точки (часто задаваемые нулевыми) и массив уровней коррекции. Чтобы избавить вызывающий советник от необходимости писать условную логику для каждого типа объекта, мы определяем унифицированную структуру SChartObjectInfo, которая всегда содержит поля time1, price1, time2 и price2. Для объектов, у которых отсутствует вторая точка, мы присваиваем неиспользуемым полям значение нуль. Кроме того, мы сохраняем имя объекта (строку), его числовой тип (ENUM_OBJECT) и понятное для трейдера название типа с помощью функции преобразования ObjectTypeToString(). Эта структура представляет собой контракт между детектором и любым использующим его советником (EA): она приводит разнородные геометрические параметры объектов к единому формату данных. Ниже приведены определение структуры и функция преобразования вместе с необходимыми заголовками.

//+------------------------------------------------------------------+
//| Structure to hold normalized object data                         |
//+------------------------------------------------------------------+
struct SChartObjectInfo
  {
   string            name;
   int               type;
   string            type_name;
   datetime          time1;
   datetime          time2;
   double            price1;
   double            price2;
  };

//+------------------------------------------------------------------+
//| Converts MQL5 object type constant to a 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");
      default:          return("UNKNOWN");
     }
  }

Работа с различными типами объектов: линии тренда, горизонтальные линии, прямоугольники, коррекция Фибоначчи

Логика извлечения является наиболее важной частью детектора. Мы реализуем эту логику внутри закрытого метода ExtractProperties(), который принимает имя объекта, его тип и ссылку на структуру SChartObjectInfo. В методе используется оператор switch по значению ENUM_OBJECT. Линии тренда, каналы и прямоугольники используют две опорные точки. Мы считываем их с помощью функций ObjectGetInteger(..., OBJPROP_TIME, index) и ObjectGetDouble(..., OBJPROP_PRICE, index) для индексов 0 и 1. Такой индексированный доступ необходим, поскольку старые имена свойств, такие как OBJPROP_TIME1, отсутствуют в MQL5 и приводят к ошибкам компиляции.

Для горизонтальных линий мы считываем только OBJPROP_PRICE по индексу 0. Для вертикальных линий — только OBJPROP_TIME с индексом 0. Для объектов Фибоначчи мы также считываем две опорные точки (хотя они часто равны нулю), чтобы сохранить согласованность; сами уровни коррекции будут извлечены в части 2 с помощью OBJPROP_FIRSTLEVEL и OBJPROP_LEVELVALUE. Данный метод явно обнуляет все поля координат перед их заполнением, что гарантирует отсутствие устаревших данных от предыдущих вызовов. Ниже приводится полное определение класса со всеми методами.

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

//+------------------------------------------------------------------+
//| Chart Object Detection Class                                     |
//+------------------------------------------------------------------+
class CChartObjectDetector
  {
private:
   long              m_chart_id;

public:
//+------------------------------------------------------------------+
//| Initializes the detector with a chart ID                         |
//+------------------------------------------------------------------+
   void              Init(long chart_id = 0)
     {
      m_chart_id = chart_id;
     }

//+------------------------------------------------------------------+
//| Returns the total number of objects on the chart                 |
//+------------------------------------------------------------------+
   int               Total()
     {
      return(ObjectsTotal(m_chart_id));
     }

//+------------------------------------------------------------------+
//| Detects all analytical chart objects and returns them in an array|
//+------------------------------------------------------------------+
   int               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;
         //--- Safety: ensure object still exists
         if(ObjectFind(m_chart_id, name) < 0) continue;
         int type = (int)ObjectGetInteger(m_chart_id, name, OBJPROP_TYPE);
         if(!IsAnalyticalObject(type)) continue;

         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);
     }

private:
//+------------------------------------------------------------------+
//| Extracts time/price properties based on object type              |
//+------------------------------------------------------------------+
   void              ExtractProperties(string name, int type, SChartObjectInfo &obj)
     {
      obj.time1 = obj.time2 = 0;
      obj.price1 = obj.price2 = 0.0;

      switch(type)
        {
         case OBJ_TREND:
         case OBJ_CHANNEL:
         case OBJ_RECTANGLE:
           {
            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;
           }
         //--- Fibonacci objects: anchor points may be zero; levels in Part 2
         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;
           }
         default:
           break;
        }
     }

//+------------------------------------------------------------------+
//| Filters only analytical object types (trendline, rectangle, etc.)|
//+------------------------------------------------------------------+
   bool              IsAnalyticalObject(int type)
     {
      return(type == OBJ_TREND  || type == OBJ_RECTANGLE ||
               type == OBJ_CHANNEL || type == OBJ_HLINE   ||
               type == OBJ_VLINE   || type == OBJ_FIBO);
     }
  };
//+------------------------------------------------------------------+

Тестовый советник: интеграция и периодическое обнаружение

Тестовый советник (ObjectDetectionTestEA.mq5) демонстрирует, как использовать детектор в условиях реальной торговли. Мы разбиваем его на четыре логические части, каждая из которых имеет собственный оформленный заголовок функции, как того требует руководство по стилю MQL5. Советник использует статическую метку времени, чтобы ограничить частоту обнаружения до одного раза в 5 секунд — это предотвращает переполнение журнала и при этом обеспечивает обновления практически в реальном времени. Вызов detector.Detect() заполняет массив объектов, а PrintFormat() выводит имя каждого объекта, его тип и ценовые координаты. Обратите внимание, что советник не выполняет никаких торговых действий; его единственная цель — проверить, правильно ли детектор распознает вручную нанесенные объекты графика и извлекает их свойства.

Шаг 1 — Подключение и глобальные экземпляры

Советник подключает заголовочный файл детектора и объявляет две глобальные переменные: экземпляр класса CChartObjectDetector и динамический массив структур SChartObjectInfo. Метод Detect() автоматически изменит размер массива.

//+-----------------------------------------------------------------------+
//|                                             ObjectDetectionTestEA.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/ChartObjectDetector.mqh>

CChartObjectDetector detector;
SChartObjectInfo objects[];

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

Функция OnInit() вызывает detector.Init(0) для привязки детектора к текущему графику. Она выводит сообщение с подтверждением и возвращает INIT_SUCCEEDED. Дополнительная инициализация не требуется — детектор готов к работе сразу же.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   detector.Init(0); // current chart
   Print("Object Detection EA Initialized");
   return(INIT_SUCCEEDED);
  }

Шаг 3 — OnDeinit: очистка (необязательно)

Функция OnDeinit() вызывается при удалении советника с графика. В этом простом тестовом советнике (EA) специальной очистки не требуется, но данная функция включена в соответствии со стандартным шаблоном советника (EA).

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- No resources to free
  }

Шаг 4 — OnTick: обнаружение с ограничением частоты

Функция OnTick() вызывается при каждом ценовом тике. Чтобы избежать записи в журнал сотен строк в секунду, мы используем статическую переменную типа datetime last_run, которая запоминает время последнего выполнения обнаружения. Если прошло менее 5 секунд, функция немедленно завершает работу. В противном случае мы обновляем переменную last_run и переходим к логике обнаружения. Этот шаблон легковесен и эффективен для демонстрационных целей; в рабочих системах для еще большего снижения накладных расходов можно использовать функцию OnTimer().

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime last_run = 0;

   //--- Run every 5 seconds (avoid spam)
   if(TimeCurrent() - last_run < 5)
      return();

   last_run = TimeCurrent();

Шаг 5 — Выполнить обнаружение и вывести результаты

Мы вызываем метод detector.Detect(objects), который заполняет массив objects и возвращает количество обнаруженных аналитических объектов. Затем советник выводит строку заголовка и перебирает массив, используя функцию PrintFormat() для вывода имени, типа и ценовых координат каждого объекта. Для горизонтальных линий имеет смысл только Price1. Для вертикальных линий выведенное в журнал значение Price1 остаётся равным нулю, поскольку тестовый советник выводит только price1 и price2; чтобы увидеть координату времени, измените вызов PrintFormat(), добавив в него time1. В рабочем советнике (EA) этот цикл вывода следует заменить собственной торговой логикой — например, проверкой того, пересекла ли цена обнаруженную линию тренда.

   int total = detector.Detect(objects);

   Print("------ DETECTED OBJECTS: ", total, " ------");

   for(int i = 0; i < total; i++)
     {
      PrintFormat("Name: %s | Type: %s | Price1: %.5f | Price2: %.5f",
                  objects[i].name,
                  objects[i].type_name,
                  objects[i].price1,
                  objects[i].price2);
     }
  }
//+------------------------------------------------------------------+

Полный код тестового советника (EA)

Для справки ниже без разрывов приводится полный код тестового советника (EA) с соблюдением всех правил оформления.

//+------------------------------------------------------------------+
//|                                             ObjectDetectionTestEA.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/ChartObjectDetector.mqh>

CChartObjectDetector detector;
SChartObjectInfo objects[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   detector.Init(0); // current chart
   Print("Object Detection EA Initialized");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- No resources to free
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime last_run = 0;

   //--- Run every 5 seconds (avoid spam)
   if(TimeCurrent() - last_run < 5)
      return();

   last_run = TimeCurrent();

   int total = detector.Detect(objects);

   Print("------ DETECTED OBJECTS: ", total, " ------");

   for(int i = 0; i < total; i++)
     {
      PrintFormat("Name: %s | Type: %s | Price1: %.5f | Price2: %.5f",
                  objects[i].name,
                  objects[i].type_name,
                  objects[i].price1,
                  objects[i].price2);
     }
  }
//+------------------------------------------------------------------+

Теперь детектор готов к интеграции в более крупные торговые системы. Обратите внимание, что в данной версии для сложных объектов, таких как объекты Фибоначчи, будет отображаться значение price1 = price2 = 0 — это нормально, поскольку инструменты Фибоначчи хранят значимые данные в массивах уровней. Во второй части этой серии мы расширим детектор, чтобы он мог извлекать уровни Фибоначчи и обрабатывать события объектов (создание, удаление, изменение).



Тестирование

Настройка тестовой среды (любой график, любой таймфрейм)

Чтобы протестировать детектор, создайте новый советник в MetaEditor, назовите его ObjectDetectionTestEA.mq5 и вставьте код тестового советника точно так, как показано ниже. Убедитесь, что include-файл ChartObjectDetector.mqh находится в папке MQL5/Include/ChartObjectsAlgorithms/ (при необходимости создайте эту папку). Скомпилируйте оба файла (F7). Ошибок компиляции возникнуть не должно. Подключите советник к любому графику — например, EURUSD на таймфрейме M15 — и вручную постройте несколько аналитических объектов: линию тренда, горизонтальную линию, прямоугольник и, по желанию, инструмент коррекции Фибоначчи.

Обнаружение объектов в режиме реального времени (периодический опрос)

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

Демонстрация размещения объектов и их обнаружения в режиме реального времени

GIF-файл, демонстрирующий процесс обнаружения объектов графика на NZDUSD 

Рис. 2: GIF-файл, демонстрирующий добавление линии тренда и горизонтальной линии, а также вывод этих объектов в журнал советником каждые 5 секунд.

Проверка извлеченных свойств (цены, время, уровни Фибоначчи)

Для линий тренда и прямоугольников значения Price1 и Price2, выведенные в журнал, должны точно соответствовать ценам, которые вы указали при построении. Для горизонтальных линий отображается только Price1 (уровень линии). Для вертикальных линий обе цены равны нулю, поскольку тестовый советник выводит только price1 и price2; чтобы увидеть временную координату, измените вызов PrintFormat, добавив в него time1. Объекты Фибоначчи будут показывать ноль для обеих цен, поскольку их основные опорные точки зачастую не заданы; в рабочей версии значения уровней следует извлекать с помощью OBJPROP_FIRSTLEVEL и OBJPROP_LEVELVALUE — это будет рассмотрено в части 2. Текущее поведение не является ошибкой; оно точно отражает тот факт, что некоторые аналитические инструменты не определяются исключительно двумя ценовыми точками.

Почему это важно для вашей торговли

Как только детектор заработает, вам больше не нужно будет вручную отслеживать каждое касание линии тренда или пробой горизонтального уровня. Советник записывает координаты каждого объекта в журнал в режиме реального времени; эту функциональность можно расширить для запуска звуковых сигналов, отправки уведомлений или даже автоматического совершения сделок. Например, можно запрограммировать правило: «если цена закрывается выше обнаруженной линии тренда, покупать 0,1 лота». Та же логика применима к прямоугольникам (пробой выше верхней границы) и горизонтальным линиям (отбой от уровня). Благодаря автоматизации мониторинга объектов вы больше не привязаны к экрану и сокращаете время реагирования с секунд до миллисекунд.

Вывод в журнал и сверка с объектами, нарисованными вручную

Запустите советник и следите за выводом в журнале. На вкладке «Эксперты» панели «Инструменты» должны появиться строки, похожие на приведенный ниже журнал.

Тестовый вывод журнала — пример для NZDUSD M30

2026.05.15 12:07:10.961 ObjectDetectionTestEA (NZDUSD,M30) ------ DETECTED OBJECTS: 2 ------
2026.05.15 12:07:10.961 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Horizontal Line 18762 | Type: HLINE | Price1: 0.59330 | Price2: 0.00000
2026.05.15 12:07:10.961 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Trendline 2432 | Type: TREND | Price1: 0.59901 | Price2: 0.59441
2026.05.15 12:07:16.366 ObjectDetectionTestEA (NZDUSD,M30) ------ DETECTED OBJECTS: 2 ------
2026.05.15 12:07:16.366 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Horizontal Line 18762 | Type: HLINE | Price1: 0.59330 | Price2: 0.00000
2026.05.15 12:07:16.366 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Trendline 2432 | Type: TREND | Price1: 0.59901 | Price2: 0.59441
2026.05.15 12:07:20.979 ObjectDetectionTestEA (NZDUSD,M30) ------ DETECTED OBJECTS: 2 ------
2026.05.15 12:07:20.979 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Horizontal Line 18762 | Type: HLINE | Price1: 0.59330 | Price2: 0.00000
2026.05.15 12:07:20.979 ObjectDetectionTestEA (NZDUSD,M30) Name: M30 Trendline 2432 | Type: TREND | Price1: 0.59901 | Price2: 0.59441

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


Заключение

Мы разработали систематизированный фреймворк MQL5, который обнаруживает и идентифицирует объекты графика, а также извлекает их свойства. Класс CChartObjectDetector преобразует субъективный визуальный слой в структурированный поток данных — массив объектов SChartObjectInfo, содержащих имена, типы, а также временные и ценовые координаты. Это позволяет преодолеть интерпретационный разрыв между субъективным анализом графиков и алгоритмическим исполнением.

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

Обнаружение — это лишь первый шаг. Текущая версия не реагирует на изменения объектов графика (перемещение линии тренда, добавление нового уровня Фибоначчи), если не запустить обнаружение заново. Кроме того, она не позволяет изменять объекты из кода. В части 2 мы расширим этот фреймворк, чтобы отслеживать события объектов графика через OnChartEvent() (CHARTEVENT_OBJECT_CREATE/DELETE/CLICK). Кроме того, мы добавим извлечение уровней Фибоначчи и продемонстрируем двустороннее взаимодействие: создание, изменение и удаление объектов из советника.


Вложения

ChartObjectDetector.mqh — модуль обнаружения (include-файл). Этот файл содержит полный код класса CChartObjectDetector и структуры SChartObjectInfo. Скопируйте его по пути MQL5/Include/ChartObjectsAlgorithms/ChartObjectDetector.mqh.

ObjectDetectionTestEA.mq5 — готовый к запуску тестовый советник. Этот советник демонстрирует, как использовать детектор. Он выполняется каждые 5 секунд и выводит информацию об обнаруженных объектах в журнал Experts. Скопируйте его по пути MQL5/Experts/ObjectDetectionTestEA.mq5.

Инструкции по установке:

1. Создайте папку MQL5/Include/ChartObjectsAlgorithms/, если она не существует.
2. Сохраните файл ChartObjectDetector.mqh в этой папке.
3. Сохраните файл ObjectDetectionTestEA.mq5 в папке MQL5/Experts/.
4. Скомпилируйте оба файла (F7 в MetaEditor).
5. Прикрепите советник к любому графику и нарисуйте как минимум одну линию тренда, горизонтальную линию или прямоугольник.
6. Просмотрите вывод на вкладке "Эксперты" (Experts) (Вид → Тестер стратегий / Эксперты).

Имя файла Тип Версия Описание
ChartObjectDetector.mqh Include-файл
Механизм обнаружения с классом CChartObjectDetector и структурой SChartObjectInfo
ObjectDetectionTestEA.mq5 Советник 1.00 Тестовый советник, который каждые 5 секунд опрашивает и выводит в журнал все обнаруженные аналитические объекты

Этот проект поддерживается в Algo Forge.

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

Прикрепленные файлы |
Автоматизация графических паттернов в MQL5 (Часть 2): Паттерны "двойная вершина" и "двойное дно" Автоматизация графических паттернов в MQL5 (Часть 2): Паттерны "двойная вершина" и "двойное дно"
Мы создадим надежный алгоритм обнаружения паттернов "двойная вершина" и "двойное дно" на языке MQL5. Сначала он подтвердит тренд на H4, а затем проверит шесть условий (равенство точек, положение линии шеи, порядок следования, ширину, высоту и допуски на основе среднего истинного диапазона, ATR). Пробой линии шеи определяется на таймфрейме графика, а машина состояний с тремя состояниями гарантирует, что по каждому паттерну сделка совершается только один раз. Проекция высоты паттерна позволяет определить четкий целевой уровень выхода.
Создание графической библиотеки для MetaTrader 5 по образцу Cairo (Часть 1): Зачем терминалу собственный 2D-рендерер Создание графической библиотеки для MetaTrader 5 по образцу Cairo (Часть 1): Зачем терминалу собственный 2D-рендерер
В этой статье мы начинаем пошаговую разработку графического 2D-движка для MetaTrader. Мы введем единый стандарт представления цветов ARGB и реализуем поверхность с возможностью повторного использования — буфер пикселей типа uint, который загружается как динамический ресурс и отображается с помощью одного объекта OBJ_BITMAP_LABEL. Вы нарисуете прямоугольники и вертикальный градиент, убедитесь, что прозрачность здесь действительно работает, и освоите эффективный способ обновления с одним вызовом Flush.
Создание графической библиотеки для MetaTrader 5 по образцу Cairo (Часть 2): Точки, контуры и путь Создание графической библиотеки для MetaTrader 5 по образцу Cairo (Часть 2): Точки, контуры и путь
Во второй части мы добавим слой геометрии в графическую библиотеку для MetaTrader 5, создаваемую по образцу Cairo. В ней определяется путь из точек с координатами двойной точности, сгруппированных в контуры, сохраняется признак того, является ли контур открытым или замкнутым, а вершины хранятся в плоском массиве с индексами начала контуров. Мы реализуем методы MoveTo, LineTo и Close, добавим базовые вспомогательные методы для построения фигур и демонстрационный пример, который отображает построенную геометрию для визуальной проверки и повторного использования.
Автоматизация графических паттернов в MQL5 (Часть 1): Мультитаймфреймовый движок для анализа свинговой структуры Автоматизация графических паттернов в MQL5 (Часть 1): Мультитаймфреймовый движок для анализа свинговой структуры
В этой статье представлен CSwingEngine — переиспользуемый класс на языке MQL5, который обнаруживает свинговые максимумы и минимумы на H4, присваивает им метки HH, LH, HL или LL и классифицирует рыночную структуру как трендовую или боковую. Свинги всегда определяются на H4 независимо от графика, на котором запущена программа, а каждая точка корректно отображается на младших таймфреймах благодаря штатной привязке к временным координатам типа datetime. Движок предоставляет понятный интерфейс для получения текущего состояния тренда и массива свингов, необходимых для распознавания паттернов с учетом рыночного контекста.