Мастер MQL5: Как научить эксперта открывать отложенные ордера по любым ценам

Vladimir Karputov | 3 сентября, 2013

Введение

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

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


1. Механизм открытия отложенных ордеров в эксперте, созданном с помощью Мастера MQL5

Сгенерированный эксперт в своей шапке имеет приблизительно следующий код:

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string             Expert_Title="ExpertMySignalEnvelopes.mq5";      // Document name
ulong                    Expert_MagicNumber        =3915;        // 
bool                     Expert_EveryTick          =false;       // 
//--- inputs for main signal
input int                Signal_ThresholdOpen      =10;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose     =10;          // Signal threshold value to close [0...100]
input double             Signal_PriceLevel         =0.0;         // Price level to execute a deal
input double             Signal_StopLevel          =85.0;        // Stop Loss level (in points)
input double             Signal_TakeLevel          =195.0;       // Take Profit level (in points)
input int                Signal_Expiration         =0;           // Expiration of pending orders (in bars)
input int                Signal_Envelopes_PeriodMA =13;          // Envelopes(13,0,MODE_SMA,...) Period of averaging
input int                Signal_Envelopes_Shift    =0;           // Envelopes(13,0,MODE_SMA,...) Time shift
input ENUM_MA_METHOD     Signal_Envelopes_Method   =MODE_SMA;    // Envelopes(13,0,MODE_SMA,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_Envelopes_Applied  =PRICE_CLOSE; // Envelopes(13,0,MODE_SMA,...) Prices series
input double             Signal_Envelopes_Deviation=0.2;         // Envelopes(13,0,MODE_SMA,...) Deviation
input double             Signal_Envelopes_Weight   =1.0;         // Envelopes(13,0,MODE_SMA,...) Weight [0...1.0]
//--- inputs for money
input double             Money_FixLot_Percent      =10.0;        // Percent
input double             Money_FixLot_Lots         =0.1;         // Fixed volume
//+------------------------------------------------------------------+

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

Параметр Signal_PriceLevel указывается в "больших" пунктах. На картинках ниже показана разница между отрицательными и положительными значениями:

При Signal_PriceLevel=-50:

Рис. 1. Signal_PriceLevel=-50

Рис. 1. Signal_PriceLevel=-50

При Signal_PriceLevel=50:

Рис. 2. Signal_PriceLevel=50

Рис. 2. Signal_PriceLevel=50

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

В данной версии эксперт открывает sell stop и buy stop ордера.


2. Где хранится запись об уровне отступа от цены для открытия отложенного ордера

Сначала рисунок, а потом комментарии к рисунку:

Хранение записи об уровне отступа от текущей цены

Рис. 3. Хранение записи об уровне отступа от текущей цены

Комментарии к рисунку.

Эксперт - эксперт, созданный с помощью Мастера MQL5.

Таким образом, параметр Signal_PriceLevel, в котором хранится уровень отступа от текущей цены и который был объявлен в эксперте, передается в объект signal класса CExpertSignal.

Класс CExpertSignal хранит значение уровня отступа от текущей цены в переменной m_price_level, которая объявлена в области видимости protected класса:

class CExpertSignal : public CExpertBase
  {
protected:
   //--- variables
   double            m_base_price;     // base price for detection of level of entering (and/or exit?)
   //--- variables for working with additional filters
   CArrayObj         m_filters;        // array of additional filters (maximum number of fileter is 64)
   //--- Adjusted parameters
   double            m_weight;         // "weight" of a signal in a combined filter
   int               m_patterns_usage; // bit mask of  using of the market models of signals
   int               m_general;        // index of the "main" signal (-1 - no)
   long              m_ignore;         // bit mask of "ignoring" the additional filter
   long              m_invert;         // bit mask of "inverting" the additional filter
   int               m_threshold_open; // threshold value for opening
   int               m_threshold_close;// threshold level for closing
   double            m_price_level;    // level of placing a pending orders relatively to the base price
   double            m_stop_level;     // level of placing of the "stop loss" order relatively to the open price
   double            m_take_level;     // level of placing of the "take profit" order relatively to the open price
   int               m_expiration;     // time of expiration of a pending order in bars


3. Структура эксперта, созданного с помощью Мастера MQL5

Эксперт состоит из нескольких блоков, различных по функциональности.

Структура эксперта 

Рис. 4. Структура эксперта

Комментарии к рисунку:


4. Блоки эксперта, которые целесообразно править

Как вы могли видеть в структуре эксперта, сгенерированного с помощью Мастера MQL5, есть блоки базовых классов. Базовые классы принадлежат Стандартной библиотеке.

Сами классы являются наследниками других базовых классов и одновременно включают в себя один или несколько базовых классов. Приведу ниже код начала всего двух классов - CExpert и CExpertSignal:

//+------------------------------------------------------------------+
//|                                                       Expert.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ExpertBase.mqh"
#include "ExpertTrade.mqh"
#include "ExpertSignal.mqh"
#include "ExpertMoney.mqh"
#include "ExpertTrailing.mqh"
//+------------------------------------------------------------------+
.
.
.
class CExpert : public CExpertBase

и

//+------------------------------------------------------------------+
//|                                                 ExpertSignal.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ExpertBase.mqh"
.
.
.
class CExpertSignal : public CExpertBase

Править базовые классы категорически не рекомендую:

  1. После обновления редактора все ваши изменения базовых классов будут затерты и базовые классы будут восстановлены в свое первоначальное состояние.
  2. Более правильный путь - это наследование. Но в таком случае придется править ВСЮ Стандартную библиотеку.

А вот блок самого эксперта и модули генераторов торговых сигналов - отличные кандидаты на роль редактируемых модулей. Тем более, что в соответствии с нашей торговой системой уже будет использован измененный модуль - генератор торговых сигналов индикатора Envelopes.

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


5. Схема реализации идеи

Из эксперта будет передаваться указатель в генератор торговых сигналов.

Для этого в генераторе надо дополнительно объявить переменную с областью видимости protected и написать метод, с помощью которого ссылка из эксперта сохраняется во внутреннюю переменную:

Схема реализации идеи 

Рис. 5. Схема реализации идеи


6. Торговая система

Период графика – D1. Используемый индикатор – Envelopes, период усреднения 13, метод усреднения Exponential. Типы ордеров, которые открывает эксперт, - это sell stop и buy stop ордера.

Если предыдущий бар был бычий, выставляем sell stop ордер. Если предыдущий бар был медвежьим, выставляем buy stop ордер. Другими словами, надеемся на откат цены:

Рис. 6. Торговая система

Рис. 6. Торговая система

Для генерации торговых сигналов в соответствии с торговой системой был изменен стандартный модуль генератора торговых сигналов SignalEnvelopes.mqh.

Кстати, можно было брать любой генератор торговых сигналов из Стандартной библиотеки.


7. Редактирование генератора торговых сигналов. Получение цены бара

Итак, приступим. Кстати, я сторонник сохранения своих программ в хранилище MQL5 Storage.

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

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

Создаем свою папку MySignals

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

Следующий шаг - создадим включаемый файл с помощью Мастера MQL5.

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

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

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

Назовем наш класс генератора сигналов MySignalEnvelopes.

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

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

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

После нажатия кнопки "Готово" Мастер MQL5 сгенерирует пустой шаблон.

Далее полученный файл MySignalEnvelopes.mqh нужно добавить в MQL5 Storage:

MQL Storage. Добавление файла.

Рис. 10. MQL5 Storage. Добавление файла

После добавления файла нужно зафиксировать изменение в MQL5 Storage:

MQL Starage. Фиксация изменений

Рис. 11. MQL5 Storage. Фиксация изменений

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

Так как наш генератор основан на стандартном файле \Include\Expert\Signal\SignalEnvelopes.mqh, то скопируем его содержимое полностью и вставим в наш файл генератора, заменив в нем все, кроме шапки:

//+------------------------------------------------------------------+
//|                                            MySignalEnvelopes.mqh |
//|                              Copyright © 2013, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2013, Vladimir Karputov"
#property link      "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);
  }
//+------------------------------------------------------------------+

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

Чтобы не запутаться, замененный код я буду выделять с помощью цвета:

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

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

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

Рис. 12. Переименование класса

Рис. 12. Переименование класса

Чтобы наш класс генератора торговых сигналов был виден в Мастере MQL5 под своим именем, переименуем в блоке описания название класса

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

на

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

Изменим период скользящей

//| Parameter=PeriodMA,int,45,Period of averaging                    |

на 13 (это лично мое мнение, вы можете ставить любое свое значение)

//| Parameter=PeriodMA,int,13,Period of averaging                    |

Еще изменим параметр Deviation (отклонение)

//| Parameter=Deviation,double,0.15,Deviation                        |

на более крупное значение

//| Parameter=Deviation,double,1.15,Deviation                        |

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

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

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
   //--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0
   CExpertSignal    *m_signal;         // храним ссылку на главный сигнал

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

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

public:
                     CMySignalEnvelopes(void);
                    ~CMySignalEnvelopes(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;        }
   //--- methods of adjusting "weights" of market models
   void              Pattern_0(int value)                { m_pattern_0=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);
   //--- метод установки указателя на главный сигнал
   virtual bool      InitSignal(CExpertSignal *signal=NULL);

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

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMySignalEnvelopes::CMySignalEnvelopes(void) : m_ma_period(13),
                                               m_ma_shift(0),
                                               m_ma_method(MODE_SMA),
                                               m_ma_applied(PRICE_CLOSE),
                                               m_deviation(1.15),
                                               m_pattern_0(50)  

Сейчас займемся изменением логики генерации торговых сигналов в соответствии с нашей торговой системой.

Блок кода, отвечающий за сигнал на покупку:

int CMySignalEnvelopes::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);
  }

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

int CMySignalEnvelopes::LongCondition(void) //---buy
  {
   int result=0;
   int idx   =StartIndex();
   double open=Open(idx);
   double close=Close(idx);
   double prlevel;
      if(IS_PATTERN_USAGE(0) && close<open)
        {
         prlevel=GetPriceLevelStopp(open,Open(0));
         m_signal.PriceLevel(prlevel);
         result=m_pattern_0;
        }
//--- return the result
   return(result);
  }

Блок кода, отвечающий за сигнал на продажу:

int CMySignalEnvelopes::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);
  }

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

int CMySignalEnvelopes::ShortCondition(void) //---sell
  {
   int result  =0;
   int idx     =StartIndex();
   double open=Open(idx);
   double close=Close(idx);
   double prlevel;
      if(IS_PATTERN_USAGE(0) && close>open)
        {
         prlevel=GetPriceLevelStopp(Open(0),open);
         m_signal.PriceLevel(prlevel);
         result=m_pattern_0;
        }
//--- return the result
   return(result);
  }


8. Пояснение к коду сигналов

При выполнении условия появления сигнала вызывается метод GetPriceLevelStopp - этот метод возвращает число вида "20" или "15" - уровень отступа от текущей цены.

Затем вызывается метод PriceLevel объекта m_signal (этим методом устанавливается уровень отступа для определения цены отложенного уровня). Напомню, что m_signal - это объект класса CExpertSignal, хранящий указатель на главный сигнал.

Код метода GetPriceLevelStopp привожу ниже:

double CMySignalEnvelopes::GetPriceLevelStopp(double price_0,double min)
  {
   double level;
   double temp;
   temp-=(price_0-min)/PriceLevelUnit();
   level=NormalizeDouble(temp,0);
   return(level);
  }

Этот метод необходимо объявить в шапке класса:

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)); }
   double            GetPriceLevelStopp(double price,double min);
  };

Еще один метод, который понадобится, - это метод передачи ссылки на главный сигнал во внутреннюю переменную:

bool CMySignalEnvelopes::InitSignal(CExpertSignal *signal)
  {
   m_signal=signal;
   return(true);
  }

 После этого необходимо создать эксперта в Мастере MQL5 и в нем подключить модуль сигналов Signals of indicator 'MySignalEnvelopes'.

В коде советника, сгенерированного при помощи Мастера MQL5, необходимо добавить вызов метода InitSignal:

//--- Set filter parameters
   filter0.PeriodMA(Signal_Envelopes_PeriodMA);
   filter0.Shift(Signal_Envelopes_Shift);
   filter0.Method(Signal_Envelopes_Method);
   filter0.Applied(Signal_Envelopes_Applied);
   filter0.Deviation(Signal_Envelopes_Deviation);
   filter0.Weight(Signal_Envelopes_Weight);
   filter0.InitSignal(signal);
//...

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

Код эксперта, сгенерированного с помощью Мастера MQL5, и код модуля сигналов приведены в приложении к статье.

Результаты тестирования эксперта с параметрами: период тестирования - с 2013.01.01 по 2013.09.01, период графика - D1, Stop Loss level = 85, Take Profit level = 195 для пар EURUSD и USDJPY представлены ниже:

Рис. 13. Тест EURUSD, период D1

Рис. 13. Тест EURUSD, период D1

Рис. 14. Тест USDJPY, период D1

Рис. 14. Тест USDJPY, период D1


Заключение

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

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