Эксперты на основе популярных торговых систем и алхимия оптимизации торгового робота

Автор: lexy Вторник, Сентябрь 9th, 2014 Нет комментариев

Рубрика: Разное

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

Механическая торговая система, использующая два таймфрейма

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

Для длинных позиций

Для коротких позиций

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

Для длинных позиций

для коротких позиций

В данном случае переменная Trend определяет только направление действующего тренда на более крупном таймфрейме и дополнительное условие входа в рынок ограничивает торговую деятельность эксперта только направлением этого глобального тренда. Абсолютно аналогично, с точки зрения логики построения программного кода также безразлично, посредством какого алгоритма будет определяться направление этого, действующего на более крупном таймфрейме, тренда. А стало быть, мы абсолютно вольны выбирать — посредством каких алгоритмов нам определять условие входа в рынок на мелком таймфрейме и направление тренда на крупном таймфрейме. Я в качестве примера для определения условий входа в рынок возьму уже известный по моим предыдущим статьям алгоритм с осциллятором OsMA, представленный экспертом Exp_5.mq4, а для определения действующего тренда возьму мувинг J2JMA.mq4. В таком случае условие определения тренда будет выглядеть самым простейшим образом:

И перед нами стоит задача, дописать код, имеющийся в эксперте Exp_5.mq4 с учётом выше обозначенной логики. Готовый код этого эксперта будет выглядеть следующим образом:

//Для работы эксперта в папке Metatrader\EXPERTS\indicators небходимо
//наличие индикаторов 5c_OsMA.mq4 и J2JMA.mq4
//+==================================================================+
//|                                                       Exp_11.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +--------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК
extern bool   Test_Up = true;//фильтр направления расчётов сделок
extern double Money_Management_Up = 0.1;
//---- входные параметры пользовательского индикатора J2JMA.mq4
extern int    TimeframeX_Up = 240;
extern int    Length1X_Up = 4;  // глубина первого сглаживания
extern int    Phase1X_Up = 100; // параметр первого сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;  
extern int    Length2X_Up = 4;  // глубина второго сглаживания
extern int    Phase2X_Up = 100; // параметр второго сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;  
extern int    IPCX_Up = 0;/* Выбор цен, по которым производится расчёт
индикатора (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL,
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close.) */
//---- входные параметры пользовательского индикатора 5c_OsMA.mq4
extern int    Timeframe_Up = 60;
extern double IndLevel_Up = 0; // пробойный уровень индикатора
extern int    FastEMA_Up = 12;  // период быстрой EMA
extern int    SlowEMA_Up = 26;  // период медленной EMA
extern int    SignalSMA_Up = 9;  // период сигнальной SMA
extern int    STOPLOSS_Up = 50;  // стоплосс
extern int    TAKEPROFIT_Up = 100; // тейкпрофит
extern int    TRAILINGSTOP_Up = 0; // трейлинстоп
extern int    PriceLevel_Up =40; // разница между текущей ценой и
                                         // ценой срабатывания отложенного ордера
extern bool   ClosePos_Up = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК
extern bool   Test_Dn = true;//фильтр направления расчётов сделок
extern double Money_Management_Dn = 0.1;
//---- входные параметры пользовательского индикатора J2JMA.mq4
extern int    TimeframeX_Dn = 240;
extern int    Length1X_Dn = 4;  // глубина сглаживания
extern int    Phase1X_Dn = 100;  // параметр первого сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;  
extern int    Length2X_Dn = 4;  // глубина сглаживания
extern int    Phase2X_Dn = 100; // параметр второго сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;  
extern int    IPCX_Dn = 0;/* Выбор цен, по которым производится расчёт
индикатора (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL,
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close.) */
//---- входные параметры пользовательского индикатора 5c_OsMA.mq4
extern int    Timeframe_Dn = 60;
extern double IndLevel_Dn = 0; // пробойный уровень индикатора
extern int    FastEMA_Dn = 12;  // период быстрой EMA
extern int    SlowEMA_Dn = 26;  // период медленной EMA
extern int    SignalSMA_Dn = 9;  // период сигнальной SMA
extern int    STOPLOSS_Dn = 50;  // стоплосс
extern int    TAKEPROFIT_Dn = 100; // тейкпрофит
extern int    TRAILINGSTOP_Dn = 0; // трейлинстоп
extern int    PriceLevel_Dn = 40; // разница между текущей ценой и
                                         // ценой срабатывания отложенного ордера
extern bool   ClosePos_Dn = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
//---- Целые переменные для минимума расчётных баров
int MinBarX_Up, MinBar_Up, MinBarX_Dn, MinBar_Dn;
//+==================================================================+
//| TimeframeCheck() functions                                       |
//+==================================================================+
void TimeframeCheck(string Name, int Timeframe)
  {
//----+
   //---- Проверка значения переменной Timeframe на корректность
   if (Timeframe != 1)
    if (Timeframe != 5)
     if (Timeframe != 15)
      if (Timeframe != 30)
       if (Timeframe != 60)
        if (Timeframe != 240)
         if (Timeframe != 1440)
           Print(StringConcatenate("TimeframeCheck: Параметр ",Name,
                     " не может ", "быть равным ", Timeframe, "!!!"));    
//----+
  }
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   TimeframeCheck("Timeframe_Up", Timeframe_Up);
   //---- Проверка значения переменной TimeframeX_Up на корректность
   TimeframeCheck("TimeframeX_Up", TimeframeX_Up);                                                                        
//---- Проверка значения переменной Timeframe_Dn на корректность
   TimeframeCheck("Timeframe_Dn", Timeframe_Dn);
   //---- Проверка значения переменной Timeframe_Dn на корректность
   TimeframeCheck("TimeframeX_Dn", TimeframeX_Dn);    
//---- Инициализация переменных            
   MinBar_Up  = 3 + MathMax(FastEMA_Up, SlowEMA_Up) + SignalSMA_Up;
   MinBarX_Up  = 3 + 30 + 30;
   MinBar_Dn  = 3 + MathMax(FastEMA_Dn, SlowEMA_Dn) + SignalSMA_Dn;
   MinBarX_Dn  = 3 + 30 + 30;                              
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+

    //---- Завершение деинициализации эксперта
    return(0);
//----+
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   double J2JMA1, J2JMA2, Osc1, Osc2;
   //----+ Объявление статических переменных
   //----+ +---------------------------------------------------------------+
   static double TrendX_Up, TrendX_Dn;
   static datetime StopTime_Up, StopTime_Dn;
   static int LastBars_Up, LastBarsX_Up, LastBarsX_Dn, LastBars_Dn;
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ                                        |
   //----+ +---------------------------------------------------------------+
   if (Test_Up)
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      int IBARSX_Up = iBars(NULL, TimeframeX_Up);

      if (IBARS_Up >= MinBar_Up && IBARSX_Up >= MinBarX_Up)
       {
         //----+ +----------------------+
         //----+ ОПРЕДЕЛЕНИЕ ТРЕНДА     |
         //----+ +----------------------+
         if (LastBarsX_Up != IBARSX_Up)
          {
           //----+ Иницмализация переменных
           LastBarsX_Up = IBARSX_Up;
           BUY_Stop = false;

           //----+ вычисление индикаторных значений для J2JMA  
           J2JMA1 = iCustom(NULL, TimeframeX_Up,
                                "J2JMA", Length1X_Up, Length2X_Up,
                                             Phase1X_Up, Phase2X_Up,  
                                                     0, IPCX_Up, 0, 1);
           //---                    
           J2JMA2 = iCustom(NULL, TimeframeX_Up,
                                "J2JMA", Length1X_Up, Length2X_Up,
                                             Phase1X_Up, Phase2X_Up,  
                                                     0, IPCX_Up, 0, 2);

           //----+ определение тренда                                
           TrendX_Up = J2JMA1 - J2JMA2;
           //----+ определение сигнала для закрывания сделок
           if (TrendX_Up < 0)
                      BUY_Stop = true;                                      
          }

         //----+ +----------------------------------------+
         //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК   |
         //----+ +----------------------------------------+
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных
           BUY_Sign = false;
           LastBars_Up = IBARS_Up;
           StopTime_Up = iTime(NULL, Timeframe_Up, 0)
                                          + 60 * Timeframe_Up;
           //----+ вычисление индикаторных значений
           Osc1 = iCustom(NULL, Timeframe_Up,
                         "5c_OsMA", FastEMA_Up, SlowEMA_Up,
                                               SignalSMA_Up, 5, 1);
           //---                  
           Osc2 = iCustom(NULL, Timeframe_Up,
                         "5c_OsMA", FastEMA_Up, SlowEMA_Up,
                                               SignalSMA_Up, 5, 2);

           //----+ определение сигналов для сделок
           if (TrendX_Up > 0)                                          
            if (Osc2 < IndLevel_Up)
             if (Osc1 > IndLevel_Up)
                        BUY_Sign = true;                              
          }

         //----+ +-------------------+
         //----+ СОВЕРШЕНИЕ СДЕЛОК   |
         //----+ +-------------------+
         if (!OpenBuyLimitOrder1(BUY_Sign, 1,
              Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up,
                                            PriceLevel_Up, StopTime_Up))
                                                                 return(-1);
         if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop, 1))
                                        return(-1);

         if (!Make_TreilingStop(1, TRAILINGSTOP_Up))
                                              return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ                                       |
   //----+ +---------------------------------------------------------------+
   if (Test_Dn)
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      int IBARSX_Dn = iBars(NULL, TimeframeX_Dn);

      if (IBARS_Dn >= MinBar_Dn && IBARSX_Dn >= MinBarX_Dn)
       {
         //----+ +----------------------+
         //----+ ОПРЕДЕЛЕНИЕ ТРЕНДА     |
         //----+ +----------------------+
         if (LastBarsX_Dn != IBARSX_Dn)
          {
           //--- Иницмализация переменных
           LastBarsX_Dn = IBARSX_Dn;
           SELL_Stop = false;

           //----+ вычисление индикаторных значений для J2JMA  
           J2JMA1 = iCustom(NULL, TimeframeX_Dn,
                                "J2JMA", Length1X_Dn, Length2X_Dn,
                                             Phase1X_Dn, Phase2X_Dn,  
                                                     0, IPCX_Dn, 0, 1);
           //---                    
           J2JMA2 = iCustom(NULL, TimeframeX_Dn,
                                "J2JMA", Length1X_Dn, Length2X_Dn,
                                             Phase1X_Dn, Phase2X_Dn,  
                                                     0, IPCX_Dn, 0, 2);

           //----+ определение тренда                                
           TrendX_Dn = J2JMA1 - J2JMA2;
           //----+ определение сигнала для закрывания сделок
           if (TrendX_Dn > 0)
                      SELL_Stop = true;                                      
          }

         //----+ +----------------------------------------+
         //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК   |
         //----+ +----------------------------------------+
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных
           SELL_Sign = false;
           LastBars_Dn = IBARS_Dn;
           StopTime_Dn = iTime(NULL, Timeframe_Dn, 0)
                                          + 60 * Timeframe_Dn;
           //----+ вычисление индикаторных значений    
           Osc1 = iCustom(NULL, Timeframe_Dn,
                         "5c_OsMA", FastEMA_Dn, SlowEMA_Dn,
                                               SignalSMA_Dn, 5, 1);
           //---                  
           Osc2 = iCustom(NULL, Timeframe_Dn,
                         "5c_OsMA", FastEMA_Dn, SlowEMA_Dn,
                                               SignalSMA_Dn, 5, 2);

           //----+ определение сигналов для сделок
           if (TrendX_Dn < 0)                                          
            if (Osc2 > IndLevel_Dn)
             if (Osc1 < IndLevel_Dn)
                        SELL_Sign = true;                              
          }

         //----+ +-------------------+
         //----+ СОВЕРШЕНИЕ СДЕЛОК   |
         //----+ +-------------------+
          if (!OpenSellLimitOrder1(SELL_Sign, 2,
              Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn,
                                            PriceLevel_Dn, StopTime_Dn))
                                                                 return(-1);
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop, 2))
                                        return(-1);

          if (!Make_TreilingStop(2, TRAILINGSTOP_Dn))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
//----+ 

    return(0);
  }
//+------------------------------------------------------------------+

Этот код даже визуально оказался в два раза больше первоначального кода эксперта Exp_5mq4, а, казалось бы, первоначальная идея не так чтобы и очень громоздко выглядела! Здесь мне следовало бы более подробно остановиться на том, как это получилось. Я опять подвергну анализу только ту часть эксперта, которая управляет длинными позициями, для коротких позиций всё аналогично. Сам исходный дополнительный код для для получения требуемых значений индикатора J2JMA выглядит следующим образом:

//----+ вычисление индикаторных значений для J2JMA  
           J2JMA1 = iCustom(NULL, TimeframeX_Up, "J2JMA", Length1X_Up, Phase1X_Up, Length2X_Up, Phase2X_Up, 0, IPCX_Up, 0, 1);
           //---                    
           J2JMA2 = iCustom(NULL, TimeframeX_Up, "J2JMA", Length1X_Up, Phase1X_Up, Length2X_Up, Phase2X_Up, 0, IPCX_Up, 0, 2);
//----+ определение тренда                                
           Trend_Up = J2JMA1 - J2JMA2;

По этой причине в шапке эксперта появляются объявления шести, соответствующих обращению к пользовательскому индикатору J2JMA новых внешних переменных:

 

extern int    TimeframeX_Up = 240;
extern int    Length1X_Up = 4;  // глубина первого сглаживания
extern int    Phase1X_Up = 100; // параметр первого сглаживания, изменяющийся в пределах -100 ... +100,
                                //влияет на качество переходного процесса усреднения;  
extern int    Length2X_Up = 4;  // глубина второго сглаживания
extern int    Phase2X_Up = 100; // параметр второго сглаживания, изменяющийся в пределах -100 ... +100,
                                //влияет на качество переходного процесса усреднения;  
extern int    IPCX_Up = 0;/* Выбор цен, по которым производится расчёт индикатора        
                          ( 0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
                          7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low,
                          12-Heiken Ashi High, 13-Heiken Ashi Open, 14-Heiken Ashi Close.)
                          */

В строку объявления глобальных целых переменных для минимума расчётных баров, которая идёт за внешними переменными эксперта, дописывается новая аналогичная переменная MinBarX_Up:

//---- Целые переменные для минимума расчётных баров
int MinBarX_Up, MinBar_Up, MinBarX_Dn, MinBar_Dn;

В блоке инициализации эксперта делается дополнительная проверка новой внешней переменной TimeframeХ_Up на корректность:

//---- Проверка значения переменной TimeframeX_Up на корректность
   TimeframeCheck("TimeframeХ_Up", TimeframeX_Up);

В этом же блоке делают инициализацию переменной MinBarX_Up:

MinBarX_Up  = 3 + 30 + 30;

Дальнейшие преобразования кода делаются в блоке функции start() эксперта. В строку объявления локальных переменных дописывается пара новых переменных J2JMA1 и J2JMA2:

//----+ Объявление локальных переменных
   double J2JMA1, J2JMA2, Osc1, Osc2;

Переменная Trend_Up объявляется как статическая, так как она инициализируется всего один раз при смене бара, а её значение используется на последующих тиках функции start():

static double TrendX_Up, TrendX_Dn;

Аналогично объявляется как статическая и переменная LastBarsX_Up:

static int LastBars_Up, LastBarsX_Up, LastBarsX_Dn, LastBars_Dn;

В самом коде для длинных позиций немного усложняется проверка на достаточность для расчёта:

if (Test_Up)
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      int IBARSX_Up = iBars(NULL, TimeframeX_Up);

      if (IBARS_Up >= MinBar_Up && IBARSX_Up >= MinBarX_Up)
       {
         // КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ
       }
     }

и появляется новый блок

         //----+ +----------------------+
         //----+ ОПРЕДЕЛЕНИЕ ТРЕНДА     |
         //----+ +----------------------+
         if (LastBarsX_Up != IBARSX_Up)
          {
           //----+ Иницмализация переменных
           LastBarsX_Up = IBARSX_Up;
           BUY_Stop = false;

           //----+ вычисление индикаторных значений для J2JMA  
           J2JMA1 = iCustom(NULL, TimeframeX_Up,
                                "J2JMA", Length1X_Up, Phase1X_Up,
                                            Length2X_Up, Phase2X_Up,  
                                                     0, IPCX_Up, 0, 1);
           //---                    
           J2JMA2 = iCustom(NULL, TimeframeX_Up,
                                "J2JMA", Length1X_Up, Phase1X_Up,
                                            Length2X_Up, Phase2X_Up,  
                                                     0, IPCX_Up, 0, 2);

           //----+ определение тренда                                
           TrendX_Up = J2JMA1 - J2JMA2;
           //----+ определение сигнала для закрывания сделок
           if (Trend_Up < 0)
                      BUY_Stop = true;                                      
          }

В этом блоке происходит инициализация необходимой нам переменной Trend_Up и помимо этого определяются сигналы для принудительного закрытия открытых позиций (инициализация переменной BUY_Stop). Вообще-то, первоначально в эксперте Exp_5.mq4 инициализация последней переменной происходила в блоке «ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК», но гораздо логичнее будет в новом эксперте переместить эту инициализацию в блок «ОПРЕДЕЛЕНИЕ ТРЕНДА» и изменить сам алгоритм её инициализации.

И самое главное — это небольшое изменение алгоритма определения сигналов для сделок в блоке «ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК»:

           //----+ определение сигналов для сделок
           if (TrendX_Up > 0)                                          
            if (Osc2 < IndLevel_Up)
             if (Osc1 > IndLevel_Up)
                        BUY_Sign = true;

После всех произведённых преобразований этот алгоритм с помощью проверки переменной Trend_Up учитывает направление действующего тренда.

Теперь мне следовало бы поподробнее остановиться на некоторых деталях оптимизации этого эксперта. Вполне естественно, что и этого моего эксперта следует оптимизировать отдельно только для длинных или только для коротких позиций, и всё равно даже в таком случае внешних переменных для оптимизации оказывается слишком много. Едва ли целесообразно все эти переменные оптимизировать одновременно, вдобавок генетический алгоритм оптимизации всё равно больше восьми переменных оптимизировать не будет! Самым идеальным решением в таком случае будет зафиксировать значения некоторых переменных и оптимизировать только оставшуюся часть незафиксированных — самых актуальных — переменных. А уже после оптимизации, выбрав устраивающий нас вариант оптимизированных параметров прогона и зафиксировав эти параметры, попытаться оптимизировать оставшиеся неоптимизированные параметры.

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

Файл с этими настройками для тестера Exp_11.ini лежит в архиве TESTER.zip. Оптимизировать переменную Money_Management_Up по вполне понятным соображениям не стоит вообще, а TimeframeX_Up и подавно. Относительно переменной TimeframeX_Up следует учесть, её значение изначально по замыслу должно быть больше, чем значение переменной Timeframe_Up. Значения переменной Length1X_Up можно менять в достаточно больших пределах, а переменной Phase1X_Up в пределах от -100 и до 100. Параметры Length2X_Up, Phase2X_Up и IPCX_Up при первой оптимизации лучше зафиксировать, то же самое касается и параметра IndLevel_Up, про который я говорил в своей, предыдущей статье, посвящённой Exp_5.mq4. Для параметров FastEMA_Up и SlowEMA_Up, пожалуй, не стоит ставить нижние границы изменения параметров совсем маленькими. Они, конечно, могут показать на этих значениях потрясающие результаты, но вот только будет ли какой-нибудь смысл в этих результатах? Целесообразность применения трейлингстопа тоже лучше проверять уже после оптимизации. А вот принудительное закрывание позиций при изменении тренда логической переменной ClosePos_Up делать всё-таки стоит всегда. Поэтому её значение лучше брать фиксированным и равным true.

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

В своей четвёртой статье я занимался экспортом результатов оптимизаций для дальнейших статистических исследований в Microsoft Excel. На мой взгляд, только что предложенный мною читателям эксперт является самым подходящим экземпляром для проведения подобных процедур. На случай, если у кого возникнет желание этим заняться, я дописал код этого эксперта с учётом рекомендаций данной статьи (Exp_11_2.mq4). Представлять код этого эксперта в тексте статьи будет несколько излишним.

Ещё один пример эксперта с использованием для расчётов данных двух графиков разных таймфреймов

Пожалуй, одного примера эксперта, основанного на этой идее, было бы маловато для подобной статьи, и поэтому я приведу пример ещё одного эксперта, построенного по этим принципам. В качестве основы я возьму своего первого эксперта Exp_1.mq4 из своей первой статьи. Та часть кода, которая отвечает за определение условий входа в рынок и управляет позициями, уже готова, осталось каким-то образом определить условие действующего на рынке тренда для более крупного таймфрейма. В этом эксперте я использую индикатор MAMA_NK.mq4:

Условием для определения направления тренда в данном случае оказывается разница значений двух мувингов на первом баре:

 

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

//+==================================================================+
//|                                                       Exp_12.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +---------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК
extern bool   Test_Up = true;//фильтр направления расчётов сделок
extern double Money_Management_Up = 0.1;
//----
extern int    TimeframeX_Up = 240;
extern double FastLimitX_Up = 0.5;
extern double SlowLimitX_Up = 0.05;
extern int    IPCX_Up = 9;/* Выбор цен, по которым производится расчёт индикатора
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close, 15-Heiken Ashi Open0.) */
//---- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
extern int    Timeframe_Up = 60;
extern int    Length_Up = 4;  // глубина сглаживания
extern int    Phase_Up = 100; // параметр, изменяющийся в пределах
          //-100 ... +100, влияет на качество переходного процесса;
extern int    IPC_Up = 0;/* Выбор цен, по которым производится расчёт
индикатора (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL,
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close.) */
extern int    STOPLOSS_Up = 50;  // стоплосс
extern int    TAKEPROFIT_Up = 100; // тейкпрофит
extern bool   ClosePos_Up = true; // разрешение принудительного закрывания позиции
//----+ +---------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК
extern bool   Test_Dn = true;//фильтр направления расчётов сделок
extern double Money_Management_Dn = 0.1;
//----
extern int    TimeframeX_Dn = 60;
extern double FastLimitX_Dn = 0.5;
extern double SlowLimitX_Dn = 0.05;
extern int    IPCX_Dn = 9;/* Выбор цен, по которым производится расчёт индикатора
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close, 15-Heiken Ashi Open0.) */
//---- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
extern int    Timeframe_Dn = 60;
extern int    Length_Dn = 4;  // глубина сглаживания
extern int    Phase_Dn = 100; // параметр, изменяющийся в пределах
         // -100 ... +100, влияет на качество переходного процесса;
extern int    IPC_Dn = 0;/* Выбор цен, по которым производится расчёт
индикатора (0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL,
6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High, 13-Heiken Ashi Open,
14-Heiken Ashi Close.) */
extern int   STOPLOSS_Dn = 50;  // стоплосс
extern int   TAKEPROFIT_Dn = 100; // тейкпрофит
extern bool   ClosePos_Dn = true; // разрешение принудительного закрывания позиции
//----+ +---------------------------------------------------------------------------+
//---- Целые переменные для минимума расчётных баров
int MinBar_Up, MinBar_Dn, MinBarX_Up, MinBarX_Dn;
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| TimeframeCheck() functions                                       |
//+==================================================================+
void TimeframeCheck(string Name, int Timeframe)
  {
//----+
   //---- Проверка значения переменной Timeframe на корректность
   if (Timeframe != 1)
    if (Timeframe != 5)
     if (Timeframe != 15)
      if (Timeframe != 30)
       if (Timeframe != 60)
        if (Timeframe != 240)
         if (Timeframe != 1440)
           Print(StringConcatenate("Параметр ",Name,
                     " не может ", "быть равным ", Timeframe, "!!!"));    
//----+
  }
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   TimeframeCheck("TimeframeX_Up", TimeframeX_Up);
//---- Проверка значения переменной Timeframe_Up на корректность
   TimeframeCheck("Timeframe_Up", Timeframe_Up);
//---- Проверка значения переменной Timeframe_Dn на корректность
   TimeframeCheck("TimeframeX_Dn", TimeframeX_Dn);
//---- Проверка значения переменной Timeframe_Dn на корректность
   TimeframeCheck("Timeframe_Dn", Timeframe_Dn);
//---- Инициализация переменных            
   MinBarX_Up = 1 + 7;
   MinBar_Up = 4 + 39 + 30;
   MinBarX_Dn = 1 + 7;
   MinBar_Dn = 4 + 39 + 30;                                      
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+

    //---- Завершение деинициализации эксперта
    return(0);
//----+
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   int    bar;
   double Mov[3], dMov12, dMov23, Mama1, Fama1;
   //----+ Объявление статических переменных
   static double TrendX_Up, TrendX_Dn;
   static int LastBars_Up, LastBars_Dn, LastBarsX_Up, LastBarsX_Dn;
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;

   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ                                        |
   //----+ +---------------------------------------------------------------+
   if (Test_Up)
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      int IBARSX_Up = iBars(NULL, TimeframeX_Up);

      if (IBARS_Up >= MinBar_Up && IBARSX_Up >= MinBarX_Up)
       {      
         //----+ +----------------------+
         //----+ ОПРЕДЕЛЕНИЕ ТРЕНДА     |
         //----+ +----------------------+
         if (LastBarsX_Up != IBARSX_Up)
          {
           //----+ Иницмализация переменных
           LastBarsX_Up = IBARSX_Up;
           BUY_Stop = false;

           //----+ вычисление индикаторных значений
           Fama1 = iCustom(NULL, TimeframeX_Up,
                    "MAMA_NK", FastLimitX_Up, SlowLimitX_Up, IPCX_Up, 0, 1);
           //---        
           Mama1 = iCustom(NULL, TimeframeX_Up,
                    "MAMA_NK", FastLimitX_Up, SlowLimitX_Up, IPCX_Up, 1, 1);
           //----+ определение тренда                                
           TrendX_Up = Mama1 - Fama1;
           //----+ определение сигнала для закрывания сделок
           if (TrendX_Up < 0)
                      BUY_Stop = true;                                      
          }

         //----+ +----------------------------------------+
         //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК   |
         //----+ +----------------------------------------+
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных
           BUY_Sign = false;
           LastBars_Up = IBARS_Up; 

           //----+ вычисление индикаторных значений и загрузка их в буфер      
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Up,
                                "JFatl", Length_Up, Phase_Up,
                                                   0, IPC_Up, 0, bar);

           //----+ определение сигналов для сделок  
           dMov12 = Mov[0] - Mov[1];
           dMov23 = Mov[1] - Mov[2]; 

           if (TrendX_Up > 0)                    
              if (dMov23 < 0)
                if (dMov12 > 0)
                        BUY_Sign = true;                                    
          }

         //----+ +-------------------+
         //----+ СОВЕРШЕНИЕ СДЕЛОК   |
         //----+ +-------------------+
          if (!OpenBuyOrder1(BUY_Sign, 1, Money_Management_Up,
                                          STOPLOSS_Up, TAKEPROFIT_Up))
                                                                 return(-1);
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop, 1))
                                        return(-1);
        }
     }

   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ                                       |
   //----+ +---------------------------------------------------------------+
   if (Test_Dn)
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      int IBARSX_Dn = iBars(NULL, TimeframeX_Dn);

      if (IBARS_Dn >= MinBar_Dn && IBARSX_Dn >= MinBarX_Dn)
       {
         //----+ +----------------------+
         //----+ ОПРЕДЕЛЕНИЕ ТРЕНДА     |
         //----+ +----------------------+
         if (LastBarsX_Dn != IBARSX_Dn)
          {
           //----+ Иницмализация переменных
           LastBarsX_Dn = IBARSX_Dn;
           SELL_Stop = false;

           //----+ вычисление индикаторных значений
           Fama1 = iCustom(NULL, TimeframeX_Dn,
                    "MAMA_NK", FastLimitX_Dn, SlowLimitX_Dn, IPCX_Dn, 0, 1);
           //---        
           Mama1 = iCustom(NULL, TimeframeX_Dn,
                    "MAMA_NK", FastLimitX_Dn, SlowLimitX_Dn, IPCX_Dn, 1, 1);
           //----+ определение тренда                                
           TrendX_Dn = Mama1 - Fama1;
           //----+ определение сигнала для закрывания сделок
           if (TrendX_Dn > 0)
                      SELL_Stop = true;                                      
          }

         //----+ +----------------------------------------+
         //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ ВХОДА В РЫНОК   |
         //----+ +----------------------------------------+
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных
           SELL_Sign = false;
                      LastBars_Dn = IBARS_Dn; 

           //----+ вычисление индикаторных значений и загрузка их в буфер    
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Dn,
                                "JFatl", Length_Dn, Phase_Dn,
                                                   0, IPC_Dn, 0, bar);

           //----+ определение сигналов для сделок  
           dMov12 = Mov[0] - Mov[1];
           dMov23 = Mov[1] - Mov[2]; 

           if (TrendX_Dn < 0)                                
              if (dMov23 > 0)
                if (dMov12 < 0)
                       SELL_Sign = true;                                        
          }

         //----+ +-------------------+
         //----+ СОВЕРШЕНИЕ СДЕЛОК   |
         //----+ +-------------------+
          if (!OpenSellOrder1(SELL_Sign, 2, Money_Management_Dn,
                                            STOPLOSS_Dn, TAKEPROFIT_Dn))
                                                                   return(-1);
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop, 2))
                                        return(-1);
        }
     }
//----+ 

    return(0);
  }
//+------------------------------------------------------------------+

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

Заключение

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

Прикрепленные файлы:
 EXPERTS.zip (19.6 Kb)
 INCLUDE.zip (25.6 Kb)
 indicators.zip (14.0 Kb)
 TESTER.zip (6.7 Kb)
Источник: mql4.com

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты