English
preview
От новичка до эксперта: Система автогеометрического анализа

От новичка до эксперта: Система автогеометрического анализа

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

Содержание:

  1. Введение
  2. Предыстория вопроса
  3. Общие сведения
  4. Реализация
  5. Результаты и тестирование
  6. Заключение

Введение

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

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

Когда мы впервые узнали о торговле на рынке Форекс, многие образовательные ресурсы знакомили нас с идеей треугольных образований и других геометрических паттернов в движении рыночных цен. И действительно, геометрия присутствует на рынках — она может дать упрощенное и наглядное представление о том, что происходит на рынке.

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


Предыстория вопроса

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

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

Что такое геометрия?

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

Почему геометрия важна в контексте трейдинга?

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

  • Треугольник (восходящий, нисходящий, симметричный)
  • Прямоугольники (зоны консолидации)
  • Каналы (параллельные линии тренда)
  • «Голова-Плечи», "Двойная вершина/дно" — все зависит от геометрической симметрии

Эти паттерны помогают трейдерам:

  • Выявлять потенциальные пробои или развороты
  • Измерять силу тренда
  • Устанавливать точки ввода/вывода

В этой таблице я собрал ключевые соображения по применению геометрического анализа к данным о рыночных ценах.

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

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

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

Треугольник

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

1. Симметричный треугольник:

Указывает на период консолидации с уменьшающейся волатильностью. Он часто предшествует пробою в любом направлении.

Symmetrical Triangle

Симметричный треугольник

2. Восходящий треугольник:

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

Ascending Triangle

Восходящий треугольник

3. Нисходящий треугольник:

Имеет плоскую или слегка наклонную линию поддержки и снижающееся сопротивление; обычно медвежий характер, намекающий на пробой вниз.

Descending Triangle

Нисходящий треугольник

Прямоугольник

Прямоугольник - это четырехгранная геометрическая фигура с равными и параллельными противоположными сторонами. Он имеет прямые углы (каждый 90 градусов) в каждом углу. В Форекс и техническом анализе прямоугольник (также называемый торговым диапазоном или зоной консолидации) - это графический паттерн, при котором цена движется в стороны в пределах горизонтальных уровней поддержки и сопротивления. Указывает на период нерешительности, прежде чем рынок потенциально продолжит прежний тренд или развернется вспять. Он доступен среди инструментов анализа рынка в терминале Meta Trader 5.

Вот основные характеристики:

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

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

Rectangle Structure

Прямоугольная структура

Breakdown Rectangle

Пробой под прямоугольной структурой


Общие сведения

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

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

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

Чтобы помочь вам понять, ниже я привел несколько иллюстраций, демонстрирующих, как эти фигуры можно построить на диаграмме с использованием компьютерной логики. Обычно, чтобы очертить каждую фигуру, нам требуется как минимум четыре ключевые точки. На изображениях мы начинаем с определения точек a, b, c, и d — они служат основными опорами для нашей структуры фигуры. Исходя из этих точек, можно заранее спроецировать фигуру. Пунктирные красные линии представляют собой теоретические ожидания, указывающие на то, как рынок может отреагировать на предполагаемые границы фигуры. То, как цена взаимодействует с этими линиями, может дать важные подсказки о будущем движении цены, такие как потенциальные пробои или отскоки.

Triangle formation

Теоретическое определение треугольника

На изображении треугольника выше, точки поворота от aдо d определены в качестве опорных точек, из которых можно построить треугольник со сходящимися линиями, встречающимися в точке X. Точки e и f представляют собой теоретические изменения в будущем — они носят умозрительный характер и могут проявляться не совсем так, как показано на рисунке. Их цель - помочь нам концептуализировать шаблон, чтобы мы могли воплотить идею в коде. Та же концепция применима к приведенному ниже рисунку прямоугольника, где структура определяется с использованием аналогичной логики. Однако ключевое отличие заключается в том, что его стороны параллельны.

Rectangle Formation

Обнаружение прямоугольной структуры


Реализация

На этом этапе начинается собственно разработка. Начнем с создания заголовочного файла с именем GeometricPatternDetector.mqh, который будет содержать все необходимые классы, которые требуются для обнаружения геометрических паттернов. Как только заголовок будет готов, мы приступим к разработке советника (EA), демонстрирующего, как эффективно использовать классы. Теперь давайте выполним следующие шаги, чтобы понять, как все это работает.

Включение необходимых заголовков

В самом верху GeometricPatternDetector.mqh мы используем три основные библиотеки MQL5, чтобы предоставить нашему детектору все необходимые инструменты. Во-первых, мы включили arrays\ArrayObj.mqh, чтобы можно было использовать CArrayObj — динамический объектно-ориентированный класс массивов для хранения наших точек разворота и результатов моделирования без использования необработанных указателей или ручного управления памятью.

Далее Indicators\Indicators.mqh использует встроенные в MetaTrader функции индикатора, такие как iATR, CopyHigh, CopyLow и CopyTime, позволяя получать исторические данные о цене и легко рассчитывать средний истинный диапазон. Наконец, Math\Stat\Math.mqh предоставляет математические константы, такие как M_PI, и вспомогательные функции, такие как MathArctan, которые мы используем для вычисления наклонов, углов и допусков на плоскостность в наших алгоритмах обнаружения паттернов. В совокупности эти элементы создают основу для надежного, читаемого и сопровождаемого кода.

#include <Arrays\ArrayObj.mqh>       // For CArrayObj: dynamic arrays of objects 
#include <Indicators\Indicators.mqh> // For iATR, CopyHigh, CopyLow, CopyTime
#include <Math\Stat\Math.mqh>        // For M_PI, MathArctan, and other math utilities

Контейнерные классы

Заголовок GeometricPatternDetector.mqh начинается с определения двух простых контейнерных классов: SwingPoint и PatternResult. Оба они происходят от базового CObject в MQL5. Класс SwingPoint содержит временную метку, цену и логический флаг, указывающий, является ли пивот максимумом или минимумом. Такой дизайн позволяет нам собирать отдельные пивоты рынка и управлять ими в едином массиве объектов.

Класс PatternResult содержит всю информацию, необходимую для описания обнаруженного паттерна, а именно символ, таймфрейм, тип паттерна, три определяющие "вершины" и точку, в которой должна быть размещена метка. Благодаря упаковке этих элементов данных в объекты код детектора остается чистым и непротиворечивым, поскольку для хранения используется CArrayObj, а не созданные вручную параллельные массивы примитивов.

// SwingPoint: holds one market pivot
class SwingPoint : public CObject {
public:
   datetime time;
   double   price;
   bool     isHigh;
   SwingPoint(datetime t=0,double p=0.0,bool h=false)
     : time(t), price(p), isHigh(h) {}
};

// PatternResult: holds the details of one detected pattern
class PatternResult : public CObject {
public:
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   ENUM_PATTERN_TYPE type;
   datetime          detectionTime;
   datetime          labelTime;
   double            labelPrice;
   datetime          vertex1Time;
   double            vertex1Price;
   datetime          vertex2Time;
   double            vertex2Price;
   datetime          vertex3Time;
   double            vertex3Price;
   PatternResult(const string _s,const ENUM_TIMEFRAMES _tf,const ENUM_PATTERN_TYPE _t,
                 datetime lt,double lp,
                 datetime v1t,double v1p,
                 datetime v2t,double v2p,
                 datetime v3t,double v3p)
     : symbol(_s), timeframe(_tf), type(_t), detectionTime(TimeCurrent()),
       labelTime(lt), labelPrice(lp),
       vertex1Time(v1t), vertex1Price(v1p),
       vertex2Time(v2t), vertex2Price(v2p),
       vertex3Time(v3t), vertex3Price(v3p) {}
};

Структура класса Detector

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

Четыре вспомогательных метода (IsNewBar, UpdateSwingPoints, CalculateATR и GetSwingHash) объявляются приватно. Эти утилиты управляют механизмами идентификации новых баров, извлечения точек разворота из последних данных о максимумах / минимумах, вычисления показателя чувствительности рынка с помощью ATR и генерации уникального строкового ключа для каждого набора из четырех пивотов. Вместе они поддерживают две основных подпрограммы обнаружения public, DetectTriangle и DetectRectangle, а также метод Update, который связывает все вместе, плюс методы GetLastPattern и ClearPatterns для получения результатов и очистки ресурсов.

class CGeometricPatternDetector {
private:
   CArrayObj   m_swings;
   int         m_swingLookback;
   double      m_atrMultiplier;
   int         m_minTouchPoints;

   string      m_lastTriangle;
   string      m_lastSwingHash;
   datetime    m_lastTriangleTime;

   string      m_lastRectangle;
   string      m_lastRectangleHash;
   datetime    m_lastRectangleTime;

   bool    IsNewBar(const string sym,const ENUM_TIMEFRAMES tf);
   void    UpdateSwingPoints(const string sym,const ENUM_TIMEFRAMES tf);
   double  CalculateATR(const string sym,const ENUM_TIMEFRAMES tf,const int period=14);
   string  GetSwingHash(SwingPoint *p1,SwingPoint *p2,SwingPoint *p3,SwingPoint *p4);

public:
   CGeometricPatternDetector(int swingLookback=3,double atrMultiplier=1.5,int minTouchPoints=2);
   ~CGeometricPatternDetector();

   void Update(const string sym,const ENUM_TIMEFRAMES tf);
   void DetectTriangle(const string sym,const ENUM_TIMEFRAMES tf);
   void DetectRectangle(const string sym,const ENUM_TIMEFRAMES tf);
   ENUM_PATTERN_TYPE GetLastPattern();
   void ClearPatterns();

   CArrayObj m_currentPatterns;
};

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

Метод извлечения точек качания UpdateSwingPoints вызывается на каждом новом баре. Он копирует окно максимумов, минимумов и временных меток с графика, затем обозначает центральный бар в этом окне как максимум колебания, если его цена превышает оба соседних бара, или минимум колебания, если его цена ниже обоих соседних баров. Каждый подтвержденный пивот оборачивается объектом SwingPoint и добавляется к массиву m_swings.

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

void CGeometricPatternDetector::UpdateSwingPoints(const string sym,const ENUM_TIMEFRAMES tf)
{
   int bars = m_swingLookback*2 + 1;
   double highs[], lows[];
   datetime times[];
   ArraySetAsSeries(highs,true);
   ArraySetAsSeries(lows,true);
   ArraySetAsSeries(times,true);

   if(CopyHigh(sym,tf,0,bars,highs)<bars ||
      CopyLow(sym,tf,0,bars,lows)<bars   ||
      CopyTime(sym,tf,0,bars,times)<bars)
   {
      Print("Error: Failed to copy price/time data");
      return;
   }

   int mid = m_swingLookback;
   datetime t = times[mid];
   double h = highs[mid], l = lows[mid];

   bool isH = true;
   for(int i=1; i<=m_swingLookback; i++)
      if(h<=highs[mid-i] || h<=highs[mid+i]) { isH=false; break; }
   if(isH) {
      m_swings.Add(new SwingPoint(t,h,true));
      Print("Swing High detected at ",TimeToString(t)," Price: ",h);
   }

   bool isL = true;
   for(int i=1; i<=m_swingLookback; i++)
      if(l>=lows[mid-i] || l>=lows[mid+i]) { isL=false; break; }
   if(isL) {
      m_swings.Add(new SwingPoint(t,l,false));
      Print("Swing Low detected at ",TimeToString(t)," Price: ",l);
   }

   while(m_swings.Total()>50) {
      delete (SwingPoint*)m_swings.At(0);
      m_swings.Delete(0);
   }
}

CalculateATR преобразует встроенный в MQL5 индикатор ATR в чувствительную к рынку единицу измерения, в то время как GetSwingHash объединяет четыре пивота времени и цены в строковый ключ. Значение ATR определяет допуски как для “плоскостности” треугольников, так и для выравнивания прямоугольников. Хэш-строка гарантирует, что каждый уникальный набор пивотов выводится только один раз за период блокировки.

double CGeometricPatternDetector::CalculateATR(const string sym,const ENUM_TIMEFRAMES tf,const int period)
{
   int h = iATR(sym,tf,period);
   if(h==INVALID_HANDLE) { Print("ATR handle error"); return 0; }
   double buf[]; ArraySetAsSeries(buf,true);
   if(CopyBuffer(h,0,0,1,buf)!=1) { Print("ATR copy error"); return 0; }
   return buf[0];
}

string CGeometricPatternDetector::GetSwingHash(SwingPoint *p1,SwingPoint *p2,SwingPoint *p3,SwingPoint *p4)
{
   return TimeToString(p1.time)+"*"+DoubleToString(p1.price,8)+"*"
        + TimeToString(p2.time)+"*"+DoubleToString(p2.price,8)+"*"
        + TimeToString(p3.time)+"*"+DoubleToString(p3.price,8)+"*"
        + TimeToString(p4.time)+"_"+DoubleToString(p4.price,8);
}

Логика обнаружения треугольника

Подпрограмма обнаружения треугольника строго отслеживает четыре последовательных колебания в ордере от минимума к максимуму и от минимума к максимуму. После определения минимального интервала между барами для каждой стороны она рассчитывает наклоны сторон минимума и максимума, разделяя разницу в цене на разницу во времени. Допуск, основанный на ATR, определяет, является ли вершина или основание треугольника достаточно ровными, чтобы считаться нисходящими или восходящими; если ни одна из сторон треугольника не является достаточно плоской, паттерн классифицируется как симметричный.

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

void CGeometricPatternDetector::DetectTriangle(const string sym,const ENUM_TIMEFRAMES tf)
{
   int tot = m_swings.Total();
   if(tot < 4) return;
   ulong barSec = PeriodSeconds(tf);
   ulong minSpan = (ulong)m_swingLookback * barSec;

   for(int i=0; i<=tot-4; i++)
   {
      SwingPoint *p1 = (SwingPoint*)m_swings.At(i);
      SwingPoint *p2 = (SwingPoint*)m_swings.At(i+1);
      SwingPoint *p3 = (SwingPoint*)m_swings.At(i+2);
      SwingPoint *p4 = (SwingPoint*)m_swings.At(i+3);

      if(!( !p1.isHigh && p2.isHigh && !p3.isHigh && p4.isHigh )) continue;
      if((ulong)(p2.time-p1.time)<minSpan ||
         (ulong)(p3.time-p2.time)<minSpan ||
         (ulong)(p4.time-p3.time)<minSpan) continue;

      double m_low  = (p3.price - p1.price) / double(p3.time - p1.time);
      double m_high = (p4.price - p2.price) / double(p4.time - p2.time);
      double tolFlat = CalculateATR(sym,tf,14) * m_atrMultiplier;

      bool lowerFlat = MathAbs(p3.price-p1.price) < tolFlat;
      bool upperFlat = MathAbs(p4.price-p2.price) < tolFlat;

      ENUM_PATTERN_TYPE type;
      if(lowerFlat && m_high < 0)             type = PATTERN_TRIANGLE_DESCENDING;
      else if(upperFlat && m_low > 0)         type = PATTERN_TRIANGLE_ASCENDING;
      else                                    type = PATTERN_TRIANGLE_SYMMETRICAL;

      double denom = m_low - m_high;
      if(MathAbs(denom)<1e-12) continue;
      double num = (p2.price - p1.price) + (m_low*p1.time - m_high*p2.time);
      double tx  = num/denom;
      double px  = p1.price + m_low*(tx-p1.time);

      datetime latest = MathMax(p1.time,p2.time);
      if(tx<=latest || tx>TimeCurrent()+barSec*50) continue;

      if(StringLen(m_lastTriangle)>0)
         ObjectDelete(0,m_lastTriangle);

      string base = "GPD_"+sym+"_"+EnumToString(tf)+"_"+TimeToString(TimeCurrent(),TIME_SECONDS);
      long cid    = ChartID();
      m_lastTriangle = base+"_T";

      ObjectCreate(cid,m_lastTriangle,OBJ_TRIANGLE,0,
                   p1.time,p1.price,
                   p2.time,p2.price,
                   tx,      px);
      ObjectSetInteger(cid,m_lastTriangle,OBJPROP_COLOR,clrOrange);
      ObjectSetInteger(cid,m_lastTriangle,OBJPROP_WIDTH,2);
      ObjectSetInteger(cid,m_lastTriangle,OBJPROP_FILL,false);

      m_lastSwingHash    = GetSwingHash(p1,p2,p3,p4);
      m_lastTriangleTime = TimeCurrent();
      m_currentPatterns.Add(new PatternResult(
         sym,tf,type,
         latest,(p2.price+p4.price)/2.0,
         p1.time,p1.price,
         p2.time,p2.price,
         (datetime)tx,px
      ));

      ChartRedraw(cid);
      break;
   }
}

Логика обнаружения прямоугольника

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

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

void CGeometricPatternDetector::DetectRectangle(const string sym,const ENUM_TIMEFRAMES tf)
{
   int tot = m_swings.Total();
   if(tot < 4) return;

   ulong barSec    = PeriodSeconds(tf);
   ulong minSpan5  = 5 * barSec;                        
   double tolATR   = CalculateATR(sym,tf,14) * m_atrMultiplier;

   for(int i=0; i<=tot-4; i++)
   {
      SwingPoint *a = (SwingPoint*)m_swings.At(i);
      SwingPoint *b = (SwingPoint*)m_swings.At(i+1);
      SwingPoint *c = (SwingPoint*)m_swings.At(i+2);
      SwingPoint *d = (SwingPoint*)m_swings.At(i+3);

      if(!( !a.isHigh && b.isHigh && !c.isHigh && d.isHigh )) continue;
      if((ulong)(b.time - a.time) < minSpan5 ||
         (ulong)(d.time - c.time) < minSpan5) continue;
      if(MathAbs(b.price - a.price) < tolATR ||
         MathAbs(d.price - c.price) < tolATR) continue;
      if(MathAbs(a.price - c.price) > tolATR) continue;

      bool highAligned = MathAbs(b.price - d.price) < tolATR;
      double lowP  = (a.price + c.price) / 2.0;
      double highP = highAligned ? (b.price + d.price)/2.0 : lowP + tolATR;

      datetime leftT  = MathMin(a.time, c.time);
      datetime rightT = leftT + (datetime)(20 * barSec);

      string rh = TimeToString(leftT,TIME_SECONDS) + "_" +
                  DoubleToString(lowP,8) + "_" +
                  DoubleToString(highP,8);
      datetime lockT = m_lastRectangleTime + (datetime)(40 * barSec);
      if(rh == m_lastRectangleHash && TimeCurrent() < lockT) return;

      if(StringLen(m_lastRectangle) > 0)
         ObjectDelete(0,m_lastRectangle);

      string base = "GPD_"+sym+"_"+EnumToString(tf)+"_"+TimeToString(TimeCurrent(),TIME_SECONDS);
      long cid    = ChartID();
      m_lastRectangle = base+"_Rect";

      ObjectCreate(cid,m_lastRectangle,OBJ_RECTANGLE,0,
                   leftT,   highP,
                   rightT,  lowP);
      ObjectSetInteger(cid,m_lastRectangle,OBJPROP_COLOR, clrBlue);
      ObjectSetInteger(cid,m_lastRectangle,OBJPROP_WIDTH, 2);
      ObjectSetInteger(cid,m_lastRectangle,OBJPROP_FILL,  false);

      m_currentPatterns.Add(new PatternResult(
         sym,tf,PATTERN_RECTANGLE,
         leftT,(highP+lowP)/2.0,
         leftT,highP,
         leftT,lowP,
         rightT,lowP
      ));
      m_lastRectangleHash = rh;
      m_lastRectangleTime = TimeCurrent();

      ChartRedraw(cid);
      break;
   }
}

Использование заголовка в советнике

Интегрировать этот заголовок в советник несложно. Мы просто включаем его, создаем CGeometricPatternDetector с нашими предпочтительными параметрами и вызываем его метод Update(Symbol(), _Period) в обработчике событий OnTick . После каждого обновления GetLastPattern показывает, обнаружен ли новый треугольник или прямоугольник, и можно просмотреть detector.m_currentPatterns для получения полной информации, выдачи предупреждений или размещения меток на диаграмме.

Советнику не нужно знать какую-либо базовую геометрию или логику пивота; он просто управляет детектором и реагирует на результаты высокого уровня. Это разделение — объявление в заголовке, подробная реализация в том же файле и простое использование в советнике — демонстрирует, как объектно-ориентированный дизайн в MQL5 инкапсулирует сложность и создает повторно используемый, поддерживаемый код. Рассмотрим подробные шаги, описанные ниже.

Заголовок включает в себя и глобальное состояние

В самом верху GeometryAnalyzerEA.mq5 мы включаем заголовок нашего детектора, чтобы советник мог получить доступ к классу CGeometricPatternDetector и его вспомогательным типам. Сразу после этого мы инстанцируем отдельный объект—детектор с выбранными параметрами - три бара ретроспективно для пивотов, множитель ATR, равный 1,5, для допуска и две минимальные точки взаимодействия для шаблонов. Мы также заявляем три глобальные переменные: lastAlerted помнит тип последнего паттерна, о котором было предупреждение, чтобы мы не повторяли его в том же баре, lastBarTime отслеживает появление нового бара, а lastLabelName содержит имя размещенной нами текстовой метки так, что её можно удалить при появлении нового паттерна.

#include <GeometricPatternDetector.mqh>

//–– detector instance & state
CGeometricPatternDetector  detector(3, 1.5, 2);
ENUM_PATTERN_TYPE          lastAlerted    = PATTERN_NONE;
datetime                   lastBarTime    = 0;
string                     lastLabelName  = "";

Инициализация и очистка

Функция OnInit запускается однократно при запуске советника. Здесь мы просто выводим сообщение для подтверждения инициализации, хотя также можно запустить таймер или выделить ресурсы, если это необходимо. И наоборот, OnDeinit срабатывает при удалении советника или закрытии графика. В этой процедуре мы очищаем все нарисованные паттерны с помощью detector.ClearPatterns()и удаляем любую задерживающуюся текстовую метку по имени. Наконец, мы регистрируем причину деинициализации, что упрощает диагностику причины остановки советника.

int OnInit()
{
   Print("GeometryAnalyzerEA initialized");
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   detector.ClearPatterns();
   if(StringLen(lastLabelName) > 0)
      ObjectDelete(0, lastLabelName);
   Print("GeometryAnalyzerEA deinitialized, reason=", reason);
}

Обнаружение нового бара

Внутри OnTick первым шагом является определение того, относится ли входящий тик к вновь сформированному бару. Мы извлекаем время открытия текущего бара и сравниваем его с сохраненным lastBarTime. Если бар новый, сбрасываем lastAlerted, чтобы паттерны сработали снова, и удаляем предыдущую текстовую метку, чтобы сохранить график чистым. Если бар не является новым, мы просто возвращаемся и больше ничего не делаем, обеспечив, что обнаружение паттерна выполняется только один раз для каждого бара.

void OnTick()
{
   datetime curBar = iTime(Symbol(), _Period, 0);
   bool isNewBar  = (curBar != lastBarTime);
   if(isNewBar)
   {
      lastBarTime = curBar;
      lastAlerted = PATTERN_NONE;
      if(StringLen(lastLabelName) > 0)
      {
         ObjectDelete(0, lastLabelName);
         lastLabelName = "";
      }
   }
   if(!isNewBar)
      return;
   // …
}

Запуск детектора

После подтверждения нового бара мы вызываем метод обновления детектора (Update), передавая текущий символ и таймфрейм. Этот однократный вызов обновляет точки поворота и выполняет обе процедуры обнаружения треугольника и прямоугольника одновременно. После возврата Update мы запрашиваем GetLastPattern(), чтобы узнать, найден ли допустимый паттерн на этом баре. Если новый паттерн не появляется или совпадает с тем, о котором мы уже предупреждали, мы завершаем работу досрочно.

detector.Update(Symbol(), _Period);

ENUM_PATTERN_TYPE pattern = detector.GetLastPattern();
if(pattern == PATTERN_NONE || pattern == lastAlerted)
   return;
lastAlerted = pattern;

Обработка обнаруженного паттерна

Если появляется новый шаблон, мы извлекаем самый последний PatternResult из массива паттернов детектора. Затем мы преобразуем enum паттерна в удобочитаемое имя и форматируем его вершины для ведения лога. Прямоугольники обрабатываются особым образом, поскольку они содержат только три угловые точки. Мы приближаем четвертую, чтобы получить полный набор координат. Мы выдаем как всплывающее предупреждение Alert, так и оповещение Print с указанием названия паттерна, времени, цены и угловых координат.

int count = detector.m_currentPatterns.Total();
PatternResult *pr = (PatternResult*)detector.m_currentPatterns.At(count - 1);

string name = (pattern == PATTERN_RECTANGLE) ? "Rectangle" :
              (pattern == PATTERN_TRIANGLE_ASCENDING)   ? "Ascending Triangle" :
              (pattern == PATTERN_TRIANGLE_DESCENDING)  ? "Descending Triangle" :
                                                         "Symmetrical Triangle";

Alert("GeometryAnalyzerEA: Detected ", name, " on ", Symbol(), " ", EnumToString(_Period));
Print("GeometryAnalyzerEA: ", name,
      " @", TimeToString(pr.labelTime, TIME_SECONDS),
      " Price=", pr.labelPrice);

Нанесение меток

Наконец, мы помещаем текстовую метку на график во время и по цене, указанным паттерном. Уникальное имя объекта генерируется путем объединения имени паттерна со временем метки. Используя функции график‐-объект, рисуем OBJ_TEXT и задаем его свойства ‐- текстовое содержимое, цвет, размер шрифта и фон, — чтобы он четко выделялся на диаграмме. Регистрируем название последней метки, чтобы прежде чем рисовать новую метку, её можно было удалить на следующем новом баре. Вызов для перерисовки графика обеспечивает немедленную отрисовку.

   lastLabelName = name + "_" + TimeToString(pr.labelTime, TIME_SECONDS);
   long chartId = ChartID();
   if(ObjectCreate(chartId, lastLabelName, OBJ_TEXT, 0, pr.labelTime, pr.labelPrice))
   {
      ObjectSetString(chartId, lastLabelName, OBJPROP_TEXT,     name);
      ObjectSetInteger(chartId, lastLabelName, OBJPROP_COLOR,    clrOrangeRed);
      ObjectSetInteger(chartId, lastLabelName, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(chartId, lastLabelName, OBJPROP_BACK,     true);
   }
   ChartRedraw(chartId);
}


Тестирование и результаты

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

Визуализация тестера стратегий: GeometricAnalyzerEA

Визуализация тестера стратегий: GeometricAnalyzerEA

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

2025.05.20 13:40:54.241 2025.01.14 18:45:00   Alert: GeometryAnalyzerEA: Detected Ascending Triangle on AUDJPY.0 PERIOD_M1
2025.05.20 13:40:54.241 2025.01.14 18:45:00   GeometryAnalyzerEA: Ascending Triangle @14:03:00 Price=97.45599999999999 → Vertices: (1736863200@97.381), (1736863380@97.448), (1736873819@97.912)
2025.05.20 13:40:54.242 2025.01.14 18:46:00   Swing High detected at 2025.01.14 18:43 Price: 97.789


Заключение

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

Реализованный нами подход закладывает прочную основу для разработки более сложных аналитических систем. Класс GeometricPatternDetector является модульным и многоразовым — он может быть интегрирован в другие проекты и расширен для повышения точности обнаружения и гибкости.

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

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

Для тех из нас, кто создает такие системы, важно освоить внутреннюю реализацию. Но благодаря инкапсулированному дизайну другие пользователи по-прежнему могут пользоваться функциональностью, не вникая в базовый код. Благодаря такой структуре мы теперь можем уверенно разрабатывать и расширять систему для выявления любых паттернов на рынке, просто разрабатывая соответствующие классы.

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


Содержимое вложения:

Файл Описание
GeometricPatternDetector.mqh
Этот заголовочный файл содержит полную реализацию логики обнаружения геометрических паттернов на основе классов. Он определяет структуры данных для точек колебания и результатов моделирования, управляет обнаружением точек колебания, вычисляет чувствительность рынка с помощью ATR и включает процедуры для идентификации треугольников и прямоугольников. Файл предназначен для модульной интеграции в советники.
GeometryAnalyzerEA.mq5
Этот советник демонстрирует практическое использование заголовка для определения паттернов. Инициализирует и обновляет класс `CGeometricPatternDetector` на каждом новом баре, извлекает результаты паттернов и визуально комментирует обнаруженные паттерны на графике. Это служит простым, реальным примером интеграции распознавания объектно-ориентированных паттернов в торговую стратегию.

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

Прикрепленные файлы |
Возможности Мастера MQL5, которые вам нужно знать (Часть 51): Обучение с подкреплением с помощью SAC Возможности Мастера MQL5, которые вам нужно знать (Часть 51): Обучение с подкреплением с помощью SAC
Soft Actor Critic (мягкий актер-критик) — это алгоритм обучения с подкреплением, использующий три нейронные сети — сеть актеров и две сети критиков. Такие модели машинного обучения объединены в партнерство "главный-подчиненный", где критики моделируются для повышения точности прогнозов сети актеров. Как обычно, рассмотрим, как эти идеи можно протестировать в качестве пользовательского сигнала советника, собранного с помощью Мастера.
Изучение передовых методов машинного обучения в стратегии пробоя «коридора Дарваса» (Darvas Box Breakout) Изучение передовых методов машинного обучения в стратегии пробоя «коридора Дарваса» (Darvas Box Breakout)
Стратегия Darvas Box Breakout, созданная Николасом Дарвасом, представляет собой подход в технической торговле, который выявляет потенциальные сигналы на покупку, когда цена акций поднимается выше установленного диапазона «коридора», что указывает на сильный восходящий импульс. В этой статье мы применим эту стратегическую концепцию в качестве примера для изучения трех передовых методов машинного обучения. К ним относятся использование модели машинного обучения для генерации сигналов вместо фильтрации сделок, применение непрерывных сигналов вместо дискретных и использование для подтверждения сделок моделей, обученных на разных таймфреймах.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Торговый инструментарий MQL5 (Часть 5): Расширение EX5-библиотеки для управления историей с помощью функций позиции Торговый инструментарий MQL5 (Часть 5): Расширение EX5-библиотеки для управления историей с помощью функций позиции
В этой статье мы узнаем, как создавать экспортируемые EX5-функции для эффективного запроса и сохранения исторических данных о позициях. В этом пошаговом руководстве мы расширим EX5-библиотеку для управления историей (History Management), разработав модули, которые извлекают ключевые свойства последней закрытой позиции. К ним относятся чистая прибыль, продолжительность сделки, стоп-лосс и тейк-профит в пипсах, значения прибыли и другие важные данные.