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

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

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

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

Я так полагаю, что читатель уже и сам убедился в том, что никакого особого труда не составляет загрузить в тестер стратегий эксперта, написанного на основе даже относительно простой механической торговой системы, и после его оптимизации получить просто удивительные результаты тестирования практически на любых исторических данных, совпадающих с историческими данными оптимизации. Но напрашивается вполне естественный и закономерный вопрос: » А какое, вообще-то, отношение к прогнозированию поведения механической торговой системы в будущем имеет результат, пускай даже и сказочно хороший, но полученный по большому счёту в результате подгонки этой системы под историю?»

 

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

 

Бэк-тэстинг или тестирование результатов оптимизаций без мистики и фетишизма

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

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

 

Если у кого есть долготерпение ждать результат годами, то тогда никаких проблем! Но вот только торговая система в итоге может оказаться абсолютно бестолковой, вот в чём незадача, и будет сильно обидно за безвозвратно убитые время и интернет трафик. И вдобавок, при таком подходе много торговых систем не протестируешь! Одну, две — от силы! Если кого-то устраивает подобная производительность труда, то тогда вперёд! И самое неприятное при таком подходе — это то, что весьма солидный объём усилий, затраченный на тестирование подобной стратегии торговой системы, напрочь отшибает у её создателя способность критически оценить результаты её работы!

 

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

 

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

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

 

Я думаю, что данная диаграмма вполне понятна. Например, берём в качестве периода анализа 2007 год. Для первой оптимизации период оптимизации будет с 01.01.2007 по 31.03.2007, а период тестирования с 01.04.2007 по 31.05.2007. После тестирования и фиксации его результатов сдвигаем периоды оптимизации и тестирования на один месяц вперёд: период оптимизации будет с 01.02.2007 по 30.04.2007, а период тестирования с 01.05.2007 по 30.06.2007. И так далее. В результате имеем таблицу, в которой зафиксированы результаты каждой оптимизации, например, вот в таком виде:

 

 

Итоговый параметр тестирования/Проход 01 02 03 04 05 06 07 08 09 10 11 12
Чистая прибыль
Общая прибыль
Общий убыток
Прибыльность
Абсолютная просадка
Относительная просадка
Всего сделок

 

 

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

 

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

 

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

 

Осцилляторная торговая система

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

 

 

Большинство осцилляторов изменяют свои значения от какого-то минимума до какого-то максимума, причём у каждого осциллятора эти значения свои. На некотором расстоянии от этих предельных значений располагают уровни UpLevel и DownLevel.

 

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

А сигнал к продаже возникает, если осцилятор входит из нетрендовой зоны в зону перепроданности:

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

 

Сигнал к покупке возникает при выходе из зоны перепроданности в нетрендовую зону (коррекциия падающего тренда):

Сигнал к продаже возникает при выходе из зоны перекупленности в нетрендовую зону (коррекция восходящего тренда):

 

Код эксперта для осцилляторной торговой системы

 

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

//+==================================================================+
//|                                                        Exp_3.mq4 |
//|                             Copyright © 2007,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+==================================================================+
#property copyright "Copyright © 2007, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +--------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК
extern bool   Test_Up1 = true;//фильтр направления расчётов сделок
extern int    Timeframe_Up1 = 240;
extern double Money_Management_Up1 = 0.1;
extern double IndLevel_Up1 = 0.8; // пробойный уровень индикатора
extern int    JLength_Up1 = 8;  // глубина JJMA сглаживания входной цены
extern int    XLength_Up1 = 8;  // глубина JurX сглаживания полученного индикатора
extern int    Phase_Up1 = 100; // параметр, изменяющийся в пределах -100 ... +100,
                              //влияет на качество переходныx процессов сглаживания
extern int    IPC_Up1 = 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_Up1 = 50;  // стоплосс
extern int    TAKEPROFIT_Up1 = 100; // тейкпрофит
extern int    TRAILINGSTOP_Up1 = 0; // трейлинстоп
extern bool   ClosePos_Up1 = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
///---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК
extern bool   Test_Up2 = true;//фильтр направления расчётов сделок
extern int    Timeframe_Up2 = 240;
extern double Money_Management_Up2 = 0.1;
extern double IndLevel_Up2 = -0.8; // пробойный уровень индикатора
extern int    JLength_Up2 = 8;  // глубина JJMA сглаживания входной цены
extern int    XLength_Up2 = 8;  // глубина JurX сглаживания полученного индикатора
extern int    Phase_Up2 = 100; // параметр, изменяющийся в пределах -100 ... +100,
                              //влияет на качество переходныx процессов сглаживания
extern int    IPC_Up2 = 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_Up2 = 50;  // стоплосс
extern int    TAKEPROFIT_Up2 = 100; // тейкпрофит
extern int    TRAILINGSTOP_Up2 = 0; // трейлинстоп
extern bool   ClosePos_Up2 = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК
extern bool   Test_Dn1 = true;//фильтр направления расчётов сделок
extern int    Timeframe_Dn1 = 240;
extern double Money_Management_Dn1 = 0.1;
extern double IndLevel_Dn1 = 0.8; // пробойный уровень индикатора
extern int    JLength_Dn1 = 8;  // глубина JJMA сглаживания входной цены
extern int    XLength_Dn1 = 8;  // глубина JurX сглаживания полученного индикатора
extern int    Phase_Dn1 = 100; // параметр, изменяющийся в пределах -100 ... +100,
                              //влияет на качество переходныx процессов сглаживания
extern int    IPC_Dn1 = 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_Dn1 = 50;  // стоплосс
extern int    TAKEPROFIT_Dn1 = 100; // тейкпрофит
extern int    TRAILINGSTOP_Dn1 = 0; // трейлинстоп
extern bool   ClosePos_Dn1 = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК
extern bool   Test_Dn2 = true;//фильтр направления расчётов сделок
extern int    Timeframe_Dn2 = 240;
extern double Money_Management_Dn2 = 0.1;
extern double IndLevel_Dn2 = -0.8; // пробойный уровень индикатора
extern int    JLength_Dn2 = 8;  // глубина JJMA сглаживания входной цены
extern int    XLength_Dn2 = 8;  // глубина JurX сглаживания полученного индикатора
extern int    Phase_Dn2 = 100; // параметр, изменяющийся в пределах -100 ... +100,
                              //влияет на качество переходныx процессов сглаживания
extern int    IPC_Dn2 = 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_Dn2 = 50;  // стоплосс
extern int    TAKEPROFIT_Dn2 = 100; // тейкпрофит
extern int    TRAILINGSTOP_Dn2 = 0; // трейлинстоп
extern bool   ClosePos_Dn2 = true; // разрешение принудительного закрывания позиции
//----+ +--------------------------------------------------------------------------+
//---- Целые переменные для минимума расчётных баров
int MinBar_Up1, MinBar_Dn1;
int MinBar_Up2, MinBar_Dn2;
//+==================================================================+
//| 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 functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up1 на корректность
   TimeframeCheck("Timeframe_Up1", Timeframe_Up1);                            
//---- Проверка значения переменной Timeframe_Up2 на корректность
   TimeframeCheck("Timeframe_Up2", Timeframe_Up2);                              
//---- Проверка значения переменной Timeframe_Dn1 на корректность
   TimeframeCheck("Timeframe_Dn1", Timeframe_Dn1);  
//---- Проверка значения переменной Timeframe_Dn2 на корректность
   TimeframeCheck("Timeframe_Dn2", Timeframe_Dn2);  
//---- Инициализация переменных            
   MinBar_Up1 = 3 + 3 * XLength_Up1 + 30;
   MinBar_Up2 = 3 + 3 * XLength_Up2 + 30;
   MinBar_Dn1 = 3 + 3 * XLength_Dn1 + 30;
   MinBar_Dn2 = 3 + 3 * XLength_Dn2 + 30;                                    
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+

    //---- Завершение деинициализации эксперта
    return(0);
//----+
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   double Osc1, Osc2;
   //----+ Объявление статических переменных
   //----+ +---------------------------------------------------------------+
   static int LastBars_Up1, LastBars_Dn1;
   static bool BUY_Sign1, BUY_Stop1, SELL_Sign1, SELL_Stop1;
   //----+ +---------------------------------------------------------------+
   static int LastBars_Up2, LastBars_Dn2;
   static bool BUY_Sign2, BUY_Stop2, SELL_Sign2, SELL_Stop2;
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ 1
   if (Test_Up1)
    {
      int IBARS_Up1 = iBars(NULL, Timeframe_Up1);

      if (IBARS_Up1 >= MinBar_Up1)
       {
         if (LastBars_Up1 != IBARS_Up1)
          {
           //----+ Иницмализация переменных
           BUY_Sign1 = false;
           BUY_Stop1 = false;
           LastBars_Up1 = IBARS_Up1; 

           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           Osc1 = iCustom(NULL, Timeframe_Up1,
                 "JCCIX", JLength_Up1, XLength_Up1,
                                Phase_Up1, IPC_Up1, 0, 1);

           Osc2 = iCustom(NULL, Timeframe_Up1,
                 "JCCIX", JLength_Up1, XLength_Up1,
                                Phase_Up1, IPC_Up1, 0, 2);

           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if (Osc2 < IndLevel_Up1)
             if (Osc1 > IndLevel_Up1)
                          BUY_Sign1 = true;

           if (Osc1 < IndLevel_Up1)
                          BUY_Stop1 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenBuyOrder1(BUY_Sign1, 1, Money_Management_Up1,
                                          STOPLOSS_Up1, TAKEPROFIT_Up1))
                                                                 return(-1);
          if (ClosePos_Up1)
                if (!CloseOrder1(BUY_Stop1, 1))
                                        return(-1);

          if (!Make_TreilingStop(1, TRAILINGSTOP_Up1))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ 2
   if (Test_Up2)
    {
      int IBARS_Up2 = iBars(NULL, Timeframe_Up2);

      if (IBARS_Up2 >= MinBar_Up2)
       {
         if (LastBars_Up2 != IBARS_Up2)
          {
           //----+ Иницмализация переменных
           BUY_Sign2 = false;
           BUY_Stop2 = false;
           LastBars_Up2 = IBARS_Up2; 

           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           Osc1 = iCustom(NULL, Timeframe_Up2,
                 "JCCIX", JLength_Up2, XLength_Up2,
                                Phase_Up2, IPC_Up2, 0, 1);

           Osc2 = iCustom(NULL, Timeframe_Up2,
                 "JCCIX", JLength_Up2, XLength_Up2,
                                Phase_Up2, IPC_Up2, 0, 2);

           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if (Osc2 < IndLevel_Up2)
             if (Osc1 > IndLevel_Up2)
                          BUY_Sign2 = true;

           if (Osc1 < IndLevel_Up2)
                          BUY_Stop2 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenBuyOrder1(BUY_Sign2, 2, Money_Management_Up2,
                                          STOPLOSS_Up2, TAKEPROFIT_Up2))
                                                                 return(-1);
          if (ClosePos_Up2)
                if (!CloseOrder1(BUY_Stop2, 2))
                                        return(-1);

          if (!Make_TreilingStop(2, TRAILINGSTOP_Up2))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ 1
   if (Test_Dn1)
    {
      int IBARS_Dn1 = iBars(NULL, Timeframe_Dn1);

      if (IBARS_Dn1 >= MinBar_Dn1)
       {
         if (LastBars_Dn1 != IBARS_Dn1)
          {
           //----+ Иницмализация переменных
           SELL_Sign1 = false;
           SELL_Stop1 = false;
           LastBars_Dn1 = IBARS_Dn1; 

           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           Osc1 = iCustom(NULL, Timeframe_Dn1,
                 "JCCIX", JLength_Dn1, XLength_Dn1,
                                Phase_Dn1, IPC_Dn1, 0, 1);

           Osc2 = iCustom(NULL, Timeframe_Dn1,
                 "JCCIX", JLength_Dn1, XLength_Dn1,
                                Phase_Dn1, IPC_Dn1, 0, 2);

           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if (Osc2 > IndLevel_Dn1)
             if (Osc1 < IndLevel_Dn1)
                          SELL_Sign1 = true;

           if (Osc1 > IndLevel_Dn1)
                          SELL_Stop1 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenSellOrder1(SELL_Sign1, 3, Money_Management_Dn1,
                                          STOPLOSS_Dn1, TAKEPROFIT_Dn1))
                                                                 return(-1);
          if (ClosePos_Dn1)
                if (!CloseOrder1(SELL_Stop1, 3))
                                        return(-1);

          if (!Make_TreilingStop(3, TRAILINGSTOP_Dn1))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ 2
   if (Test_Dn2)
    {
      int IBARS_Dn2 = iBars(NULL, Timeframe_Dn2);

      if (IBARS_Dn2 >= MinBar_Dn2)
       {
         if (LastBars_Dn2 != IBARS_Dn2)
          {
           //----+ Иницмализация переменных
           SELL_Sign2 = false;
           SELL_Stop2 = false;
           LastBars_Dn2 = IBARS_Dn2; 

           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           Osc1 = iCustom(NULL, Timeframe_Dn2,
                 "JCCIX", JLength_Dn2, XLength_Dn2,
                                Phase_Dn2, IPC_Dn2, 0, 1);

           Osc2 = iCustom(NULL, Timeframe_Dn2,
                 "JCCIX", JLength_Dn2, XLength_Dn2,
                                Phase_Dn2, IPC_Dn2, 0, 2);

           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if (Osc2 > IndLevel_Dn2)
             if (Osc1 < IndLevel_Dn2)
                          SELL_Sign2 = true;

           if (Osc1 > IndLevel_Dn2)
                          SELL_Stop2 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenSellOrder1(SELL_Sign2, 4, Money_Management_Dn2,
                                          STOPLOSS_Dn2, TAKEPROFIT_Dn2))
                                                                 return(-1);
          if (ClosePos_Dn2)
                if (!CloseOrder1(SELL_Stop2, 4))
                                        return(-1);

           if (!Make_TreilingStop(4, TRAILINGSTOP_Dn2))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
//----+ 

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

Этот код оказался в два раза больше, чем код, который я представил в предыдущей статье. Кстати, можете себе представить, как бы выглядел код этого эксперта, ежели бы в нём вместо всех обращений к функциям управления ордерами был развёрнутый программный код! Следует отметить, что в данном эксперте появляются новые входные переменные: IndLevel_Up1, IndLevel_Up2, IndLevel_Dn1, IndLevel_Dn2. Переменные IndLevel_Up1, IndLevel_Up2 определяют значение уровней Uplevel и DownLevel для двух алгоритмов длинных позиций, а переменные IndLevel_Dn1, IndLevel_Dn2 определяют значение уровней DownLevel и Uplevel для двух алгоритмов коротких позиций.

 

При оптимизации этого эксперта следует учитывать тот факт, что величина этих уровней может изменяться в пределах от -1.0 и до +1.0. Если вы захотите заменить в этом эксперте осциллятор JCCIX на какой-нибудь другой, то следует учесть, что максимальное и минимальное значения этих уровней могут оказаться другими. Сам изначальный индикатор JCCIX представляет собой аналог индикатора CCI, в котором сделана замена алгоритма сглаживания с помощью обычных скользящих средних на JMA и ультралинейное сглаживания. В эксперте используются трейлинстопы (Trailing Stop), значения которых определяются значениями входных параметров эксперта типа TRAILINGSTOP_Up1, TRAILINGSTOP_Up2, TRAILINGSTOP_Dn1, TRAILINGSTOP_Dn2. Во всех остальных отношениях этот эксперт вполне аналогичен эксперту из предыдущей статьи.

Замена непосредственного входа в рынок на отложенные ордера

Во многих случаях в экспертах, аналогичных тому, код которого был только что рассмотрен, замена в эксперте непосредственного входа в рынок на отложенные ордера позоляет гораздо точнее входить в рынок и получать большую прибыль и при этом иметь меньшую вероятность нарваться на стоплосс (Stop Loss). Набор функций из файла Lite_EXPERT1.mqh позволяет без проблем сделать подобную замену.

 

Вполне достаточно сделать замену функций OpenBuyOrder1() и OpenSellOrder1() на OpenBuyLimitOrder1() и OpenSellLimitOrder1() соответственно. При подобной замене функций возникает необходимость инициализации новых входных переменных этих функций: LEVEL и Expiration. Например, можно достроить торговую систему, в которой значения переменной LEVEL будет определяться входными параметрами эксперта. Дату отмены отложенного ордера можно определить временем следующей смены текущего бара.

 

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

//Для работы эксперта в папке Metatrader\EXPERTS\indicators небходимо
//наличие индикатора 5c_OsMA.mq4
//+==================================================================+
//|                                                        Exp_5.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 int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
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 int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
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 MinBar_Up, 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("Параметр ",Name,
                     " не может ", "быть равным ", Timeframe, "!!!"));    
//----+
  }
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   TimeframeCheck("Timeframe_Up", Timeframe_Up);                                                          
//---- Проверка значения переменной Timeframe_Dn на корректность
   TimeframeCheck("Timeframe_Dn", Timeframe_Dn);  
//---- Инициализация переменных            
   MinBar_Up  = 3 + MathMax(FastEMA_Up, SlowEMA_Up) + SignalSMA_Up;
   MinBar_Dn  = 3 + MathMax(FastEMA_Dn, SlowEMA_Dn) + SignalSMA_Dn;                              
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+

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

      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных
           BUY_Sign1 = false;
           BUY_Stop1 = 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 (Osc2 < IndLevel_Up)
             if (Osc1 > IndLevel_Up)
                          BUY_Sign1 = true;

           if (Osc1 < IndLevel_Up)
                          BUY_Stop1 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenBuyLimitOrder1(BUY_Sign1, 1,
              Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up,
                                            PriceLevel_Up, StopTime_Up))
                                                                 return(-1);
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop1, 1))
                                        return(-1);

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

      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных
           SELL_Sign1 = false;
           SELL_Stop1 = 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 (Osc2 > IndLevel_Dn)
             if (Osc1 < IndLevel_Dn)
                          SELL_Sign1 = true;

           if (Osc1 > IndLevel_Dn)
                          SELL_Stop1 = true;                                          
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (!OpenSellLimitOrder1(SELL_Sign1, 2,
              Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn,
                                            PriceLevel_Dn, StopTime_Dn))
                                                                 return(-1);
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop1, 2))
                                        return(-1);

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

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

Вместо двух пробойных уровней UpLevel и DownLevel используется один уровень и поэтому в этом эксперте используется всего два алгоритма управления позициями. Обычно этот уровень в торговой системе связанной с OsMA выбирается равным нулю, но я его оставил во внешних переменных эксперта и его можно менять. Тут следует учесть, что у индикатора OSMA нет максимума и минимума, в пределах которых изменяется индикатор, а стало быть и пробойный уровень не имеет ограничения своих значений сверху и снизу, но, я повторюсь, обычно он равен нулю. Для определения времени отмены отложенных ордеров используются статические переменные StopTime_Up и StopTime_Dn, которые один раз при смене бара инициализируются временем следующей смены бара.

Заключение

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

 

Что касается бэк-тестинга, то я ещё раз повторюсь и скажу, что это, пожалуй, самый лучший способ достаточно быстрого избавления от «розовых» очков относительно результатов оптимизаций начинающего экспертописателя. Никаких проблем с написанием экспертов, показывающих самые невероятные результаты при подгонке под историю, нет! Тут невозможно открыть Америку! Гораздо сложнее понять, как использовать имеющихся экспертов так, чтобы вовремя избежать ситуаций, когда рынок уходит от оптимальных оптимизированных параметров экспертов.

Прикрепленные файлы:
 EXPERTS.zip (7.5 Kb)
 INCLUDE.zip (24.3 Kb)
 indicators.zip (5.6 Kb)
 TESTER.zip (5.3 Kb)
Источник: mql4.com

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

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

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