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

Nikolay Kositsin | 1 апреля, 2008


Введение


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

Механизация бэктестинга


Для решения этой задачи следует:

1. Вписать под шапкой любого, интересующего вас эксперта строку следующего содержания:

//+==================================================================+
//| Custom BackTesting function                                      |
//+==================================================================+
#include <IsBackTestingTime.mqh>

Посредством этой директивы мы включаем в код эксперта функцию IsBackTestingTime(). Вполне естественно следует не забыть поместить файл IsBackTestingTime.mqh в папку INCLUDE. Эта функция:

bool IsBackTestingTime()
 {
 } 

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

//---- Объявления внешних переменных для бэктестинга
extern datetime Start_Time = D'2007.01.01'; // время старта нулевой оптимизации
extern int Opt_Period = 3; // период оптимизации в месяцах, если значение периода меньше нуля, то все параметры измеряются в днях
extern int Test_Period = 2; // период тестирования в месяцах
extern int Period_Shift = 1; // шаг сдвига периода оптимизации в месяцах
extern int Opt_Number = 0; // номер оптимизации

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

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


 //----+ Выполнение условий бэктестинга  
   if (!IsBackTestingTime())
                       return(0);

Схематично это будет выглядеть следующим образом:

//+==================================================================+
//|                                                 Exp_BackTest.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//+==================================================================+
//| Custom BackTesting function                                      |
//+==================================================================+
#include <IsBackTestingTime.mqh>
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА 
//---- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ЭКСПЕРТА
//+==================================================================+
//| ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ ЭКСПЕРТА                                |
//+==================================================================+
 
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//----+ +------------------------------------------------------------+    
//---- КОД ДЛЯ ИНИЦИАЛИЗАЦИИ ЭКСПЕРТА
//----+ +------------------------------------------------------------+                 
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Выполнение условий бэктестинга  
   if (!IsBackTestingTime())
                       return(0);             
   //----+ +---------------------------------------------------------+    
   //----+ КОД АЛГОРИТМА ЭКСПЕРТА
   //----+ +---------------------------------------------------------+
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

Кому интерсно решение подобной задачи на примере готового эксперта, могут посмотреть код эксперта Exp_5_1.mq4, являющегося модифицированным для бэктестинга экспертом Exp_5.mq4 из предыдущей статьи. По большому счёту какой-то значительной разницы в оптимизации подобного эксперта по сравнению с обычным экспертом нет. Разве что, на мой взгляд, не стоит оптимизировать бэктестерные переменные, за исключением переменной Opt_Number, хотя как знать, тут у каждого свой подход должен быть.

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

Но даже в этом случае подобный подход очень сильно ускоряет понимание поведения эксперта. Ну и при подобном мероприятии следует помнить, что значение внешней переменной Opt_Number может изменяться от нуля и до некоторого максимального значения, которое можно определить следующим образом: от общего периода, на котором проводятся все бэктестерные оптимизации взятого в месяцах, отнимается период бэктестерной оптимизации в месяцах (Opt_Period) и отнимается период бэктестинга (Test_Period). К полученной величине добавить единицу. Полученный результат и будет максимумом для переменной Opt_Number, в случае, ежели Period_Shift равен единице.


Торговые системы, основанные на пересечениях двух мувингов

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

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

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

//+==================================================================+
//|                                                        Exp_6.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_Up = true;//фильтр направления расчётов сделок
extern int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
extern int    LengthA_Up = 4;  // глубина сглаживания быстрого мувинга
extern int    PhaseA_Up = 100; // параметр, изменяющийся в пределах 
          //-100 ... +100, влияет на качество переходного процесса быстрого мувинга; 
extern int    IPCA_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    LengthB_Up = 4; // приращение глубины сглаживания медленного мувинга к быстрому
extern int    PhaseB_Up = 100; // параметр, изменяющийся в пределах 
          //-100 ... +100, влияет на качество переходного процесса  медленного мувинга; 
extern int    IPCB_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 int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
extern int    LengthA_Dn = 4;  // глубина сглаживания быстрого мувинга
extern int    PhaseA_Dn = 100; // параметр, изменяющийся в пределах
         // -100 ... +100, влияет на качество переходного процесса быстрого мувинга; 
extern int    IPCA_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    LengthB_Dn = 4; // приращение глубины сглаживания медленного мувинга к быстрому
extern int    PhaseB_Dn = 100; // параметр, изменяющийся в пределах
         // -100 ... +100, влияет на качество переходного процесса  медленного мувинга; 
extern int    IPCB_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;
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   if (Timeframe_Up != 1)
    if (Timeframe_Up != 5)
     if (Timeframe_Up != 15)
      if (Timeframe_Up != 30)
       if (Timeframe_Up != 60)
        if (Timeframe_Up != 240)
         if (Timeframe_Up != 1440)
           Print(StringConcatenate("Параметр Timeframe_Up не может ",  
                                  "быть равным ", Timeframe_Up, "!!!"));
//---- Проверка значения переменной Timeframe_Dn на корректность 
   if (Timeframe_Dn != 1)
    if (Timeframe_Dn != 5)
     if (Timeframe_Dn != 15)
      if (Timeframe_Dn != 30)
       if (Timeframe_Dn != 60)
        if (Timeframe_Dn != 240)
         if (Timeframe_Dn != 1440)
           Print(StringConcatenate("Параметр Timeframe_Dn не может ",  
                                 "быть равным ", Timeframe_Dn, "!!!")); 
//---- Инициализация переменных             
   MinBar_Up = 4 + 30;
   MinBar_Dn = 4 + 30;                                        
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- Завершение деинициализации эксперта
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   int    bar;
   double MovA[2], MovB[2];
   //----+ Объявление статических переменных
   static int LastBars_Up, LastBars_Dn;
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;
   
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ
   if (Test_Up)
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      
      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных 
           BUY_Sign = false;
           BUY_Stop = false;
           LastBars_Up = IBARS_Up;
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           for(bar = 1; bar < 3; bar++)
                     MovA[bar - 1] =                  
                         iCustom(NULL, Timeframe_Up, 
                                "JJMA", LengthA_Up, PhaseA_Up, 
                                                   0, IPCA_Up, 0, bar);
           for(bar = 1; bar < 3; bar++)
                MovB[bar - 1] =                  
                   iCustom(NULL, Timeframe_Up, 
                     "JJMA", LengthA_Up + LengthB_Up, PhaseB_Up, 
                                                   0, IPCB_Up, 0, bar);
                                                   
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if ( MovA[1] < MovB[1])
               if ( MovA[0] > MovB[0])
                        BUY_Sign = true;
                          
            if ( MovA[0] > MovB[0])
                        BUY_Stop = 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);
      
      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных 
           SELL_Sign = false;
           SELL_Stop = false;
           LastBars_Dn = IBARS_Dn; 
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ
           for(bar = 1; bar < 3; bar++)
                     MovA[bar - 1] =                  
                         iCustom(NULL, Timeframe_Dn, 
                                "JJMA", LengthA_Dn, PhaseA_Dn, 
                                                   0, IPCA_Dn, 0, bar);
           for(bar = 1; bar < 3; bar++)
                MovB[bar - 1] =                  
                   iCustom(NULL, Timeframe_Dn, 
                     "JJMA", LengthA_Dn + LengthB_Dn, PhaseB_Dn, 
                                                   0, IPCB_Dn, 0, bar);
           
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if ( MovA[1] > MovB[1])
               if ( MovA[0] < MovB[0])
                        SELL_Sign = true;
                          
            if ( MovA[0] < MovB[0])
                        SELL_Stop = 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);
  }
//+------------------------------------------------------------------+

Торговые системы, основанные на пересечениях двух осцилляторов


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

Для выставления отложенных ордеров BuyLimit алгоритм будет выглядеть следующим образом:

Аналогичный алгоритм для ордеров типа SellLimit получим в следующем виде:



Код этого эксперта во многом аналогичен коду предыдущего эксперта:

//+==================================================================+
//|                                                        Exp_7.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_Up = true; //фильтр направления расчётов сделок
extern int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
extern int    FST_period_Up = 12;  // период быстрого мувинга
extern int    SLO_period_Up = 22; // приращение периода медленного мувинга к быстрому
extern int    SIGN_period_Up = 8; // период сигнальной линии
extern int    Price_Up = 0;  // выбор цен, по которым производится расчёт MACD
extern int    STOPLOSS_Up = 50;  // стоплосс
extern int    TAKEPROFIT_Up = 100; // тейкпрофит
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 int    FST_period_Dn = 12;  // период быстрого мувинга
extern int    SLO_period_Dn = 22; // приращение периода медленного мувинга к быстрому
extern int    SIGN_period_Dn = 8; // период сигнальной линии
extern int    Price_Dn = 0;  // выбор цен, по которым производится расчёт MACD
extern int    STOPLOSS_Dn = 50;  // стоплосс
extern int    TAKEPROFIT_Dn = 100; // тейкпрофит
extern int    PriceLevel_Dn =40;  // разница между текущей ценой и 
                                         // ценой срабатывания отложенного ордера
extern bool   ClosePos_Dn = true; // разрешение принудительного закрывания позиции
//----+ +---------------------------------------------------------------------------+
//---- Целые переменные для минимума расчётных баров
int MinBar_Up, MinBar_Dn;
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   if (Timeframe_Up != 1)
    if (Timeframe_Up != 5)
     if (Timeframe_Up != 15)
      if (Timeframe_Up != 30)
       if (Timeframe_Up != 60)
        if (Timeframe_Up != 240)
         if (Timeframe_Up != 1440)
           Print(StringConcatenate("Параметр Timeframe_Up не может ",  
                                  "быть равным ", Timeframe_Up, "!!!"));
//---- Проверка значения переменной Timeframe_Dn на корректность 
   if (Timeframe_Dn != 1)
    if (Timeframe_Dn != 5)
     if (Timeframe_Dn != 15)
      if (Timeframe_Dn != 30)
       if (Timeframe_Dn != 60)
        if (Timeframe_Dn != 240)
         if (Timeframe_Dn != 1440)
           Print(StringConcatenate("Параметр Timeframe_Dn не может ",  
                                 "быть равным ", Timeframe_Dn, "!!!")); 
//---- Инициализация переменных             
   MinBar_Up = 4 + FST_period_Up + SLO_period_Up + SIGN_period_Up;
   MinBar_Dn = 4 + FST_period_Dn + SLO_period_Dn + SIGN_period_Dn;                                        
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- Завершение деинициализации эксперта
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   int    bar;
   double MovA[2], MovB[2];
   //----+ Объявление статических переменных
   static int LastBars_Up, LastBars_Dn;
   static datetime StopTime_Up, StopTime_Dn; 
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;
   
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ
   if (Test_Up)
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      
      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных 
           BUY_Sign = false;
           BUY_Stop = false;
           LastBars_Up = IBARS_Up;
           StopTime_Up = iTime(NULL, Timeframe_Up, 0)
                                            + 60 * Timeframe_Up;
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ           
           for(bar = 1; bar < 3; bar++)
             MovA[bar - 1] = iMACD(NULL, Timeframe_Up, 
                      FST_period_Up, FST_period_Up + SLO_period_Up,
                                         SIGN_period_Up, Price_Up, 0, bar);
                          
           for(bar = 1; bar < 3; bar++)
             MovB[bar - 1] = iMACD(NULL, Timeframe_Up, 
                      FST_period_Up, FST_period_Up + SLO_period_Up,
                                         SIGN_period_Up, Price_Up, 1, bar);
                 
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if ( MovA[1] < MovB[1])
               if ( MovA[0] > MovB[0])
                        BUY_Sign = true;
                          
            if ( MovA[0] > MovB[0])
                        BUY_Stop = 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 (Test_Dn)
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      
      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных 
           SELL_Sign = false;
           SELL_Stop = false;
           LastBars_Dn = IBARS_Dn;
           StopTime_Dn = iTime(NULL, Timeframe_Dn, 0) 
                                            + 60 * Timeframe_Dn; 
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ          
           for(bar = 1; bar < 3; bar++)
             MovA[bar - 1] = iMACD(NULL, Timeframe_Dn, 
                      FST_period_Dn, FST_period_Dn + SLO_period_Dn,
                                         SIGN_period_Dn, Price_Dn, 0, bar);
                          
           for(bar = 1; bar < 3; bar++)
             MovB[bar - 1] = iMACD(NULL, Timeframe_Dn, 
                      FST_period_Dn, FST_period_Dn + SLO_period_Dn,
                                         SIGN_period_Dn, Price_Dn, 1, bar);
           
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                          
           if ( MovA[1] > MovB[1])
               if ( MovA[0] < MovB[0])
                        SELL_Sign = true;
                          
            if ( MovA[0] < MovB[0])
                        SELL_Stop = 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);
        }
     }
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

Заключение

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