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

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

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

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

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

Общая схема строения экспертов

 

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

Торговая система на основе изменения направления движения мувинга

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

а сигналов для продажи — смена направления движения мувинга с возрастающего на падающее:

 

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

 

Код эксперта

//+==================================================================+
//|                                                        Exp_1.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    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 int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
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;
//+==================================================================+
//| 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 + 39 + 30;
   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;
   //----+ Объявление статических переменных
   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++)
                     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 (dMov23 < 0)
              if (dMov12 > 0)
                        BUY_Sign = true;

           if (dMov12 < 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++)
                     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 (dMov23 > 0)
              if (dMov12 < 0)
                       SELL_Sign = true;

           if (dMov12 > 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);
  }
//+------------------------------------------------------------------+

Для получения сигналов покупки и продажи во всех блоках используются два абсолютно одинаковых независимых алгоритма, каждый из которых имеет свои внешние параметры эксперта для оптимизации. По моим наблюдениям подобный подход к написанию этого эксперта оказывается значительно более прибыльным, чем вариант со всего одним алгоритмом для определения сигналов Buy и Sell сделок. Кому интересен вариант с одним мувингом для лонговых и шортовых позиций, могут заняться этим алгоритмом, который представлен в эксперте EXP_0.mq4, а мы продолжим исследование эксперта с двумя мувингами. Эксперт может открывать всего по одной позиции в Buy и по одной позиции в Sell направлениях одновременно на одной торговой паре. Эксперт совершает сделки с рынка. Сделки закрываются по ордерам стоплосс и тейкпрофит. При возникновении трендовых сигналов против открытых позиций в эксперте имеется возможность принудительно закрывать сделки. Метод получения сигналов для выхода из сделок аналогичен методу получения сигналов для входа, но имеет противоположный характер.

 

Содержание файла Lite_EXPERT1.mqh

Исходя из соображений максимальной экономии собственного труда, следует стремиться к тому, чтобы использовать при написании экспертов максимальное количество универсальных пользовательских функций. И в дальшейшем производить сборку кода эксперта, как это делают на современных заводах по производству любой техники, на которых всякое новое изделие содержит максимальное количество унифицированных и стандартных деталей, блоков и модулей! По этой причине во всех экспертах в блоке «Совершение сделок» используется несколько универсальных пользовательских функций, которые включаются в код эксперта директивой #include <Lite_EXPERT1.mqh> :

bool OpenBuyOrder1
(
  bool BUY_Signal, int MagicNumber,
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool OpenSellOrder1
(
  bool SELL_Signal, int MagicNumber,
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

CountLastTime
(
  int lastError
)

bool CloseOrder1
(
  bool Stop_Signal, int MagicNumber
)

int StopCorrect
(
  string symbol, int Stop
)

bool MarginCheck
(
  string symbol, int Cmd, double& Lot
)

double GetFreeMargin()

bool DeleteOrder1
(
  bool& CloseStop, int MagicNumber
)

bool OpenBuyLimitOrder1
(
  bool& Order_Signal, int MagicNumber,
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenBuyStopOrder1
(
  bool& Order_Signal, int MagicNumber,
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenSellLimitOrder1
(
  bool& Order_Signal, int MagicNumber,
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenSellStopOrder1
(
  bool& Order_Signal, int MagicNumber,
       double Money_Management, int STOPLOSS, int TAKEPROFIT,
                                      int LEVEL, datetime Expiration
)

bool OpenBuyOrder2
(
  bool BUY_Signal, int MagicNumber,
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool OpenSellOrder2
(
  bool SELL_Signal, int MagicNumber,
             double Money_Management, int STOPLOSS, int TAKEPROFIT
)

bool Make_TreilingStop
(
  int MagicNumber, int TRAILINGSTOP
)

Функция OpenBuyOrder1() открывает длинные позиции при обращении к ней, если значение внешней переменной BUY_Signal равно true и отсутствуют открытые позиции, идентификационное («магическое») число которых равно значению переменной MagicNumber. Значения внешних переменных STOPLOSS и TAKEPROFIT определяют соответственно величину стоплосса и тейкпрофита в пунктах. Значение переменной Money_Management может изменяться от нуля и до единицы. Эта переменная определяет, какая часть средств от всех имеющихся в наличии на депозите расходуется для совершения сделки. Если сделать значение этой переменной меньше нуля, то в этом случае функция OpenBuyOrder1() будет использовать её значение, как величину лота! Абсолютно аналогично открывает короткие позиции при обращении к ней функция OpenSellOrder1(). Обе функции открывают позиции независимо друг от друга, но в течение одиннадцати секунд на сервер может быть подана всего одна команда для совершения сделки. Помимо совершения сделок функции OpenBuyOrder1() и OpenSellOrder1() пишут в лог файл информацию по открываемым сделкам.

 

Функция CloseOrder1() при обращении к ней, если переменная Stop_Signal принимает значение true, закрывает позицию с идентификационным («магическим») числом, равным значению переменной MagicNumber.

 

Функция StopCorrect() принимает в качестве параметра Stop значение стоплосса или тейкпрофита, проверяет его на минимально допустимое, при необходимости корректирует до минимально допустимого и возвращает его с учётом возможной коррекции.

 

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

 

Функции OpenBuyOrder1(), OpenSellOrder1() и CloseOrder1() используются внутри функции start(), в то время как функции StopCorrect() и MarginCheck() предназначены для использования внутри кода функций OpenBuyOrder1() и OpenSellOrder1().

 

При корректном завершении выполнения функций OpenBuyOrder1(), OpenSellOrder1() и CloseOrder1() любая из них возвращает значение true, при возникновении ошибок при выполнении этих функций возвращаемое значение оказывается равным false. Все три функции OpenBuyOrder1(), OpenSellOrder1() и CloseOrder1() при совершении сделок значения своих внешних переменных BUY_Signal, SELL_Signal и Stop_Signal сбрасывают в false!

 

Функция GetFreeMargin() возвращает размер свободных средств текущем счете с учётом прибылей и убытков, который можно использовать для открывания позиций. Эта функция используется для расчёта величины лота.

 

Функция CountLastTime() делает инициализацию переменной LastTime с учётом ошибки, возникшей при выполнении торговой операции. Обращение к этой функции следует делать сразу после выполнения торговой операции, например:

  //----+ Открываем позицию на покупку    
  ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3,
            Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime);

  //---- Расчёт паузы между торговыми операциями          
  CountLastTime(GetLastError());

 

Кроме вышеперечисленных функций файл Lite_EXPERT.mqh содержит ещё четыре функции для выставления отложенных ордеров и одну функцию для удаления отложенных ордеров: OpenBuyLimitOrder(), OpenBuyStopOrder1(), OpenSellLimitOrder1(), OpenSellStopOrder1(), DeleteOrder1(). Назначение внешних переменных этих функций абсолютно аналогичное и понятное из названий,. Новые переменные LEVEL и Expiration необходимы для передачи функциям расстояния в пунктах от текущей цены, на котором ставятся отложенные ордера, и время истечения отложенного ордера соответственно.

 

Помимо всех, уже рассмотренных функций в файле имеются ещё две функции: OpenBuylOrder2() и OpenSellOrder2(), являющиеся полными аналогами функций OpenBuyOrder1() и OpenSellOrder1(), за исключением одной детали. Эти функции сначала открывают позиции без ордеров стоплосс и тейкпрофит, а после этого модифицируют уже открытые позиции значениями стоплосса и тейкпрофита. Эти функции необходимы для работы в составе экспертов, предполагающихся для использования с брокерами, в регламенте торговых операций которых при открытии позиции по рынку Клиент не может выставить ордера Stop Loss и Take Profit в связи с типом исполнения «Market Watch». Выставление ордеров типа Stop Loss и Take Profit у таких брокеров происходит по средствам модификации открытой позиции.

 

И последняя функция, которая входит в состав файла — это Make_TreilingStop(). Функия выполняет стандартный трейлинстоп.

 

Помимо функций файл Lite_EXPERT.mqh содержит целую переменную LastTime, которая объявлена на глобальном уровне по причине её одновременного использования во всех функциях для открытия ордеров.

 

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

//+==================================================================+
//| OpenBuyOrder1()                                                  |
//+==================================================================+
bool OpenBuyOrder1
        (bool& BUY_Signal, int MagicNumber,
                double Money_Management, int STOPLOSS, int TAKEPROFIT)
{
//----+
  if (!BUY_Signal)
           return(true);
  //---- Проверка на истечение минимального интервала времени
                                    //между двумя торговыми операциями        
  if (TimeCurrent() < LastTime)
                          return(true);
  int total = OrdersTotal();
  //---- Проверка на наличие открытой позиции
          //с магическим числом равным значению переменной MagicNumber
  for(int ttt = total - 1; ttt >= 0; ttt--)    
      if (OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES))
                      if (OrderMagicNumber() == MagicNumber)
                                                      return(true);
  string OpderPrice, Symb = Symbol();
  int    ticket, StLOSS, TkPROFIT;
  double LOTSTEP, MINLOT, MAXLOT, MARGINREQUIRED;
  double FreeMargin, LotVel, Lot, ask, Stoploss, TakeProfit;                                                 

  //----+ расчёт величины лота для открывания позиции
  if (Money_Management > 0)
    {        
      MARGINREQUIRED = MarketInfo(Symb, MODE_MARGINREQUIRED);
      if (MARGINREQUIRED == 0.0)
                    return(false);

      LotVel = GetFreeMargin()
               * Money_Management / MARGINREQUIRED;        
    }
  else
    LotVel = MathAbs(Money_Management);
  //----  
  LOTSTEP = MarketInfo(Symb, MODE_LOTSTEP);
  if (LOTSTEP <= 0)
              return(false);  
  //---- нормирование величины лота до ближайшего стандартного значения
  Lot = LOTSTEP * MathFloor(LotVel / LOTSTEP);  

  //----+ проверка лота на минимальное допустимое значение
  MINLOT = MarketInfo(Symb, MODE_MINLOT);
  if (MINLOT < 0)
         return(false);
  if (Lot < MINLOT)
          return(true);

  //----+ проверка лота на максимальное допустимое значение
  MAXLOT = MarketInfo(Symb, MODE_MAXLOT);
  if (MAXLOT < 0)
         return(false);
  if (Lot > MAXLOT)
          Lot = MAXLOT;

  //----+ проверка величины лота на достаточность средств на счёте  
  if (!MarginCheck(Symb, OP_BUY, Lot))
                               return(false);
  if (Lot < MINLOT)
          return(true);
  //----
  ask = NormalizeDouble(Ask, Digits);
  if (ask == 0.0)
          return(false);
  //----            
  StLOSS = StopCorrect(Symb, STOPLOSS);
  if (StLOSS < 0)
          return(false);  
  //----
  Stoploss = NormalizeDouble(ask - StLOSS * Point, Digits);
  if (Stoploss < 0)
         return(false);
  //----      
  TkPROFIT = StopCorrect(Symb, TAKEPROFIT);
  if (TkPROFIT < 0)
          return(false);  
  //----              
  TakeProfit = NormalizeDouble(ask + TkPROFIT * Point, Digits);
  if (TakeProfit < 0)
         return(false);

  Print(StringConcatenate
         ("Открываем по ", Symb,
            " позицию на покупку с магическим числом ", MagicNumber));

  //----+ Открываем позицию на покупку    
  ticket = OrderSend(Symb, OP_BUY, Lot, ask, 3,
            Stoploss, TakeProfit, NULL, MagicNumber, 0, Lime);

  //---- Расчёт паузы между торговыми операциями          
  CountLastTime(GetLastError());
  //----
  if(ticket > 0)
   {
     if (OrderSelect(ticket, SELECT_BY_TICKET))
       {
         BUY_Signal = false;
         OpderPrice = DoubleToStr(OrderOpenPrice(), Digits);  
         Print(StringConcatenate(Symb, " BUY ордер с тикетом №",
                ticket, " и магическим числом ", OrderMagicNumber(),
                                         " открыт по цене ",OpderPrice));
         return(true);
       }
     else
       {
         Print(StringConcatenate("Не удалось окрыть ", Symb,
            " BUY ордер с магическим числом ", MagicNumber, "!!!"));
         return(true);
       }
    }
  else
    {
      Print(StringConcatenate("Не удалось окрыть ", Symb,
           " BUY ордер с магическим числом ", MagicNumber, "!!!"));
      return(true);
    }
  //----
  return(true);
//----+
}

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

 

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

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

Дополнительные пояснения к коду эксперта

 

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

//---- Инициализация переменных
   MinBar_Up = 4 + 39 + 30;

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

if (IBARS_Up >= MinBar_Up)

Проверка

if (LastBars_Up != IBARS_Up)

необходима для того, чтобы эксперт не занимался пересчётом сигналов для входа в рынок на каждом тике, а делал это только при смене бара, что весьма серьёзно экономит ресурсы компьютера и значительно сокращает время оптимизации эксперта. Именно по этой причине переменная LastBars_Up объявлена как статическая, для запоминания количества баров на предыдущем тике функции int start(). Инициализация переменных BUY_Sign, BUY_Stop для входа и выхода из рынка происходит всего один раз при смене бара, а значение они должны держать, пока не будет совершена или закрыта сделка, либо пока не произойдёт ещё одна смена бара. По этой причине эти переменные объявлены как статические. Я так полагаю, что остальные подробности строения эксперта вполне понятны из его кода и в дальнейших комментариях не нуждаются.

Замена мувинга в эксперте

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

           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ        
           for(bar = 1; bar <= 3; bar++)
                     Mov[bar - 1]=                  
                         iCustom(NULL, Timeframe_Up,
                                "J2JMA", Length1_Up, Length2_Up,
                                             Phase1_Up, Phase2_Up,  
                                                  0, IPC_Up, 0, bar);

Вся задача состоит в том, чтобы немного переписать блок внешних параметров эксперта (я опять привожу в качестве примера только половину алгоритма!):

 

//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК
extern bool   Test_Up = true;//фильтр направления расчётов сделок
extern int    Timeframe_Up=240;
extern double Money_Management_Up = 0.1;
extern int    Length1_Up = 4;  // глубина первого сглаживания
extern int    Phase1_Up = 100; // параметр первого сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;
extern int    Length2_Up = 4;  // глубина второго сглаживания
extern int    Phase2_Up = 100; // параметр второго сглаживания,
       //изменяющийся в пределах -100 ... +100, влияет на качество
       //переходного процесса усреднения;

В блоке инициализации изменить значение переменной:

 

//---- Инициализация переменных
   MinBar_Up = 4 + 30 + 30;

Теперь мы имеем два последовательных JMA сглаживания, для каждого из которых необходимо минимум по тридцать баров и четыре бара на расчётный алгоритм эксперта. После этого в блоке получения исходных значений переписать обращение к пользовательскому индикатору на обращение, которое я поместил вначале подзаголовка. Для второй половины алгоритма всё делаем абсолютно аналогично. Таким образом можно использовать в подобном эксперте не только мувинги, но и осцилляторы, что иногда также приводит к очень интересным результатам. Готовый код эксперта на основе индикатора J2JMA представлен файлом EXP_2.mqh.

 

Оптимизация эксперта

 

Теперь нам следовало бы на примере данного эксперта рассмотреть некоторые подробности оптимизации всех экспертов, которые будут предложены в данных статьях. Как я уже говорил, в эксперте имеется два независимых алгоритма для работы с длинными и короткими позициями. Вполне естественно, что оптимизировать этого эксперта гораздо удобнее и быстрее одновременно только на Buy или Sell направлении торговли. Для этого имеются внешнии переменные эксперта Test_Up и Test_Dn соответственно.

 

Присваивая одной из этих логических переменных значение false, мы тем самым отключаем все вычисления, связанные с этим направлением торговли в эксперте, и в итоге эксперт при оптимизации затрачивает на этот процесс значительно меньше времени. После оптимизации параметров эксперта в одном из направлений можно поменять значения переменных Test_Up и Test_Dn на противоположные и заняться оптимизацией эксперта в другом направлении торговли. А уже после этого присвоить обеим этим переменным значения true и протестировать полученный результат. Сам процесс оптимизации и тестирования на сей момент достаточно хорошо описан в статье Тестирование экспертов в клиентском терминале MetaTrader 4. Взгляд изнутри , и данный эксперт в этом плане ничего особенного не представляет. Запускаем Тестер стратегий, загружаем в него нашего эксперта, выбираем торгуемую пару, период пары, метод оптимизации и период времени, на котором будет произведена оптимизация:

после этого в Тестере стратегий нажимаем кнопку «Свойства эксперта» и переходим к закладке «Тестирование»:

 

Здесь мы определяем размер депозита, для оптимизации выбираем одно из двух направлений торговли (Long или Short) и выбираем генетический алгоритм оптимизации. После этого переходим к закладке «Входные параметры»:

 

 

в этом окне мы в соответствии с выбранным направлением торговли присваиваем одной из внешних переменных Test_Up и Test_Dn значение true, а другой false. Переменным Timeframe_Up и Timeframe_Dn присваиваем значения периодов графиков, на которых будут производится оптимизации, значениями переменных Money_Management_Up и Money_Management_Dn определяем, какую часть средств от всех имеющихся в наличии на депозите расходуется для совершения Buy и Sell сделок соответственно.
Для оставшихся внешних переменных задаём пределы их изменения при оптимизации. После этого можно выделить галочками оптимизируемые внешние переменные. Закрываем окно кнопкой «OK» и приступаем к оптимизации, нажав в Тестере стратегий кнопку «Старт». После завершения оптимизации:

 

переходим к закладке «Результаты» Тестера стратегий:

 

 

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

 

 

В таком виде этим окном не очень-то удобно пользоваться и развернутый вид во многих случаях оказывается более предпочтительным. А так как внутренними средствами клиентского терминала на сегодняшний день развернуть это окно невозможно, то мной был сделан дополнительный исполняемый файл для разворачивания этого окна до нужных размеров (OpenExp.exe). Этот файл разворачивает окно свойств экспертов имена файлов которых начинаются с Exp_ с учётом регистра. Для того, чтобы использовать этот файл, его следует запускать из какой-нибудь директории, после этого программный модуль будет ожидать появления Окна свойств эксперта и менять его размеры, в этот момент не рекомендуется делать движений мышью.

 

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

 

 

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

А нижняя граница всех имеющихся исторических данных, которую мы можем обнаружить в архиве котировок всего навсего:

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

Эти ошибки никакого отношения к самому эксперту не имеют! Просто период оптимизации следует выбирать исходя из того, что мы имеем в наличии, а не того, что нам бы хотелось!

Мне следовало бы дать некоторые пояснения к закладке «Входные параметры» эксперта. Я опять остановлюсь только на половине параметров для длинных позиций. Про параметр Test_Up я уже говорил. Смысл параметра Timeframe_Up в пояснениях не нуждается. Что делать с параметром Money_Management_Up я упоминал выше. А вот параметр Length_Up нам следовало бы изучить гораздо более основательно. Сам смысл этого параметра аналогичен параметру Период для обычных скользящих средних. По логике вещей этот параметр может принимать значения от единицы и до бесконечности. Большого смысла ставить верхний предел изменения этого параметра больше полутора сотен нет, во многих случаях гораздо проще перейти на более крупный таймфрейм. При оптимизации этого параметра следует учитывать тот факт, что чем большее значение имеет этот параметр, тем более устойчивые и долгосрочные тенденции начинает определять индикатор JFATL. Таким образом в этой торговой системе мы имеем неявную зависимость между параметром Length_Up и параметрами STOPLOSS_Up и TAKEPROFIT_Up. По логике вещей величина стоплосса и тейкпрофита напрямую зависит от параметра Length_Up. Конечно, ничто не мешает ставить ордера независимо от параметра Length_Up, но в таком случае прибыльность такой торговой системы может определяться не свойствами самой торговой системы, а свойствами текущей ситуации на рынке в более глобальном масштабе, которая абсолютно никак не определена в данной МТС! Можно пояснить смысл данного утверждения на конкретном примере следующим образом. Допустим, что удалось при оптимизации данного эксперта на EUR/USD на минутном графике получить весьма приличный результат тестирования эксперта со следующими параметрами:

extern bool   Test_Up = true;
extern int    Timeframe_Up = 1;
extern double Money_Management_Up = 0.1;
extern int    Length_Up = 4;
extern int    Phase_Up = 100;
extern int    IPC_Up = 0;
extern int    STOPLOSS_Up = 100;
extern int    TAKEPROFIT_Up = 200;
extern bool   ClosePos_Up = false;

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

 

Диапозон изменения значений переменной Phase_Up от -100 и до +100. При значениях этой переменной близким к -100 переходные процессы мувинга JFATL имеют минимальный характер, но по моим наблюдениям лучшие варианты оптимизации во многих случаях получаются при +100. Переменная IPC_Up определяет, какой ценовой ряд буден использован для его последующей обработки алгоритмом JFATL. С помощью переменной ClosePos_Up происходит включение принудительного закрывания позиций, если начался тренд против открытой позиции. Тут следует учесть, что если тейкпрофит и стоплосс открытой позиции будут выставлены слишком далеко от рынка, то все позиции будут закрываться по сигналам мувинга, и эти ордера не будут оказывать на торговлю никакого значения, если переменная ClosePos_Up имеет значение, равное true!

 

Заключение

 

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

 

Прикрепленные файлы:
 EXPERTS.zip (5.8 Kb)
 INCLUDE.zip (19.5 Kb)
 indicators.zip (8.9 Kb)
 TESTER.zip (3.1 Kb)
Источник: mql4.com

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

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

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