Исследование методов свечного анализа (Часть III): Библиотека работы с паттернами

Alexander Fedosov | 2 апреля, 2019

Содержание

Введение

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

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


Структура библиотеки

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

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

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

Блок 1. Здесь отображен весь список простых типов свечей из которых состоят как существующие паттерны, так и сгенерированные. Каждый из типов имеет свои настройки, которые можно увидеть нажав на шестеренку в верхнем правом углу визуального представления свечи. При этом стоит отметить, что для типов свечей с первой по пятую существует лишь одна настройка, но для свечи типа Молот две. 

Блок 2. Весовые коэффициенты. Имеют три параметра К1, К2, К3 и влияют на результаты оценки эффективности паттернов. 

Блок 3. Пороговое значение тренда в пунктах. 

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

Блок 5. Число свечей в паттерне. Также эта настройка применима только для кастомных паттернов. 

Далее рассмотрим вкладку Анализ и обратим внимание на входные параметры на ней.

Рис.2 Входные параметры на вкладке Анализ.

Блок 6. В этом блок расположены настройки текущего таймфрейма, а также Диапазон выборки данных, на котором исследуются паттерны. 

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

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

Разработка библиотеки

Итак, определившись с основными моментами перед программной реализацией, начнем создание самой библиотеки. Для начала создадим файл необходимых перечислений Enums.mqh.

//+------------------------------------------------------------------+
//|                                                        Enums.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                           https://www.mql5.com/ru/users/alex2356 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Тип свечи                                                        |
//+------------------------------------------------------------------+
enum TYPE_CANDLESTICK
  {
   CAND_NONE,           // Неопознанная
   CAND_MARIBOZU,       // Марибозу
   CAND_DOJI,           // Дожи
   CAND_SPIN_TOP,       // Волчки
   CAND_HAMMER,         // Молот
   CAND_INVERT_HAMMER,  // Перевернутый молот
   CAND_LONG,           // Длинная
   CAND_SHORT           // Короткая
  };
//+------------------------------------------------------------------+
//| Тип паттерна                                                     |
//+------------------------------------------------------------------+
enum TYPE_PATTERN
  {
   NONE,
   HUMMER,
   INVERT_HUMMER,
   HANDING_MAN,
   SHOOTING_STAR,
   ENGULFING_BULL,
   ENGULFING_BEAR,
   HARAMI_BULL,
   HARAMI_BEAR,
   HARAMI_CROSS_BULL,
   HARAMI_CROSS_BEAR,
   DOJI_STAR_BULL,
   DOJI_STAR_BEAR,
   PIERCING_LINE,
   DARK_CLOUD_COVER
  };
//+------------------------------------------------------------------+
//| Тип тренда                                                       |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //Восходящий
   DOWN,                //Нисходящий
   FLAT                 //Боковой
  };
//+------------------------------------------------------------------+

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

Далее создадим файл Pattern.mqh в нем будет создан класс CPattern и в начале, в приватной секции, объявим переменные для параметров, на которые я обратил внимание в предыдущем разделе. А также сразу подключим файл с перечислениями.

//+------------------------------------------------------------------+
//|                                                      Pattern.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                           https://www.mql5.com/ru/users/alex2356 |
//+------------------------------------------------------------------+
#include "Enums.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CPattern
  {
private:
   //--- Весовые коэффициенты
   double            m_k1;
   double            m_k2;
   double            m_k3;
   //--- Пороговое значение тренда в пунктах
   int               m_threshold_value;
   //--- Коэффициент настроек длинной свечи
   double            m_long_coef;
   //--- Коэффициент настроек короткой свечи
   double            m_short_coef;
   //--- Коэффициент настроек свечи доджи
   double            m_doji_coef;
   //--- Коэффициент настроек свечи марибозу
   double            m_maribozu_coef;
   //--- Коэффициент настроек свечи волчок
   double            m_spin_coef;
   //--- Коэффициенты настроек свечи молот
   double            m_hummer_coef1;
   double            m_hummer_coef2;
   //--- Диапазон выборки для предустановленных паттернов
   int               m_range_total;
   //--- Период для определения тренда
   int               m_trend_period;
   //--- Найдено паттернов
   int               m_found;
   //--- Встречаемость паттерна
   double            m_coincidence;
   //--- Вероятность движения вверх и вниз
   double            m_probability1;
   double            m_probability2;
   //--- Эффективность
   double            m_efficiency1;
   double            m_efficiency2;
   //--- Свойства простой свечи
   struct CANDLE_STRUCTURE
     {
      double            m_open;
      double            m_high;
      double            m_low;
      double            m_close;                      // OHLC
      TYPE_TREND        m_trend;                      // Тренд
      bool              m_bull;                       // Бычья свеча
      double            m_bodysize;                   // Размер тела
      TYPE_CANDLESTICK  m_type;                       // Тип свечи
     };
   //--- Свойства оценки эффективности паттерна
   struct RATING_SET
     {
      int               m_a_uptrend;
      int               m_b_uptrend;
      int               m_c_uptrend;
      int               m_a_dntrend;
      int               m_b_dntrend;
      int               m_c_dntrend;
     };

Как видно из листинга выше, в нем добавлены две структуры. Первая CANDLE_STRUCTURE необходима для определения типа свечи на графике. Также обращу ваше внимание, что в ней используется два типа перечислений — тип тренда TYPE_TREND и TYPE_CANDLESTICK из файла Enums.mqh, рассмотренных ранее и созданных как раз для данной структуры. Вторая структура RATING_SET хранит в себе записи оценок движения цены после появления заданного паттерна. Подробнее об этом описано в первой статье.

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

public:
                     CPattern(void);
                    ~CPattern(void);
   //--- Установка и возврат весовых коэффициентов
   void              K1(const double k1)                             { m_k1=k1;                       }
   double            K1(void)                                        { return(m_k1);                  }
   void              K2(const double k2)                             { m_k2=k2;                       }
   double            K2(void)                                        { return(m_k2);                  }
   void              K3(const double k3)                             { m_k3=k3;                       }
   double            K3(void)                                        { return(m_k3);                  }
   //--- Установка и возврат порогового значения тренда
   void              Threshold(const int threshold)                  { m_threshold_value=threshold;   }
   int               Threshold(void)                                 { return(m_threshold_value);     }
   //--- Установка и возврат коэффициента настроек длинной свечи
   void              Long_coef(const double long_coef)               { m_long_coef=long_coef;         }
   double            Long_coef(void)                                 { return(m_long_coef);           }
   //--- Установка и возврат коэффициента настроек короткой свечи
   void              Short_coef(const double short_coef)             { m_short_coef=short_coef;       }
   double            Short_coef(void)                                { return(m_short_coef);          }
   //--- Установка и возврат коэффициента настроек свечи доджи
   void              Doji_coef(const double doji_coef)               { m_doji_coef=doji_coef;         }
   double            Doji_coef(void)                                 { return(m_doji_coef);           }
   //--- Установка и возврат коэффициента настроек свечи марибозу
   void              Maribozu_coef(const double maribozu_coef)       { m_maribozu_coef=maribozu_coef; }
   double            Maribozu_coef(void)                             { return(m_maribozu_coef);       }
   //--- Установка и возврат коэффициента настроек свечи волчок
   void              Spin_coef(const double spin_coef)               { m_spin_coef=spin_coef;         }
   double            Spin_coef(void)                                 { return(m_spin_coef);           }
   //--- Установка и возврат коэффициентов настроек свечи молот
   void              Hummer_coef1(const double hummer_coef1)         { m_hummer_coef1=hummer_coef1;   }
   void              Hummer_coef2(const double hummer_coef2)         { m_hummer_coef2=hummer_coef2;   }
   double            Hummer_coef1(void)                              { return(m_hummer_coef1);        }
   double            Hummer_coef2(void)                              { return(m_hummer_coef2);        }
   //--- Установка и возврат диапазона выборки для предустановленных паттернов
   void              Range(const int range_total)                    { m_range_total=range_total;     }
   int               Range(void)                                     { return(m_range_total);         }
   //--- Установка и возврат количества свечей для расчета периода определения тренда  
   void              TrendPeriod(const int period)                   { m_trend_period=period;         }
   int               TrendPeriod(void)                               { return(m_trend_period);        }

В конструкторе класса опишем параметры по умолчанию, как это задано в приложении во вкладках Настройки.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPattern::CPattern(void) : m_k1(1),
                           m_k2(0.5),
                           m_k3(0.25),
                           m_threshold_value(100),
                           m_long_coef(1.3),
                           m_short_coef(0.5),
                           m_doji_coef(0.04),
                           m_maribozu_coef(0.01),
                           m_spin_coef(1),
                           m_hummer_coef1(0.1),
                           m_hummer_coef2(2),
                           m_range_total(8000),
                           m_trend_period(5)
  {
  }

Во второй части публичной секции класса CPattern описаны методы обработки объявленных нами входных параметров и получение свойств и характеристик исследуемых паттернов.

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

CandleType

Возвращает тип выбранной свечи из существующих паттернов, перечисленных в списке TYPE_CANDLESTICK.
TYPE_CANDLESTICK  CandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int shift);

Параметры

Возвращаемое значение

Тип выбранной свечи из перечисления TYPE_CANDLESTICK.

//+------------------------------------------------------------------+
//| Тип свечи                                                        |
//+------------------------------------------------------------------+
enum TYPE_CANDLESTICK
  {
   CAND_NONE,           // Неопознанная
   CAND_MARIBOZU,       // Марибозу
   CAND_DOJI,           // Дожи
   CAND_SPIN_TOP,       // Волчки
   CAND_HAMMER,         // Молот
   CAND_INVERT_HAMMER,  // Перевернутый молот
   CAND_LONG,           // Длинная
   CAND_SHORT           // Короткая
  };

Реализация

//+------------------------------------------------------------------+
//| Возвращает тип выбранной свечи                                   |
//+------------------------------------------------------------------+
TYPE_CANDLESTICK CPattern::CandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int shift)
  {
   CANDLE_STRUCTURE res;
   if(GetCandleType(symbol,timeframe,res,shift))
      return(res.m_type);
   return(CAND_NONE);
  }
//+------------------------------------------------------------------+
//| Распознавание типа свечи                                         |
//+------------------------------------------------------------------+
bool CPattern::GetCandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,CANDLE_STRUCTURE &res,const int shift)
  {
   MqlRates rt[];
   int aver_period=m_trend_period;
   double aver=0;
   SymbolSelect(symbol,true);
   int copied=CopyRates(symbol,timeframe,shift,aver_period+1,rt);
//--- Получаем данные предыдущих свечей
   if(copied<aver_period)
      return(false);
//---
   res.m_open=rt[aver_period].open;
   res.m_high=rt[aver_period].high;
   res.m_low=rt[aver_period].low;
   res.m_close=rt[aver_period].close;

//--- Определяем направление тренда
   for(int i=0;i<aver_period;i++)
      aver+=rt[i].close;
   aver/=aver_period;

   if(aver<res.m_close)
      res.m_trend=UPPER;
   if(aver>res.m_close)
      res.m_trend=DOWN;
   if(aver==res.m_close)
      res.m_trend=FLAT;
//--- Определяем бычья свеча или медвежья
   res.m_bull=res.m_open<res.m_close;
//--- Получаем абсолютную величину тела свечи
   res.m_bodysize=MathAbs(res.m_open-res.m_close);
//--- Получаем размеры теней
   double shade_low=res.m_close-res.m_low;
   double shade_high=res.m_high-res.m_open;
   if(res.m_bull)
     {
      shade_low=res.m_open-res.m_low;
      shade_high=res.m_high-res.m_close;
     }
   double HL=res.m_high-res.m_low;
//--- Вычисляем средний размер тела предыдущих свечей
   double sum=0;
   for(int i=1; i<=aver_period; i++)
      sum=sum+MathAbs(rt[i].open-rt[i].close);
   sum=sum/aver_period;

//--- Определяем тип свечи   
   res.m_type=CAND_NONE;
//--- long 
   if(res.m_bodysize>sum*m_long_coef)
      res.m_type=CAND_LONG;
//--- sort 
   if(res.m_bodysize<sum*m_short_coef)
      res.m_type=CAND_SHORT;
//--- doji
   if(res.m_bodysize<HL*m_doji_coef)
      res.m_type=CAND_DOJI;
//--- maribozu
   if((shade_low<res.m_bodysize*m_maribozu_coef || shade_high<res.m_bodysize*m_maribozu_coef) && res.m_bodysize>0)
      res.m_type=CAND_MARIBOZU;
//--- hammer
   if(shade_low>res.m_bodysize*m_hummer_coef2 && shade_high<res.m_bodysize*m_hummer_coef1)
      res.m_type=CAND_HAMMER;
//--- invert hammer
   if(shade_low<res.m_bodysize*m_hummer_coef1 && shade_high>res.m_bodysize*m_hummer_coef2)
      res.m_type=CAND_INVERT_HAMMER;
//--- spinning top
   if(res.m_type==CAND_SHORT && shade_low>res.m_bodysize*m_spin_coef && shade_high>res.m_bodysize*m_spin_coef)
      res.m_type=CAND_SPIN_TOP;
//---
   ArrayFree(rt);
   return(true);
  }

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

PatternType

Распознает тип паттерна с выбранной свечи. Имеет 5 перегрузок метода из-за того, что аргументами могут быть как существующие паттерны из перечисления TYPE_PATTERN, так и сгенерированные первой, второй или третьей размерности. А также аргументом может быть массив паттернов из перечисления TYPE_PATTERN.

   //--- Распознавание типа паттерна
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,const int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,const int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,const int shift);
   //--- Распознавание набора паттернов
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN &pattern[],int shift);

Параметры

Указатель на массив существующих паттернов из списка TYPE_PATTERN.

//+------------------------------------------------------------------+
//| Тип паттерна                                                     |
//+------------------------------------------------------------------+
enum TYPE_PATTERN
  {
   NONE,
   HUMMER,
   INVERT_HUMMER,
   HANDING_MAN,
   SHOOTING_STAR,
   ENGULFING_BULL,
   ENGULFING_BEAR,
   HARAMI_BULL,
   HARAMI_BEAR,
   HARAMI_CROSS_BULL,
   HARAMI_CROSS_BEAR,
   DOJI_STAR_BULL,
   DOJI_STAR_BEAR,
   PIERCING_LINE,
   DARK_CLOUD_COVER
  };

Возвращаемое значение

Значение типа bool.

Реализация

Так как существует 5 видов реализации метода PatternType, то разберем его отдельно с различными аргументами. Первый из них ищет уже существующий любой из установленных паттернов из перечисления TYPE_PATTERN. Как видно ниже, для этого используется приватный метод CheckPattern.

//+------------------------------------------------------------------+
//| Распознавание предустановленного паттерна                        |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,const int shift)
  {
   if(CheckPattern(symbol,timeframe,shift)==pattern)
      return(true);
   return(false);
  }
//+------------------------------------------------------------------+
//| Проверяет и возвращает тип паттерна                              |
//+------------------------------------------------------------------+
TYPE_PATTERN CPattern::CheckPattern(const string symbol,const ENUM_TIMEFRAMES timeframe,int shift)
  {
   CANDLE_STRUCTURE cand1,cand2;
   TYPE_PATTERN pattern=NONE;
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   GetCandleType(symbol,timeframe,cand2,shift);                                           // предыдущая свеча
   GetCandleType(symbol,timeframe,cand1,shift-1);                                         // текущая свеча
//--- Перевернутый молот бычья модель
   if(cand2.m_trend==DOWN &&                                                              // проверяем направление тренда
      cand2.m_type==CAND_INVERT_HAMMER)                                                   // проверка "перевернутый молот"
      pattern=INVERT_HUMMER;
//--- Висельник медвежья модель
   else if(cand2.m_trend==UPPER && // проверяем направление тренда
      cand2.m_type==CAND_HAMMER) // проверка "молот"
      pattern=HANDING_MAN;
//--- Молот бычья модель
   else if(cand2.m_trend==DOWN && // проверяем направление тренда
      cand2.m_type==CAND_HAMMER) // проверка "молот"
      pattern=HUMMER;
//--- Падающая звезда медвежья модель
   else if(cand1.m_trend==UPPER && cand2.m_trend==UPPER && // проверяем направление тренда
      cand2.m_type==CAND_INVERT_HAMMER && cand1.m_close<=cand2.m_open) // проверка "перевернутый молот"
      pattern=SHOOTING_STAR;
//--- Поглощение бычья модель
   else if(cand1.m_trend==DOWN && cand1.m_bull && cand2.m_trend==DOWN && !cand2.m_bull && // проверяем направление тренда и направление свечи
      cand1.m_bodysize>cand2.m_bodysize &&
      cand1.m_close>=cand2.m_open && cand1.m_open<cand2.m_close)
      pattern=ENGULFING_BULL;
//--- Поглощение медвежья модель
   else if(cand1.m_trend==UPPER && cand1.m_bull && cand2.m_trend==UPPER && !cand2.m_bull && // проверяем направление тренда и направление свечи
      cand1.m_bodysize<cand2.m_bodysize &&
      cand1.m_close<=cand2.m_open && cand1.m_open>cand2.m_close)
      pattern=ENGULFING_BEAR;
//--- Крест Харами бычья модель
   else if(cand2.m_trend==DOWN && !cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // проверка "длинной" первой свечи и свечи доджи
      cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) // доджи внутри тела первой свечи
      pattern=HARAMI_CROSS_BULL;
//--- Крест Харами медвежья модель
   else if(cand2.m_trend==UPPER && cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // проверка "длинной" свечи и доджи
      cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) // доджи внутри тела первой свечи 
      pattern=HARAMI_CROSS_BEAR;
//--- Харами бычья модель
   else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // проверка "длинной" первой свечи
      cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && // вторая свеча не доджи и тело первой свечи больше тела второй
                    cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) // тело второй свечи внутри тела первой свечи 
                    pattern=HARAMI_BULL;
//--- Харами медвежья модель
   else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // проверка "длинной" первой свечи

      cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && // вторая свеча не доджи и тело первой свечи больше тела второй
                    cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) // тело второй свечи внутри тела первой свечи 
                    pattern=HARAMI_BEAR;
//--- Звезда доджи бычья модель
   else if(cand1.m_trend==DOWN && !cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // проверка 1 "длинной" свечи и 2 доджи
      cand1.m_close<=cand2.m_open) // открытие доджи ниже равно закрытию первой свечи 
      pattern=DOJI_STAR_BULL;
//--- Звезда доджи медвежья модель
   else if(cand1.m_trend==UPPER && cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // проверка 1 "длинной" свечи и 2 доджи
      cand1.m_open>=cand2.m_close) //открытие доджи выше равно закрытию первой свечи
      pattern=DOJI_STAR_BEAR;
//--- Просвет в облаках бычья модель
   else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // проверка "длинной" свечи
      cand1.m_close>(cand2.m_close+cand2.m_open)/2 && // закрытие второй выше середины первой
      cand2.m_open>cand1.m_close && cand2.m_close>=cand1.m_open)
      pattern=PIERCING_LINE;
//--- Завеса из темных облаков медвежья модель
   else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && // проверяем направление тренда и направление свечи
      (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // проверка "длинной" свечи
      cand1.m_close<(cand2.m_close+cand2.m_open)/2 && // close 2 ниже середины тела 1
      cand1.m_close<cand2.m_open && cand2.m_close<=cand1.m_open)
      pattern=DARK_CLOUD_COVER;
   return(pattern);
  }
//+------------------------------------------------------------------+

Следующий вид метода PatternType отличен от предыдущего лишь тем, что вместо аргумента тип искомого паттерна передается массив искомых паттернов:

//+------------------------------------------------------------------+
//| Распознавание из массива паттернов                               |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN &pattern[],int shift)
  {
   for(int i=0;i<ArraySize(pattern);i++)
     {
      if(CheckPattern(symbol,timeframe,shift)==pattern[i])
         return(true);
     }
   return(false);
  }

Далее рассмотрим реализацию метода PatternType для сгенерированных паттернов из простых типов свечей. Их, как было сказано чуть выше, три типа для размерности один, два и три. Рассмотрим их поочередно:

//+------------------------------------------------------------------+
//| Распознавание паттерна по индексу свечи                          |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,int shift)
  {
//--- Проверка на корректность индекса свечи
   if(index<0 || index>11)
      return(false);
//---
   CANDLE_STRUCTURE cand,cur_cand;
   RATING_SET ratings;
   ZeroMemory(cand);
   IndexToPatternType(cand,index);
//--- Получаем тип текущей свечи
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // текущая свеча
//---
   if(cur_cand.m_type==cand.m_type && cur_cand.m_bull==cand.m_bull)
      return(true);
   return(false);
  }

Это реализация метода для поиска паттерна размерностью один, он по сути своей чем-то схож по смыслу с методом CandleType(), однако передаваемые аргументы различны по типу и широте выбора. Также в реализации этого метода хотелось бы обратить внимание на неиспользуемый ранее приватный метод IndextoPatternType():

   //--- Преобразует индекс свечи в ее тип
   void              IndexToPatternType(CANDLE_STRUCTURE &res,int index);

Он преобразует индекс простого типа свечи в ее тип и передает это в заданную структуру.

Теперь рассмотрим метод для паттерна, состоящего из двух свечей простого типа:

//+------------------------------------------------------------------+
//| Распознавание паттерна по индексу свечи                          |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int shift)
  {
//--- Проверка на корректность индекса свечи
   if(index1<0 || index1>11 || index2<0 || index2>11)
      return(false);
//---
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand;
   RATING_SET ratings;
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   IndexToPatternType(cand1,index1);
   IndexToPatternType(cand2,index2);
//--- Получаем тип текущей свечи
   GetCandleType(symbol,timeframe,prev_cand,shift+1);                             // предыдущая свеча
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // текущая свеча
//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
      return(true);
   return(false);
  }

Программная реализации очень схожа с предыдущей, но в текущей реализации я хотел бы обратить внимание на то, что выбирая индекс свечи для анализа следует учитывать такую особенность: так как паттерн состоит из двух свечей, то тип свечи index1 будет для свечи смещенной на shift, а для index2 на shift+1. Такая же особенность касается и реализации метода для паттерна размерностью три:

//+------------------------------------------------------------------+
//| Распознавание паттерна по индексу свечи                          |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,int shift)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET ratings;
//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
//---
   IndexToPatternType(cand1,index1);
   IndexToPatternType(cand2,index2);
   IndexToPatternType(cand3,index3);

//--- Получаем тип текущей свечи
   GetCandleType(symbol,timeframe,prev_cand2,shift+2);                            // предыдущая свеча
   GetCandleType(symbol,timeframe,prev_cand,shift+1);                             // предыдущая свеча
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // текущая свеча
//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull && 
      prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
      return(true);
   return(false);
  }

Found

Возвращает количество найденных паттернов заданного типа. Имеет 3 перегрузки метода для существующих паттернов TYPE_PATTERN, а также для сгенерированных паттернов из простых типов свечей размерность 1-3.

   //--- Возвращает количество найденных паттернов заданного типа
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3);

Параметры

  • symbol  Выбранный символ для поиска.
  • timeframe  Выбранный таймфрейм.
  • patternТип существующего паттерна из списка TYPE_PATTERN.
  • index,index1,index2,index3 Индекс свечи простого типа(блок 4 рис.1).

Возвращаемое значение

Количество найденных паттернов заданного типа.

Реализация

Сама программная реализация данного метода достаточна проста. Основную работу по сбору статистики делает приватный метод PatternStat().

//+------------------------------------------------------------------+
//| Возвращает количество найденных паттернов заданного типа         |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| Возвращает количество найденных паттернов заданного типа         |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1)
  {
   PatternStat(symbol,timeframe,index1);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| Возвращает количество найденных паттернов заданного типа         |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| Возвращает количество найденных паттернов заданного типа         |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   return(m_found);
  }

Сам же метод PatternStat() имеет два вида, для существующих паттернов и для сгенерированных. Рассмотрим их подробнее. Первый из них для паттернов из перечисления TYPE_PATTERN:

//+------------------------------------------------------------------+
//| Получение статистики по заданному паттерну                       |
//+------------------------------------------------------------------+
CPattern::PatternStat(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
//---
   int pattern_counter=0;
//---
   RATING_SET pattern_coef={0,0,0,0,0,0};
//---
   for(int i=m_range_total;i>4;i--)
     {
      if(CheckPattern(symbol,timeframe,i)==pattern)
        {
         pattern_counter++;
         if(pattern==HUMMER || pattern==INVERT_HUMMER || pattern==HANDING_MAN)
            GetCategory(symbol,timeframe,pattern_coef,i-3);
         else
            GetCategory(symbol,timeframe,pattern_coef,i-4);
        }
     }
//---
   CoefCalculation(pattern_coef,pattern_counter);
  }

А второй для сгенерированных паттернов, составляемых посредством индексов простых типов свечей.

//+------------------------------------------------------------------+
//| Получение статистики по заданному паттерну                       |
//+------------------------------------------------------------------+
void CPattern::PatternStat(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2=0,int index3=0)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET rating={0,0,0,0,0,0};
   int pattern_total=0,pattern_size=1;
//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
   ZeroMemory(cur_cand);
   ZeroMemory(prev_cand);
   ZeroMemory(prev_cand2);
//---
   if(index2>0)
      pattern_size=2;
   if(index3>0)
      pattern_size=3;
//---
   if(pattern_size==1)
      IndexToPatternType(cand1,index1);
   else if(pattern_size==2)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
     }
   else if(pattern_size==3)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
      IndexToPatternType(cand3,index3);
     }
//---
   for(int i=m_range_total;i>5;i--)
     {
      if(pattern_size==1)
        {
         //--- Получаем тип текущей свечи
         GetCandleType(symbol,timeframe,cur_cand,i);                                       // текущая свеча
         //---
         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-3);
           }
        }
      else if(pattern_size==2)
        {
         //--- Получаем тип текущей свечи
         GetCandleType(symbol,timeframe,prev_cand,i);                                        // предыдущая свеча
         GetCandleType(symbol,timeframe,cur_cand,i-1);                                       // текущая свеча
         //---

         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
            prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-4);
           }
        }
      else if(pattern_size==3)
        {
         //--- Получаем тип текущей свечи
         GetCandleType(symbol,timeframe,prev_cand2,i);                                       // предыдущая свеча
         GetCandleType(symbol,timeframe,prev_cand,i-1);                                      // предыдущая свеча
         GetCandleType(symbol,timeframe,cur_cand,i-2);                                       // текущая свеча
         //---
         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
            prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull && 
            prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-5);
           }
        }
     }
//---
   CoefCalculation(rating,pattern_total);
  }

В обоих реализациях метода мы встречаем еще один, который ранее не встречался. Это приватный метод CoefCalculation():

   //--- Расчет коэффициентов 
   bool              CoefCalculation(RATING_SET &rate,int found);

Он обрабатывает все полученные результаты при поиске и тестировании паттерна. А именно в его аргументах находится указатель на структуру RATING_SET, отвечающую за сбор данных о результатах тестирования эффективности паттерна и второй — количество найденных паттернов. В самом методе  CoefCalculation() происходит расчет остальных показателей и свойств исследуемых паттернов, которые будут рассмотрены чуть ниже.

//+------------------------------------------------------------------+
//| Расчет коэффициентов оценки эффективности                        |
//+------------------------------------------------------------------+
bool CPattern::CoefCalculation(RATING_SET &rate,int found)
  {
   int sum1=0,sum2=0;
   sum1=rate.m_a_uptrend+rate.m_b_uptrend+rate.m_c_uptrend;
   sum2=rate.m_a_dntrend+rate.m_b_dntrend+rate.m_c_dntrend;
//---
   m_probability1=(found>0)?NormalizeDouble((double)sum1/found*100,2):0;
   m_probability2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0;
   m_efficiency1=(found>0)?NormalizeDouble((m_k1*rate.m_a_uptrend+m_k2*rate.m_b_uptrend+m_k3*rate.m_c_uptrend)/found,3):0;
   m_efficiency2=(found>0)?NormalizeDouble((m_k1*rate.m_a_dntrend+m_k2*rate.m_b_dntrend+m_k3*rate.m_c_dntrend)/found,3):0;
   m_found=found;
   m_coincidence=((double)found/m_range_total*100);
   return(true);
  }

Coincidence

Возвращает встречаемость заданного паттерна в процентах. Имеет 3 перегрузки метода для существующих паттернов TYPE_PATTERN, а также для сгенерированных паттернов из простых типов свечей размерность 1-3.

   //--- Возвращает встречаемость заданного паттерна
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3);

Параметры

  • symbolВыбранный символ для поиска.
  • timeframeВыбранный таймфрейм.
  • patternТип существующего паттерна из списка TYPE_PATTERN.
  • index,index1,index2,index3Индекс свечи простого типа(блок 4 рис.1).

Возвращаемое значение

Встречаемость заданного паттерна в процентах.

Реализация

Аналогична методу Found() разница лишь в том, что возвращается значение переменной m_coincidence.

//+------------------------------------------------------------------+
//| Возвращает встречаемость заданного паттерна                      |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| Возвращает встречаемость заданного паттерна                      |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1)
  {
   PatternStat(symbol,timeframe,index1);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| Возвращает встречаемость заданного паттерна                      |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| Возвращает встречаемость заданного паттерна                      |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   return(m_coincidence);
  }

Probability

Возращает вероятность движения после заданного паттерна  в процентах. Имеет 3 перегрузки метода для существующих паттернов TYPE_PATTERN, а также для сгенерированных паттернов из простых типов свечей размерность 1-3.

   //--- Возращает вероятность движения после заданного паттерна 
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend);

Параметры

  • symbolВыбранный символ для поиска.
  • timeframeВыбранный таймфрейм.
  • patternТип существующего паттерна из списка TYPE_PATTERN.
  • index,index1,index2,index 3Индекс свечи простого типа(блок 4 рис.1).

  • trendТип тренда TYPE_TREND
//+------------------------------------------------------------------+
//| Тип тренда                                                       |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //Восходящий
   DOWN,                //Нисходящий
   FLAT                 //Боковой
  };
//+------------------------------------------------------------------+

Возвращаемое значение

Вероятность движения после заданного паттерна  в процентах.

Реализация

Аналогична методу Found() разница лишь в том, что возвращается значение переменной m_probability1 или m_probability2 в зависимости от выбранного типа тренда.

//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }

Efficiency

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

   //--- Возращает коэффициент эффективности заданного паттерна 
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend);

Параметры

  • symbolВыбранный символ для поиска.
  • timeframeВыбранный таймфрейм.
  • patternТип существующего паттерна из списка TYPE_PATTERN.
  • index,index1,index2,index3Индекс свечи простого типа(блок 4 рис.1).
  • trendТип тренда TYPE_TREND
//+------------------------------------------------------------------+
//| Тип тренда                                                       |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //Восходящий
   DOWN,                //Нисходящий
   FLAT                 //Боковой
  };
//+------------------------------------------------------------------+

Возвращаемое значение

Коэффициент эффективности заданного паттерна.

Реализация

Аналогична методу Found() разница лишь в том, что возвращается значение переменной m_efficiency1 или m_efficiency2 в зависимости от выбранного типа тренда.

//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//|Возращает вероятность движения после заданного паттерна           |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }

Практическое применение

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

CandleDetector

Первым реализуем индикатор, который будет показывать на графике свечу заданного нами типа из перечисления TYPE_CANDLESTICK. Создадим папку Pattern в папке Indicators. В ней создадим файл с именем CandleDetector.mq5 и в нем приступим к созданию индикатора. Подключим библиотеку Pattern.mqh для доступа работы с паттернами. А также настроим первоначальные свойства будущего индикатора:

//+------------------------------------------------------------------+
//|                                               CandleDetector.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                           https://www.mql5.com/ru/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/users/alex2356"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#include <Pattern/Pattern.mqh>
//+----------------------------------------------+
//|  Параметры отрисовки индикатора              |
//+----------------------------------------------+
//---- отрисовка индикатора в виде значка
#property indicator_type1   DRAW_ARROW
//---- толщина линии индикатора
#property indicator_width1  1

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

//+----------------------------------------------+
//| Входные параметры индикатора                 |
//+----------------------------------------------+
input TYPE_CANDLESTICK  CandleType=1;                 // Тип свечи
input color             LabelColor=clrCrimson;
input double            LongCoef=1.3;
input double            ShortCoef=0.5;
input double            DojiCoef=0.04;
input double            MaribozuCoef=0.01;
input double            SpinCoef=1;
input double            HummerCoef1=0.1;
input double            HummerCoef2=2;
input int               TrendPeriod=5; 

Далее в инициализации настроим внешний вид индикатора, а также определим для поиска индикатора все значения из входных параметров.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- инициализация переменных начала отсчета данных
   min_rates_total=TrendPeriod+1;
//---- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//---- превращение динамического массива Signal[] в индикаторный буфер
   SetIndexBuffer(0,Signal,INDICATOR_DATA);
//---- осуществление сдвига начала отсчета отрисовки индикатора 1
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,min_rates_total);
//---- индексация элементов в буферах, как в таймсериях   
   ArraySetAsSeries(Signal,true);
//---- установка значений индикатора, которые не будут видимы на графике
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//---- символ для индикатора
   PlotIndexSetInteger(0,PLOT_ARROW,108);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,LabelColor);
//----
   Pat.Long_coef(LongCoef);
   Pat.Short_coef(ShortCoef);
   Pat.Doji_coef(DojiCoef);
   Pat.Maribozu_coef(MaribozuCoef);
   Pat.Spin_coef(SpinCoef);
   Pat.Hummer_coef1(HummerCoef1);
   Pat.Hummer_coef2(HummerCoef2);
   Pat.TrendPeriod(TrendPeriod);
   return(INIT_SUCCEEDED);
  }

В расчетной части индикатора стоит обратить внимание лишь на используемый метод CandleType() для поиска заданного типа свечи на графике.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---- проверка количества баров на достаточность для расчета
   if(rates_total<min_rates_total)
      return(0);
//---- объявления локальных переменных 
   int limit,bar;
//---- индексация элементов в массивах, как в таймсериях  
   ArraySetAsSeries(low,true);
//---- расчет стартового номера first для цикла пересчета баров
   if(prev_calculated>rates_total || prev_calculated<=0)          // проверка на первый старт расчета индикатора
      limit=rates_total-min_rates_total;                          // стартовый номер для расчета всех баров
   else
      limit=rates_total-prev_calculated;                          // стартовый номер для расчета новых баров
//---- основной цикл расчета индикатора
   for(bar=limit; bar>=0; bar--)
     {
      Signal[bar]=0.0;
      if(Pat.CandleType(Symbol(),PERIOD_CURRENT,bar)==CandleType)
         Signal[bar]=low[bar]-200*_Point;
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

Пример работы данного индикатора представлен ниже на рис.3( поиск типа свечи Длинная).

Рис.3 Пример работы индикатора CandleDetector.

PatternDetector

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

//+----------------------------------------------+
//| Входные параметры индикатора                 |
//+----------------------------------------------+
input TYPE_PATTERN      PatternType=1;                // Тип паттерна
input color             LabelColor=clrCrimson;
input double            LongCoef=1.3;
input double            ShortCoef=0.5;
input double            DojiCoef=0.04;
input double            MaribozuCoef=0.01;
input double            SpinCoef=1;
input double            HummerCoef1=0.1;
input double            HummerCoef2=2;
input int               TrendPeriod=5;
//---
CPattern Pat;
double Signal[];
//---- объявление целочисленных переменных начала отсчета данных
int min_rates_total;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//---- инициализация переменных начала отсчета данных
   min_rates_total=TrendPeriod+2;
//---- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//---- превращение динамического массива SignUp [] в индикаторный буфер
   SetIndexBuffer(0,Signal,INDICATOR_DATA);
//---- осуществление сдвига начала отсчета отрисовки индикатора 1
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,min_rates_total);
//---- индексация элементов в буферах, как в таймсериях   
   ArraySetAsSeries(Signal,true);
//---- установка значений индикатора, которые не будут видимы на графике
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//---- символ для индикатора
   PlotIndexSetInteger(0,PLOT_ARROW,108);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,LabelColor);
//----
   Pat.Long_coef(LongCoef);
   Pat.Short_coef(ShortCoef);
   Pat.Doji_coef(DojiCoef);
   Pat.Maribozu_coef(MaribozuCoef);
   Pat.Spin_coef(SpinCoef);
   Pat.Hummer_coef1(HummerCoef1);
   Pat.Hummer_coef2(HummerCoef2);
   Pat.TrendPeriod(TrendPeriod);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---- проверка количества баров на достаточность для расчета
   if(rates_total<min_rates_total)
      return(0);
//---- объявления локальных переменных 
   int limit,bar;
//---- индексация элементов в массивах, как в таймсериях  
   ArraySetAsSeries(low,true);
//---- расчет стартового номера first для цикла пересчета баров
   if(prev_calculated>rates_total || prev_calculated<=0)       // проверка на первый старт расчета индикатора
      limit=rates_total-min_rates_total;                       // стартовый номер для расчета всех баров
   else
      limit=rates_total-prev_calculated;                       // стартовый номер для расчета новых баров
//---- основной цикл расчета индикатора
   for(bar=limit; bar>0; bar--)
     {
      Signal[bar]=0.0;
      if(Pat.PatternType(_Symbol,_Period,PatternType,bar))
         Signal[bar]=low[bar]-200*_Point;
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

Результат работы индикатора PatternDetector представлен на рис.4(поиск паттерна Поглощение - бычья модель).

Рис.4 Пример работы индикатора PatternDetector.

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

Для начала создадим в папке Experts создадим папку Pattern и в ней файл PatternExpert.mq5, в который мы будем писать код будущего торгового эксперта. На первом этапе подключим библиотеку для работы с паттернами Pattern.mqh, а также библиотеку торговых операций Trade.mqh. Объявим экземпляры классов и введем перечисление PATTERN_MODE, необходимое для переключения установки паттернов с существующих на сгенерированные.

//+------------------------------------------------------------------+
//|                                                PatternExpert.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                           https://www.mql5.com/ru/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/users/alex2356"
#property version   "1.00"
#include <Pattern/Pattern.mqh>
#include "Trade.mqh" 
CTradeBase Trade;
CPattern Pat;
//+------------------------------------------------------------------+
//| Режим поиска паттернов                                           |
//+------------------------------------------------------------------+
enum PATTERN_MODE
  {
   EXISTING,
   GENERATED
  };

Теперь определимся со входными параметрами торгового эксперта. В первом блоке будут параметры эксперта:

//+------------------------------------------------------------------+
//| Входные параметры эксперта                                       |
//+------------------------------------------------------------------+
input    string               Inp_EaComment="Pattern Strategy";         // EA Comment
input    double               Inp_Lot=0.01;                             // Lot
input    MarginMode           Inp_MMode=LOT;                            // Money Management

//--- Параметры эксперта
input    string               Inp_Str_label="===EA parameters===";      // Label
input    int                  Inp_MagicNum=1111;                        // Magic number
input    int                  Inp_StopLoss=40;                          // Stop Loss(points)
input    int                  Inp_TakeProfit=30;                        // Take Profit(points)

Во второй части параметры для настроек и торговли. 

//--- Параметры торговли
input ENUM_TIMEFRAMES         Timeframe=PERIOD_CURRENT;                 // Current Timefrate
input PATTERN_MODE            PatternMode=0;                            // Pattern Mode
input TYPE_PATTERN            BuyPatternType=ENGULFING_BULL;            // Buy Pattern Type
input TYPE_PATTERN            SellPatternType=ENGULFING_BEAR;           // Sell Pattern Type
input uint                    BuyIndex1=1;                              // BuyIndex of simple candle1
input uint                    BuyIndex2=0;                              // BuyIndex of simple candle2
input uint                    BuyIndex3=0;                              // BuyIndex of simple candle3
input uint                    SellIndex1=1;                             // SellIndex of simple candle1
input uint                    SellIndex2=0;                             // SellIndex of simple candle2
input uint                    SellIndex3=0;                             // SellIndex of simple candle3
input double                  LongCoef=1.3;                             // Long candle coef
input double                  ShortCoef=0.5;                            // Short candle coef
input double                  DojiCoef=0.04;                            // Doji candle coef
input double                  MaribozuCoef=0.01;                        // Maribozu candle coef
input double                  SpinCoef=1;                               // Spin candle coef
input double                  HummerCoef1=0.1;                          // Hummer candle coef1
input double                  HummerCoef2=2;                            // Hummer candle coef2
input int                     TrendPeriod=5;                            // Trend Period

Рассмотрим некоторые из них более подробно:

  • Current Timeframe — Выбранный рабочий таймфрейм. Удобно при оптимизации. По умолчанию — Текущий на графике.
  • Pattern Mode — Режим выбора паттернов. EXISTING — существующие паттерны, при этой опции работают следующие две настройки Buy Pattern Type и Sell Pattern Type. GENERATED - сгенерированные паттерны. С этой опцией Buy/Sell Pattern Type игнорируются и используются настройки BuyIndex1-3 и SellIndex1-3.
  • Buy Pattern Type/ Sell PAtternType — выбор паттерна при появлении которого будет открыта длинная/короткая позиция. 
  • BuyIndex1-3/SellIndex1-3 — выбор паттерна составленного из простых типов свечей(рис.1 Блок 4) при появлении которого будет открыта длинная/короткая позиция. 

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Проверка на наличие подключения к торговому серверу
   if(!TerminalInfoInteger(TERMINAL_CONNECTED))
     {
      Print(Inp_EaComment,": No Connection!");
      return(INIT_FAILED);
     }
//--- Проверка на разрешение автоторговли
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      Print(Inp_EaComment,": Trade is not allowed!");
      return(INIT_FAILED);
     }
//---
   Pat.TrendPeriod(TrendPeriod);
//---
   return(INIT_SUCCEEDED);
  }

В  расчетной части всё достаточно просто, но все же рассмотрим как работают функции поиска сигнала на продажу и на покупку.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!Trade.IsOpenedByMagic(Inp_MagicNum))
     {
      //--- Открытие ордера при наличии сигнала на покупку
      if(BuySignal())
         Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
      //--- Открытие ордера при наличии сигнала на продажу
      if(SellSignal())
         Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
     }
  }

Обе функции аналогичны, поэтому рассмотрим одну из них BuySignal() — поиск сигнала на покупку.

//+------------------------------------------------------------------+
//| Условия на покупку                                               |
//+------------------------------------------------------------------+
bool BuySignal()
  {
   if(PatternMode==0)
     {
      if(BuyPatternType==NONE)
         return(false);
      if(Pat.PatternType(_Symbol,Timeframe,BuyPatternType,1))
         return(true);
     }
   else if(PatternMode==1)
     {
      if(BuyIndex1>0 && BuyIndex2==0 && BuyIndex3==0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,1))
            return(true);
        }
      else if(BuyIndex1>0 && BuyIndex2>0 && BuyIndex3==0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,1))
            return(true);
        }
      else if(BuyIndex1>0 && BuyIndex2>0 && BuyIndex3>0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,BuyIndex3,1))
            return(true);
        }
     }
   return(false);
  }

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

Теперь проведем тестирование и оптимизацию полученного торгового советника в двух режимах — паттернов из перечисления TYPE_PATTERN и с помощью сгенерированных паттернов из свечей простого типа всё на том же рис.1 в четвертом блоке.

Для тестировании получившегося торгового эксперта выберем начальные предустановки:

  • Интервал: Для режима Uptrend 01.01.2018 — 15.03.2018.
  • Валютная пара: EURUSD.
  • Режим торговли: Без задержки. Представленные стратегии не относятся к высокочастотным, поэтому влияние задержек очень мало.
  • Тестирование: OHLC на М1. 
  • Начальный депозит: 1000 USD.
  • Плечо: 1:500.
  • Сервер: MetaQuotes-Demo.
  • Котировки: 5-значные.

Режим сгенерированных паттернов.

Определимся с параметрами, которые будут тестироваться и оптимизироваться.

Рис.5 Набор оптимизируемых параметров в режиме сгенерированных паттернов.

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

Рис.6 Результаты тестирования лучших параметров в режиме сгенерированных паттернов.

Режим существующих паттернов.

Установим параметры тестирования и оптимизации.

Рис.7 Набор оптимизируемых параметров в режиме существующих паттернов. 

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

Рис.8 Результаты тестирования лучших параметров в режиме существующих паттернов.


Заключение

В конце статьи приложен архив со всеми перечисленными файлами, отсортированными по папкам. Поэтому для корректной работы достаточно положить папку MQL5 в корень терминала. Для того, чтобы найти корень терминала, в котором находится папка MQL5 нужно в MetaTarder5 нажать комбинацию клавиш Ctrl+Shift+D или воспользоваться контекстным меню, как показано на рис.9 ниже.

Рис.9 Поиск папки MQL5 в корне терминала MetaTrader5.

Программы, используемые в статье:

#
 Имя
Тип
Описание
1
Pattern.mqh Библиотека  Библиотека для работы с паттернами
2 CandleDetector.mq5 Индикатор  Индикатор поиска свечей
3 PatternDetector.mq5 Индикатор  Индикатор поиска паттернов
4 PatternExpert.mq5  Эксперт   Торговый эксперт для работы с паттернами
5 Trade.mqh Библиотека  Класс торговых функций