![Исследование методов свечного анализа (Часть III): Библиотека работы с паттернами](https://c.mql5.com/i/articles/overlay.png)
Исследование методов свечного анализа (Часть III): Библиотека работы с паттернами
Содержание
- Введение
- Структура библиотеки
- Разработка библиотеки
- CandleType. Тип выбранной свечи
- PatternType. Тип паттерна
- Found. Количество найденных паттернов заданного типа
- Coincidence. Встречаемость паттерна заданного типа
- Probability. Вероятность движения после появления заданного паттерна
- Efficiency. Коэффициент эффективности заданного паттерна
- Практическое применение
- CandleDetector. Индикатор поиска свечей
- PatternDetector. Индикатор поиска паттернов
- PatternExpert. Торговый эксперт работающий с паттернами
- Заключение
Введение
Рассмотренные ранее методы свечного анализа имели исследовательский характер, целью которых в первой статье было проверить насколько действенны в сегодняшних реалиях уже существующие паттерны, а во второй статье расширить границы исследования этого метода анализа рынка. Разработанные критерии оценки позволили изучить, протестировать и сравнить достаточно широкий пласт возможных комбинаций паттернов. Для этого была разработано пользовательское приложение 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 CandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int shift);
Параметры
- symbol — Выбранный символ для поиска
- timeframe — Выбранный таймфрейм
- shift — Индекс выбранной для анализа свечи, начиная с 0.
Возвращаемое значение
Тип выбранной свечи из перечисления 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);
Параметры
- symbol — Выбранный символ для поиска.
- timeframe — Выбранный таймфрейм.
- pattern,pattern[] — Тип существующего паттерна из списка TYPE_PATTERN.
Указатель на массив существующих паттернов из списка 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 };
- index,index1,index2,index3 — Индекс свечи простого типа(блок 4 рис.1).
- shift — Индекс выбранной для начала анализа свечи, начиная с 0.
Возвращаемое значение
Значение типа 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 | Библиотека | Класс торговых функций |
![Как за 10 минут написать DLL библиотеку на MQL5 (Часть II): Пишем в среде Visual Studio 2017](https://c.mql5.com/2/35/logo__2.png)
![Цветная оптимизация торговых стратегий](https://c.mql5.com/2/35/avatar-colorful.png)
![Визуализация истории мультивалютной торговли по отчетам в форматах HTML и CSV](https://c.mql5.com/2/35/mql5-article-html-csv.png)
![MTF-индикаторы как инструмент технического анализа](https://c.mql5.com/2/35/mtf-avatar.png)
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Очень ждал выхода этой статьи, но всё равно прозевал )
Статья интересная, очень познавательная и доходчиво написана. Спасибо.
Теперь о том, что хотелось бы увидеть в следующей статье, если вы захотите её написать.
Марибозу считается не правильно или я чего то недопонимаю.
На мой взгляд искать свечи по одной не очень удобно поэтому реализовал это немного по другому
Не понятно вот это " устанавливаем значение Trend Period, которое влияет на определение паттернов на графике." На что влияет этот параметр?
Советник, торгует только один сгенерированный паттерн, это тоже не очень удобно. Было бы хорошо, если бы в советнике была возможность торговли несколько сгенерированных паттернов, а если бы это было оформлено в графическом интерфейсе как PatternAnalyzer было бы вообще супер.
Ещё раз, спасибо за статью.
Очень ждал выхода этой статьи, но всё равно прозевал )
Статья интересная, очень познавательная и доходчиво написана. Спасибо.
Теперь о том, что хотелось бы увидеть в следующей статье, если вы захотите её написать.
Марибозу считается не правильно или я чего то недопонимаю.
На мой взгляд искать свечи по одной не очень удобно поэтому реализовал это немного по другому
Советник, торгует только один сгенерированный паттерн, это тоже не очень удобно. Было бы хорошо, если бы в советнике была возможность торговли несколько сгенерированных паттернов, а если бы это было оформлено в графическом интерфейсе как PatternAnalyzer было бы вообще супер.
Ещё раз, спасибо за статью.
Спасибо за отклик. Материал для следующей части я не продумывал, наверно потому что в три уместились все те идеи, которые у меня были по паттернами. Насчет Марибозу, я посмотрю в чем может быть загвоздка.
А советник, торгующий по нескольким сгенерированным паттернами. Так там и так для покупки и продажи свой отдельный паттерн можно поставить.
Очень ждал выхода этой статьи, но всё равно прозевал )
Статья интересная, очень познавательная и доходчиво написана. Спасибо.
Не понятно вот это " устанавливаем значение Trend Period, которое влияет на определение паттернов на графике." На что влияет этот параметр?
На определение типа простых свечей. Сами понимаете что понятие длинная и короткая свеча, к примеру, на минутном таймфрейме и на дневном понятия очень разные. Trend period как определяет размер свечей адаптивно данному участку графика, так и определяет направление движения цены для определенных паттернов.
Насчет Марибозу, я посмотрю в чем может быть загвоздка.
Там просто в коде в MainWindow.mqh вместо "или" надо поставить "и".
(shade_low<bodysize*maribozu_coef || shade_high<bodysize*maribozu_coef) && bodysize>0 && bull
Там просто в коде в MainWindow.mqh вместо "или" надо поставить "и".
В четвертой части подправим) Спасибо.