Генератор торговых сигналов пользовательского индикатора

Vladimir Karputov | 19 июля, 2013

Введение

В этой статье я расскажу, как создать генератор торговых сигналов на основе пользовательского индикатора. Покажу, как написать свою торговую модель для пользовательского индикатора. Также объясню значение модели "model 0" и зачем в модуле торговых сигналов используются конструкции вида "IS_PATTERN_USAGE(0)".

В статье постоянно будут идти два вида кода: код, который мы только хотим изменить и код, который мы уже изменили. Измененный код будет выделяться цветом таким образом:

//+------------------------------------------------------------------+
//|                                                     MySignal.mqh |
//|                              Copyright © 2012, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+

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

 

1. Пользовательский индикатор

Я уверен, что у Вас уже давно есть на примете индикатор не из стандартной поставки. И на базе именно этого индикатора Вы бы хотели построить модуль торговых сигналов. В качестве такого пользовательского индикатора я возьму индикатор MACD из стандартной поставки. Путь к этому индикатору следующий: ...MQL5\Indicators\Examples\MACD.mq5

Каждый индикатор может описывать одну или несколько рыночных моделей. Рыночная модель - определенное сочетание значения индикатора и значения цены. У индикатора MACD существуют следующие модели: разворот,  пересечение основной и сигнальной линии, пересечение нулевого уровня, дивергенция и двойная дивергенция.

1.1 Новая модель для индикатора.

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

Рисунок 1: Модель возможного роста индикатора 

Рисунок 1: Модель возможного роста индикатора

если индикатор MACD находится выше нулевой линии и при этом его значения уменьшаются – значит можно предположить дальнейшее снижение и можно открывать короткую позицию:

 Рисунок 2: Модель возможного снижения индикатора

Рисунок 2: Модель возможного снижения индикатора 

Итак, мы определились с выбором пользовательского индикатора. Придумали и описали новую торговую модель индикатора. Начнем писать код.

 

2. Пишем генератор торговых сигналов своего пользовательского индикатора

Наш генератор является наследником от базового класса CExpertSignal. Базовый класс CExpertSignal - это класс для создания генераторов торговых сигналов. Класс CExpertSignal содержит набор публичных (т.е. доступных извне) методов. Опросив эти методы, эксперт может узнать мнение генератора торговых сигналов по поводу входа в рынок в ту или другую сторону.

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

 

3. Создаем класс генератора торговых сигналов

По умолчанию генератор торговых сигналов должен находиться в папке ...MQL5\Include\Expert\Signal. Чтобы не засорять папку ...\Signal стандартной библиотеки создадим в директории ...\Expert свою папку \MySignals:

Рисунок 3. Создаем свою папку MySignals  

Рисунок 3. Создаем свою папку MySignals 

Следующий шаг - создадим включаемый файл с помощью Мастера MQL5. Выберете в MetaEditor меню Файл пункт "Создать" и отметьте пункт "Включаемый файла (*.mqh)".

Рисунок 4. Мастер MQL5. Создаем включаемый файл 

Рисунок 4. Мастер MQL5. Создаем включаемый файл

Назовем наш класс генератора сигналов MySignal. Путь к нашему классу будет следующий: Include\Expert\MySignals\MySignal. Укажем это:

Рисунок 5. Мастер MQL5. Расположение включаемого файла 

Рисунок 5. Мастер MQL5. Расположение включаемого файла

После нажатия кнопки "Готово" Мастер MQL5 сгенерирует пустой шаблон. Начиная с этого момента дальше мы будем работать ручками и пользоваться Copy-Paste. Хочу обратить Ваше внимание, что внутреннее содержание всех сигналов из Стандартной библиотеки практически идентично. Различия только в различных алгоритмах определения торговых моделей.

Поэтому я советую брать любой файл сигнала из папки  \Include\Expert\Signal, скопировать все его содержание, затем вставить в свой шаблон. После этого начать редактировать полученный файл генератора торговых сигналов.

 

4. Описание нашего класса генератора торговых сигналов

В качестве шаблона я взял файл \Include\Expert\Signal\SignalEnvelopes.mqh.  Выделил всё, кроме шапки:
//+------------------------------------------------------------------+
//|                                              SignalEnvelopes.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+

и вставил в наш практически пустой шаблон MySignal.mqh. Вот, что получилось: 

//+------------------------------------------------------------------+
//|                                                     MySignal.mqh |
//|                              Copyright © 2012, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals of indicator 'Envelopes'                           |
//| Type=SignalAdvanced                                              |
//| Name=Envelopes                                                   |
//| ShortName=Envelopes                                              |
//| Class=CSignalEnvelopes                                           |
//| Page=signal_envelopes                                            |
//| Parameter=PeriodMA,int,45,Period of averaging                    |
//| Parameter=Shift,int,0,Time shift                                 |
//| Parameter=Method,ENUM_MA_METHOD,MODE_SMA,Method of averaging     |
//| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series   |
//| Parameter=Deviation,double,0.15,Deviation                        |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CSignalEnvelopes.                                          |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'Envelopes' indicator.                              |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+
class CSignalEnvelopes : public CExpertSignal
  {
protected:
   CiEnvelopes       m_env;            // object-indicator
   //--- adjusted parameters
   int               m_ma_period;      // the "period of averaging" parameter of the indicator
   int               m_ma_shift;       // the "time shift" parameter of the indicator
   ENUM_MA_METHOD    m_ma_method;      // the "method of averaging" parameter of the indicator
   ENUM_APPLIED_PRICE m_ma_applied;    // the "object of averaging" parameter of the indicator
   double            m_deviation;      // the "deviation" parameter of the indicator
   double            m_limit_in;       // threshold sensitivity of the 'rollback zone'
   double            m_limit_out;      // threshold sensitivity of the 'break through zone'
   //--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0 "price is near the necessary border of the envelope"
   int               m_pattern_1;      // model 1 "price crossed a border of the envelope"

public:
                     CSignalEnvelopes(void);
                    ~CSignalEnvelopes(void);
   //--- methods of setting adjustable parameters
   void              PeriodMA(int value)                 { m_ma_period=value;        }
   void              Shift(int value)                    { m_ma_shift=value;         }
   void              Method(ENUM_MA_METHOD value)        { m_ma_method=value;        }
   void              Applied(ENUM_APPLIED_PRICE value)   { m_ma_applied=value;       }
   void              Deviation(double value)             { m_deviation=value;        }
   void              LimitIn(double value)               { m_limit_in=value;         }
   void              LimitOut(double value)              { m_limit_out=value;        }
   //--- methods of adjusting "weights" of market models
   void              Pattern_0(int value)                { m_pattern_0=value;        }
   void              Pattern_1(int value)                { m_pattern_1=value;        }
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking if the market models are formed
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);

protected:
   //--- method of initialization of the indicator
   bool              InitMA(CIndicators *indicators);
   //--- methods of getting data
   double            Upper(int ind)                      { return(m_env.Upper(ind)); }
   double            Lower(int ind)                      { return(m_env.Lower(ind)); }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSignalEnvelopes::CSignalEnvelopes(void) : m_ma_period(45),
                                           m_ma_shift(0),
                                           m_ma_method(MODE_SMA),
                                           m_ma_applied(PRICE_CLOSE),
                                           m_deviation(0.15),
                                           m_limit_in(0.2),
                                           m_limit_out(0.2),
                                           m_pattern_0(90),
                                           m_pattern_1(70)
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSignalEnvelopes::~CSignalEnvelopes(void)
  {
  }
//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//+------------------------------------------------------------------+
bool CSignalEnvelopes::ValidationSettings(void)
  {
//--- validation settings of additional filters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data checks
   if(m_ma_period<=0)
     {
      printf(__FUNCTION__+": period MA must be greater than 0");
      return(false);
     }
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool CSignalEnvelopes::InitIndicators(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- create and initialize MA indicator
   if(!InitMA(indicators))
      return(false);
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialize MA indicators.                                        |
//+------------------------------------------------------------------+
bool CSignalEnvelopes::InitMA(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- add object to collection
   if(!indicators.Add(GetPointer(m_env)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- initialize object
   if(!m_env.Create(m_symbol.Name(),m_period,m_ma_period,m_ma_shift,m_ma_method,m_ma_applied,m_deviation))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalEnvelopes::LongCondition(void)
  {
   int result=0;
   int idx   =StartIndex();
   double close=Close(idx);
   double upper=Upper(idx);
   double lower=Lower(idx);
   double width=upper-lower;
//--- if the model 0 is used and price is in the rollback zone, then there is a condition for buying
   if(IS_PATTERN_USAGE(0) && close<lower+m_limit_in*width && close>lower-m_limit_out*width)
      result=m_pattern_0;
//--- if the model 1 is used and price is above the rollback zone, then there is a condition for buying
   if(IS_PATTERN_USAGE(1) && close>upper+m_limit_out*width)
      result=m_pattern_1;
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalEnvelopes::ShortCondition(void)
  {
   int result  =0;
   int idx     =StartIndex();
   double close=Close(idx);
   double upper=Upper(idx);
   double lower=Lower(idx);
   double width=upper-lower;
//--- if the model 0 is used and price is in the rollback zone, then there is a condition for selling
   if(IS_PATTERN_USAGE(0) && close>upper-m_limit_in*width && close<upper+m_limit_out*width)
      result=m_pattern_0;
//--- if the model 1 is used and price is above the rollback zone, then there is a condition for selling
   if(IS_PATTERN_USAGE(1) && close<lower-m_limit_out*width)
      result=m_pattern_1;
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+

Обратите внимание на строку 6:

#include <Expert\ExpertSignal.mqh>

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

Продолжаем редактирование шаблона. Для того чтобы наш шаблон впоследствии был виден для Мастера MQL5 необходимо изменить описание нашего класса:

//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals of indicator 'Envelopes'                           |
//| Type=SignalAdvanced                                              |
//| Name=Envelopes                                                   |
//| ShortName=Envelopes                                              |
//| Class=CSignalEnvelopes                                           |
//| Page=signal_envelopes                                            |
//| Parameter=PeriodMA,int,45,Period of averaging                    |
//| Parameter=Shift,int,0,Time shift                                 |
//| Parameter=Method,ENUM_MA_METHOD,MODE_SMA,Method of averaging     |
//| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series   |
//| Parameter=Deviation,double,0.15,Deviation                        |
//+------------------------------------------------------------------+

Итак, по порядку. Строка

//| Title=Signals of indicator 'Envelopes'                           |

 означает под каким названием будет виден наш класс сигналов в Мастере MQL5. Изменим это название, примерно так:

//| Title=Сигнал индикатора 'Пользовательский индикатор'             |

Следующая строка: 

//| Name=Envelopes                                                   |

означает имя для описания переменных нашего класса торговых сигналов. Это описание будет применяться Мастером MQL5. Отредактируем строку и получим:

//| Name=MyCustomIndicator                                           |

Следующая строка:

//| ShortName=Envelopes                                              |

Дадим этому параметру такое же имя:

//| ShortName=MyCustomIndicator                                      |

В следующей строке задаётся имя класса:

//| Class=CSignalEnvelopes                                           |

Переименуем этот параметр:

//| Class=CSignalMyCustInd                                           |

Следующий параметр оставляем без изменения.

//| Page=signal_envelopes                                            |

Следующая группа параметров отвечает за описание параметров индикатора, на основании которого создается генератор торговых сигналов. Как я говорил раньше, в качестве пользовательского индикатора я буду использовать индикатор ...MQL5\Indicators\Examples\MACD.mq5. Этот индикатор имеет такие параметры:

//--- input parameters
input int                InpFastEMA=12;               // Fast EMA period
input int                InpSlowEMA=26;               // Slow EMA period
input int                InpSignalSMA=9;              // Signal SMA period
input ENUM_APPLIED_PRICE  InpAppliedPrice=PRICE_CLOSE; // Applied price

4.1 Блок описания параметров 

Обращаю внимание, что данные параметры я привожу именно для пользовательского индикатора  MACD.mq5. У Вашего пользовательского индикатора могут быть совершено другие параметры. Главное - это соблюсти соответствие параметров индикатора их описанию в классе торговых сигналов. Применительно к рассматриваемому пользовательскому индикатору MACD.mq5 >блок описания параметров в классе торговых сигналов будет иметь такой вид:

//| Parameter=PeriodFast,int,12,Period of fast EMA                   |
//| Parameter=PeriodSlow,int,24,Period of slow EMA                   |
//| Parameter=PeriodSignal,int,9,Period of averaging of difference   |
//| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series   |

Посмотрите на соответствие параметров в индикаторе и теперь в блоке описаний класса. После всех изменений блок описаний нашего класса будет выглядеть так:

//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Сигнал индикатора 'Пользовательский индикатор'             |
//| Type=SignalAdvanced                                              |
//| Name=MyCustomIndicator                                           |
//| ShortName=MyCustomIndicator                                      |
//| Class=CSignalMyCustInd                                           |
//| Page=signal_envelopes                                            |
//| Parameter=PeriodFast,int,12,Period of fast EMA                   |
//| Parameter=PeriodSlow,int,24,Period of slow EMA                   |
//| Parameter=PeriodSignal,int,9,Period of averaging of difference   |
//| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series   |
//+------------------------------------------------------------------+

В программировании считается хорошим тоном комментировать свой код - так будет легче разобраться в коде через некоторое время. Поэтому изменим следующий блок:

//+------------------------------------------------------------------+
//| Class CSignalEnvelopes.                                          |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'Envelopes' indicator.                              |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+

на описание применительно к нашему классу:

//+------------------------------------------------------------------+
//| Class CSignalMyCustInd.                                          |
//| Purpose: Класс генератора торговых сигналов на основе            |
//|          пользовательского индикатора.                           |
//| Является производным от класса CExpertSignal.                    |
//+------------------------------------------------------------------+

Для того чтобы не запутаться в коде следует произвести замену всех значений "CSignalEnvelopes" на "CSignalMyCustInd"

Рисунок 6. Замена CSignalEnvelopes на CSignalMyCustInd 

Рисунок 6. Замена CSignalEnvelopes на CSignalMyCustInd 

А сейчас немного теории.

 

5. Класс CiCustom

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

 

6. Класс CIndicators.

Класс CIndicators является классом для коллекционирования экземпляров классов таймсерий и технических индикаторов. Класс CIndicators обеспечивает создание экземпляров классов технических индикаторов, их хранение и управление ими (синхронизацию данных, управление хэндлами и памятью).

В классе CIndicators  нам будет интересен метод Creat. Этот метод создает индикатор указанного типа с указанными параметрами.

 

7. Продолжаем писать наш класс торговых сигналов

Следующий блок кода, который мы будем изменять (строки 28-42):
class CSignalMyCustInd : public CExpertSignal
  {
protected:
   CiEnvelopes       m_env;            // object-indicator
   //--- adjusted parameters
   int               m_ma_period;      // the "period of averaging" parameter of the indicator
   int               m_ma_shift;       // the "time shift" parameter of the indicator
   ENUM_MA_METHOD    m_ma_method;      // the "method of averaging" parameter of the indicator
   ENUM_APPLIED_PRICE m_ma_applied;    // the "object of averaging" parameter of the indicator
   double            m_deviation;      // the "deviation" parameter of the indicator
   double            m_limit_in;       // threshold sensitivity of the 'rollback zone'
   double            m_limit_out;      // threshold sensitivity of the 'break through zone'
   //--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0 "price is near the necessary border of the envelope"
   int               m_pattern_1;      // model 1 "price crossed a border of the envelope"

 

8. Создание пользовательского индикатора в генераторе торговых сигналов

Посмотрите блок кода выше. В строке

   CiEnvelopes       m_env;            // object-indicator

объявляется объект - индикатор класса CiEnvelopes. CiEnvelopes - это класс для работы с техническим индикатором из стандартной библиотеки.  Класс CiEnvelopes был создан на основе технического индикатора из стандартной библиотеки. Мы же пишем код генератора на основе своего пользовательского индикатора. И готового класса для Вашего пользовательского индикатора в стандартной библиотеке, конечно, нет. Наш выход – использовать класс CiCustom.

Объявим наш индикатор как класс CiCustom:

   CiCustom          m_mci;            // объект-индикатор "MyCustomIndicator"

8.1 Четыре переменные

Помните про блок описания параметров в классе? В этом описании были три параметра. Теперь в нашем классе генератора, в области protected, мы объявим четыре переменные для передачи значений нашим четырем параметрам:

   //--- настраиваемые параметры
   int               m_period_fast;    // "период быстрой EMA"
   int               m_period_slow;    // "период медленной EMA"
   int               m_period_signal;  // "период усреднения разности"
   ENUM_APPLIED_PRICE m_applied;       // "тип цены"

Следующий блок кода:

   //--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0 "price is near the necessary border of the envelope"
   int               m_pattern_1;      // model 1 "price crossed a border of the envelope"

В этом коде объявляются переменные для придания "веса" торговым моделям нашего генератора торговых сигналов. Заменим блок "весов" на следующий код:

   //--- "веса" рыночных моделей (0-100)
   int               m_pattern_0;      // model 0 "the oscillator has required direction"
   int               m_pattern_1;      // model 1 "индикатор растет - buy; индикатор падает - sell"

 

9. "Model 0"

Как Вы помните, в начале статьи мы решили описать только одну новую модель, которую будет генерировать наш генератор торговых сигналов. А в коде выше я прописал две рыночные модели (model 0 и model 1). Здесь "model 0" является важной вспомогательной моделью. "model 0" нужна при торговле отложенными ордерами. Применение модели "model 0" обеспечивает перенос отложенных ордеров вслед за ценой. Рассмотрим наш генератор торговых сигналов и следующие условия:

Такие условия идеально описывают нашу торговую модель. Вот как будут развиваться события: В момент создания бара "№1" проверяются условия нашей торговой модели. Имеем: MACD находится ниже нулевой линии, MACD растет. Это соответствует сигналу на покупку. Значит, выставляем отложенный ордер на покупку:

Рисунок 7. Выставляем отложенный ордер buy stop  

Рисунок 7. Выставляем отложенный ордер buy stop 

В момент создания следующего бара "№2" проверка условий показала: MACD находится ниже нуля и при этом MACD снижается. В соответствии с нашей торговой моделью в данном случае нет условий ни для покупки, ни для продажи. А здесь, внимание: в соответствии с логикой класса CExpertSignal раз нет условий ни для покупки, ни для продажи значит нужно УДАЛИТЬ все отложенные ордера. При этом если цена резко пойдет вверх, и мы упустим возможность выгодно войти по покупке, так как у нас не будет отложенного ордера.

Именно для таких случаев применяется вспомогательная модель "model 0". Модель "model 0" сработает, если выполняются такие условия:

Значит можно устанавливать отложенный ордер на покупку. А так как ордер мы устанавливаем на расстоянии 50 пунктов от цены открытия бара, то фактически, мы переносим отложенный ордер "buy stop" ниже вслед за ценой:

Рисунок 8. Передвигаем ниже ордер buy stop  

Рисунок 8. Передвигаем ниже ордер buy stop 

Таким образом, применение вспомогательной модели "model 0" дает нам возможность переносить отложенный ордер вслед за ценой.

 

10. Продолжаем изменять код нашего шаблона

Следующий блок кода, который мы будем изменять: 
public:
                     CSignalMyCustInd(void);
                    ~CSignalMyCustInd(void);
   //--- methods of setting adjustable parameters
   void              PeriodMA(int value)                 { m_ma_period=value;        }
   void              Shift(int value)                    { m_ma_shift=value;         }
   void              Method(ENUM_MA_METHOD value)        { m_ma_method=value;        }
   void              Applied(ENUM_APPLIED_PRICE value)   { m_ma_applied=value;       }
   void              Deviation(double value)             { m_deviation=value;        }
   void              LimitIn(double value)               { m_limit_in=value;         }
   void              LimitOut(double value)              { m_limit_out=value;        }
   //--- methods of adjusting "weights" of market models
   void              Pattern_0(int value)                { m_pattern_0=value;        }
   void              Pattern_1(int value)                { m_pattern_1=value;        }
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking if the market models are formed
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);

 

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

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

   //--- методы установки настраиваемых параметров
   void              PeriodFast(int value)               { m_period_fast=value;           }
   void              PeriodSlow(int value)               { m_period_slow=value;           }
   void              PeriodSignal(int value)             { m_period_signal=value;         }
   void              Applied(ENUM_APPLIED_PRICE value)   { m_applied=value;               }

Следующий код будет без изменений, за исключением только перевода:

   //--- методы настраивания "весов" рыночных моделей
   void              Pattern_0(int value)                { m_pattern_0=value;        }
   void              Pattern_1(int value)                { m_pattern_1=value;        }
   //--- метод проверки настроек
   virtual bool      ValidationSettings(void);
   //--- метод создания индикатора и таймсерий
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- методы проверки, если модели рынка сформированы
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);

Следующий блок кода, который мы будем изменять: 

protected:
   //--- method of initialization of the indicator
   bool              InitMA(CIndicators *indicators);
   //--- methods of getting data
   double            Upper(int ind)                      { return(m_env.Upper(ind)); }
   double            Lower(int ind)                      { return(m_env.Lower(ind)); }
  };

Этот блок будет изменен очень сильно. Обратите внимание, я использую метод GetData класса CIndicator. Название вызываемым методам я дам прямо в коде:

protected:
   //--- метод инициализации индикатора
   bool              InitMyCustomIndicator(CIndicators *indicators);
   //--- методы получения данных
   //- получение значения индикатора
   double            Main(int ind) { return(m_mci.GetData(0,ind));      }
   //- получение значения сигнальной линии
   double            Signal(int ind) { return(m_mci.GetData(1,ind));    }
   //- разница между двумя соседними значениями индикатора
   double            DiffMain(int ind) { return(Main(ind)-Main(ind+1)); }
   int               StateMain(int ind);
   double            State(int ind) { return(Main(ind)-Signal(ind)); }
   //- подготавливает данные для поиска
   bool              ExtState(int ind);
   //- ищет рыночную модель с указанными параметрами
   bool              CompareMaps(int map,int count,bool minimax=false,int start=0);
  };

 Следующий блок кода - это конструктор.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSignalMyCustInd::CSignalMyCustInd(void) : m_ma_period(45),
                                           m_ma_shift(0),
                                           m_ma_method(MODE_SMA),
                                           m_ma_applied(PRICE_CLOSE),
                                           m_deviation(0.15),
                                           m_limit_in(0.2),
                                           m_limit_out(0.2),
                                           m_pattern_0(90),
                                           m_pattern_1(70)
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }

В конструкторе изменяем название переменных. Также мы будем использовать только две серии: USE_SERIES_HIGH+USE_SERIES_LOW

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CSignalMyCustInd::CSignalMyCustInd(void) : m_period_fast(12),
                                           m_period_slow(24),
                                           m_period_signal(9),
                                           m_applied(PRICE_CLOSE),
                                           m_pattern_0(10),
                                           m_pattern_1(50)
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_HIGH+USE_SERIES_LOW;
  }

Изменим метод ValidationSettings нашего класса.

//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::ValidationSettings(void)
  {
//--- validation settings of additional filters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data checks
   if(m_ma_period<=0)
     {
      printf(__FUNCTION__+": period MA must be greater than 0");
      return(false);
     }
//--- ok
   return(true);
  }

В блоке проверок проверяем главное условие для данного пользовательского индикатора: m_period_fast>=m_period_slow

//+------------------------------------------------------------------+
//| Проверка параметров защищенных данных                            |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::ValidationSettings(void)
  {
//--- validation settings of additional filters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data checks
   if(m_period_fast>=m_period_slow)
     {
      printf(__FUNCTION__+": slow period must be greater than fast period");
      return(false);
     }
//--- ok
   return(true);
  }

Следующий блок - создание индикаторов:

//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::InitIndicators(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- create and initialize MA indicator
   if(!InitMA(indicators))
      return(false);
//--- ok
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Создание индикаторов.                                            |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::InitIndicators(CIndicators *indicators)
  {
//--- check of pointer is performed in the method of the parent class
//---
//--- инициализация индикаторов и таймсерий дополнительных фильтров
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- создание и инициализация пользовательского индикатора
   if(!InitMyCustomIndicator(indicators))
      return(false);
//--- ok
   return(true);
  }

Следующий блок - блок инициализации индикаторов:

//+------------------------------------------------------------------+
//| Initialize MA indicators.                                        |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::InitMA(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- add object to collection
   if(!indicators.Add(GetPointer(m_env)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- initialize object
   if(!m_env.Create(m_symbol.Name(),m_period,m_ma_period,m_ma_shift,m_ma_method,m_ma_applied,m_deviation))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- ok
   return(true);
  }

Сначала добавляем объект в коллекцию. Потом задаем параметры нашего индикатора. Затем используем метод Create класса CIndicators создаем пользовательский индикатор:

//+------------------------------------------------------------------+
//| Инициализация индикаторов.                                       |
//+------------------------------------------------------------------+
bool CSignalMyCustInd::InitMyCustomIndicator(CIndicators *indicators)
  {
//--- добавление объекта в коллекцию
   if(!indicators.Add(GetPointer(m_mci)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- задание параметров индикатора
   MqlParam parameters[4];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Examples\\MACD.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_fast;
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=m_period_slow;
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_period_signal;
//--- инициализация объекта
   if(!m_mci.Create(m_symbol.Name(),0,IND_CUSTOM,4,parameters))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- количество буферов
   if(!m_mci.NumBuffers(4)) return(false);
//--- ok
   return(true);
  }

Следующий блок - проверка условий для покупки:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalMyCustInd::LongCondition(void)
  {
   int result=0;
   int idx   =StartIndex();
   double close=Close(idx);
   double upper=Upper(idx);
   double lower=Lower(idx);
   double width=upper-lower;
//--- if the model 0 is used and price is in the rollback zone, then there is a condition for buying
   if(IS_PATTERN_USAGE(0) && close<lower+m_limit_in*width && close>lower-m_limit_out*width)
      result=m_pattern_0;
//--- if the model 1 is used and price is above the rollback zone, then there is a condition for buying
   if(IS_PATTERN_USAGE(1) && close>upper+m_limit_out*width)
      result=m_pattern_1;
//--- return the result
   return(result);
  }

В соответствии с нашим решением о модели "model 0", проверяются две модели:  

//+------------------------------------------------------------------+
//| "Голосование" за то, что цена будет расти.                       |
//+------------------------------------------------------------------+
int CSignalMyCustInd::LongCondition(void)
  {
   int result=0;
   int idx   =StartIndex();
//--- check direction of the main line
   if(DiffMain(idx)>0.0)
     {
      //--- основная линия направлена вверх, и это подтверждает возможность роста цен
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)<0.0)
         result=m_pattern_1;      // signal number 1
     }
//--- return the result
   return(result);
  }

Следующий блок - проверка условий для продажу:  

//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalMyCustInd::ShortCondition(void)
  {
   int result  =0;
   int idx     =StartIndex();
   double close=Close(idx);
   double upper=Upper(idx);
   double lower=Lower(idx);
   double width=upper-lower;
//--- if the model 0 is used and price is in the rollback zone, then there is a condition for selling
   if(IS_PATTERN_USAGE(0) && close>upper-m_limit_in*width && close<upper+m_limit_out*width)
      result=m_pattern_0;
//--- if the model 1 is used and price is above the rollback zone, then there is a condition for selling
   if(IS_PATTERN_USAGE(1) && close<lower-m_limit_out*width)
      result=m_pattern_1;
//--- return the result
   return(result);
  }

В соответствии с нашим решением о модели "model 0", проверяются две модели:  

//+------------------------------------------------------------------+
//| "Голосование" за то, что цена упадет.                            |
//+------------------------------------------------------------------+
int CSignalMyCustInd::ShortCondition(void)
  {
   int result=0;
   int idx   =StartIndex();
//--- check direction of the main line
   if(DiffMain(idx)<0.0)
     {
            //--- основная линия направлена вниз, и это подтверждает возможность снижения цен
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)>0.0)
         result=m_pattern_1;      // signal number 1
     }
//--- return the result
   return(result);
  }

 

Заключение

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