English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Адаптивные торговые системы и их использование в терминале MetaTrader 5

Адаптивные торговые системы и их использование в терминале MetaTrader 5

MetaTrader 5Торговые системы | 1 сентября 2010, 17:49
12 228 12
MetaQuotes
MetaQuotes

Введение

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

Многие уже оценили новые возможности, ставшие доступными с появлением нового языка MQL5, который отличается высокой скоростью работы и возможностью применения объектно-ориентированного программирования. Кроме того, с появлением мультивалютного тестера стратегий в торговом терминале MetaTrader 5 трейдеры получили уникальные инструменты для разработки, изучения и использования сложных торговых систем.

Осенью начнется Automated Trading Championship 2010, в котором примут участие тысячи торговых роботов, написанных на MQL5. Победит эксперт, заработавший максимальную прибыль за время проведения соревнования. Но какая торговая стратегия окажется наиболее эффективной?

Тестер стратегий терминала MetaTrader 5 позволяет найти наилучший набор параметров, при которых торговая система обеспечивала максимальную прибыль на указанном временном интервале. Но как это сделать в реальном времени? Идея об использовании виртуальной торговли нескольких стратегий в советнике обсуждалась в статье "Конкурс советников внутри советника", в которой приведена ее реализация на MQL4. 

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


1. Особенности адаптивных торговых стратегий

Рынки постоянно меняются. Торговые стратегии нуждаются в адаптации к текущим рыночным условиям.

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

На рисунке 1 представлены графики изменения средств на счете (equity)  десяти советников (MA_3,...MA_93), каждый из которых торговал по стратегии скользящих средних, но с разными периодами (3,13,..93). Тестирование стратегий осуществлялось на валютной паре EURUSD H1, интервал тестирования 4.01.2010-20.08.2010.

Рисунок 1. Графики изменения средств на счете (equity) десяти стратегий

Рисунок 1. Графики изменения средств на счете (equity) десяти стратегий

Как видно из рис. 1, в первые две недели работы все советники показывали приблизительно одинаковые результаты, но со временем они стали существенно различаться по заработанной прибыли. В конце периода тестирования в тройке лидеров оказались советники, которые использовали в торговле скользящие средние с периодами, равными 63, 53 и 43.

Рынок сам выбрал лучших. Почему бы не следовать за его выбором? Что получится, если все эти десять стратегий объединить в одном советнике, обеспечить каждой стратегии возможность ведения "виртуальной" торговли, а в реальной торговле периодически (например, в начале каждого нового бара) определять наилучшую стратегию и вести реальную торговлю в соответствии с ее сигналами?

Результаты тестирования полученной адаптивной стратегии приведены на рисунке 2. Динамика графика средств на счете при адаптивной торговле выделена красным цветом. Обратите внимание на то, что более половины времени форма динамики изменения средств на счете для адаптивной стратегии повторяет форму стратегии MA_63, которая в конечном счете, оказалась победителем.

Рисунок 2. Графики изменения средств на счете адаптивной стратегии, использующей сигналы 10 торговых систем

Рисунок 2. Графики изменения средств на счете адаптивной стратегии, использующей сигналы 10 торговых систем

Графики изменения баланса имеют похожую динамику (рис. 3):

Рисунок 3. Графики изменения баланса адаптивной стратегии, использующей сигналы 10 торговых систем

Рисунок 3. Графики изменения баланса адаптивной стратегии, использующей сигналы 10 торговых систем

Если на текущий момент ни одна из стратегий не является прибыльной, адаптивная система не должна вести торговлю. Пример такого случая приведен на рис. 4 (интервал с 4 по 22 января 2010г).

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

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

С начала января 2010 лидером по результативности была стратегия MA_3. Поскольку на тот момент MA_3 (синяя) заработала больше всего средств, то адаптивная стратегия (красная) следовала за ее сигналами. В период с 8 по 20 января все рассматриваемые торговые стратегии были в минусе, поэтому адаптивная стратегия не открывала новых торговых позиций. 

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


2. Программная реализация адаптивной торговой стратегии

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

Сначала мы рассмотрим код адаптивного советника, затем подробно остановимся на классе CAdaptiveStrategy, в котором реализован функционал адаптивной системы, затем приведем структуру класса CSampleStrategy - базового класса торговых стратегий, в котором реализован функционал виртуальной торговли.

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

2.1. Код советника

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

//+------------------------------------------------------------------+
//|                                       Adaptive_Expert_Sample.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <CAdaptiveStrategy.mqh>

CAdaptiveStrategy Adaptive_Expert;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(Adaptive_Expert.Expert_OnInit());
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Adaptive_Expert.Expert_OnDeInit(reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Adaptive_Expert.Expert_OnTick();
  }
//+------------------------------------------------------------------+

Первые три строчки характеризуют свойства программ, далее следует директива #include, указывающая препроцессору на необходимость включения файла CAdaptiveStrategy.mqh. Угловые скобки обозначают, что этот файл следует взять из стандартного каталога (обычно это каталог_терминала\MQL5\Include).

В следующей строке декларируется объект Adaptive_Expert (экземпляр класса CAdaptiveStrategy), а код функций OnInit, OnDeinit и OnTick эксперта состоит из вызовов соответствующих функций Expert_OnInit,  Expert_OnDeInit и Expert_OnTick объекта Adaptive_Expert.

2.2. Класс CAdaptiveStrategy

Класс адаптивного советника CAdaptiveStrategy расположен в файле CAdaptiveStrategy.mqh. Начнем с включаемых файлов:

#include <Arrays\ArrayObj.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <CStrategyMA.mqh>
#include <CStrategyStoch.mqh>

Мы включаем файл ArrayObj.mqh, поскольку для работы с классами различных стратегий удобно использовать объект класса CArrayObj, представляющий собой динамический массив указателей на экземпляры классов, порожденными от базового класса CObject и его наследников. Этим объектом будет массив m_all_strategies, он будет выступать роли "контейнера" торговых стратегий.

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

Для запроса свойств текущей позиции и для совершения торговых операций мы будем использовать классы CPositionInfo и CTrade Стандартной библиотеки, поэтому включаем файлы PositionInfo.mqh и Trade.mqh.

Рассмотрим структуру класса CAdaptiveStrategy.

//+------------------------------------------------------------------+
//| Класс CAdaptiveStrategy                                          |
//+------------------------------------------------------------------+
class CAdaptiveStrategy
  {
protected:
   CArrayObj        *m_all_strategies;   // объекты торговых стратегий

   void              ProceedSignalReal(int state,double trade_volume);
   int               RealPositionDirection();

public:
   // инициализация адаптивной стратегии
   int               Expert_OnInit();
   // деинициализация адаптивной  стратегии
   int               Expert_OnDeInit(const int reason);
   // проверка условий торговли и открытие виртуальных позиций
   void              Expert_OnTick();
  };

Для реализации единого подхода к объектам различных классов торговые стратегии (точнее экземпляры их классов) хранятся в динамическом массиве m_all_strategies (типа CArrayObj), который выступает в роли "контейнера" классов стратегий. По этой причине базовый класс торговых стратегий SampleStrategy порожден от класса CObject.

Функция ProceedSignalReal реализует "синхронизацию" направления и объема реальной позиции с заданным направлением с заданным объемом:

//+------------------------------------------------------------------+
//| Данный метод предназначен для "синхронизации" текущей            |
//| реальной торговой позиции со значением состояния state           |
//+------------------------------------------------------------------+
void CAdaptiveStrategy::ProceedSignalReal(int state,double trade_volume)
  {
   CPositionInfo posinfo;
   CTrade trade;

   bool buy_opened=false;
   bool sell_opened=false;

   if(posinfo.Select(_Symbol)) // если есть открытые позиции
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) buy_opened=true;    // открыта позиция на покупку
      if(posinfo.Type()==POSITION_TYPE_SELL) sell_opened=true;  // открыта позиция на продажу

      // если state = 0, значит нужно закрыть открытые позиции
      if((state==POSITION_NEUTRAL) && (buy_opened || sell_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
        }
      //переворот: закрытие позиции на покупку и открытие позиции на продажу
      if((state==POSITION_SHORT) && (buy_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      //переворот: закрытие позиции на продажу и открытие позиции на покупку
      if(((state==POSITION_LONG) && (sell_opened)))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
   else // если нет открытых позиций
     {
      // открытие позиции на покупку
      if(state==POSITION_LONG)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.1,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      // открытие позиции на продажу
      if(state==POSITION_SHORT)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,0.1,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
  }

Отметим, что работа с торговой позицией упрощается за счет использования торговых классов. Мы использовали объекты классов CPositionInfo и CTrade для запроса свойств рыночной позиции и для совершения торговых операций соответственно.

Функция RealPositionDirection запрашивает параметры реальной открытой позиции и возвращает ее направление:

//+------------------------------------------------------------------+
//| Возвращает направление (0,+1,-1) текущей реальной позиции        |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::RealPositionDirection()
  {
   int direction=POSITION_NEUTRAL;
   CPositionInfo posinfo;

   if(posinfo.Select(_Symbol)) // если есть открытые позиции
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) direction=POSITION_LONG;    // открыта позиция на покупку
      if(posinfo.Type()==POSITION_TYPE_SELL) direction=POSITION_SHORT;  // открыта позиция на продажу
     }
   return(direction);
  }

Теперь перейдем к рассмотрению основных функций класса СAdaptiveStrategy.

Начнем с функции Expert_OnInit:

//+------------------------------------------------------------------+
//| Функция инициализации адаптивного советника                      |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnInit()
  {
//--- Создадим массив объектов m_all_strategies
//--- сюда мы будем помещать наши объекты со стратегиями 
   m_all_strategies=new CArrayObj;
   if(m_all_strategies==NULL)
     {
      Print("Ошибка создания объекта m_all_strategies"); return(-1);
     }

// создаем 10 торговых стратегий CStrategyMA (торговля по скольящим средним)
// производим их инициализацию, устанавливаем параметры
// и добавляем в контейнер m_all_strategies 
   for(int i=0; i<10; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Ошибка создания объекта типа CStrategyMA");
         return(-1);
        }
      //задаем период каждой из стратегий
      int period=3+i*10;
      // инициализация стратегии
      t_StrategyMA.Initialization(period,true);
      // устанавливаем информацию о стратегии
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //t_StrategyMA.Set_Stops(3500,1000);

      //добавляем объект стратегии в массив объектов m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_SampleStrategy;
      t_SampleStrategy=m_all_strategies.At(i);
      Print(i," Имя стратегии:",t_SampleStrategy.StrategyName(),
              " ID стратегии:",t_SampleStrategy.StrategyID(),
              " Вирт. торговля:",t_SampleStrategy.IsVirtualTradeAllowed());
     }
//---
   return(0);
  }

В функции Expert_OnInit подготавливается набор торговых стратегий. Сначала создается объект динамического массива m_all_strategies.

В данном случае мы создали десять экземпляров класса CStrategyMA. Для каждого из них произвели инициализацию (в данном случае задали различные периоды и разрешили "виртуальную" торговлю), вызвав функцию Initialization.

Затем при помощи функции SetStrategyInfo мы задали финансовый инструмент, имя стратегии и комментарий.

При необходимости, при помощи функции Set_Stops(TP,SL) можно указать значения (в пунктах) Take Profit и Stop Loss, которые будут исполняться в процессе "виртуальной" торговли. У нас эта строка закомментирована.

После того, как класс стратегии создан и настроен, мы добавляем его в контейнер m_all_strategies.

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

Вместо десяти указанных нами средних (3,13,23...93) можно добавить целый веер из сотни скользящих средних (экземпляров класса CStrategyMA):

  for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Ошибка создания объекта типа CStrategyMA");
         return(-1);
        }
      //задаем период каждой из стратегий
      int period=3+i*10;
      // инициализация стратегии
      t_StrategyMA.Initialization(period,true);
      // устанавливаем информацию о стратегии
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //добавляем объект стратегии в массив объектов m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

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

  for(int i=0; i<5; i++)
     {
      CStrategyStoch *t_StrategyStoch;
      t_StrategyStoch=new CStrategyStoch;
      if(t_StrategyStoch==NULL)
        {
         delete m_all_strategies;
         printf("Ошибка создания объекта типа t_StrategyStoch");
         return(-1);
        }
      //задаем период каждой из стратегий
      int Kperiod=2+i*5;
      int Dperiod=2+i*5;
      int Slowing=3+i;
      // инициализация стратегии
      t_StrategyStoch.Initialization(Kperiod,Dperiod,Slowing,true);
      // устанавливаем информацию о стратегии
      string s=IntegerToString(Kperiod)+"/"+IntegerToString(Dperiod)+"/"+IntegerToString(Slowing);
      t_StrategyStoch.SetStrategyInfo(_Symbol,"[Stoch_"+s+"]",100+i," Stochastic "+s);
      //добавляем объект стратегии в массив объектов m_all_strategies
      m_all_strategies.Add(t_StrategyStoch);
     }

В таком случае в контейнере будет содержаться 10 стратегий со скользящими средними и 5 стратегий стохастического осциллятора.

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

Завершается функция Expert_OnInit выводом списка всех стратегий, содержащихся в контейнере m_all_strategies. Отметим, что здесь все стратегии в контейнере мы рассматриваем как потомков класса CSampleStrategy. Классы торговых стратегий CStrategyMA и CStrategyStoch также являются его потомками.

Тот же фокус используется в функции Expert_OnDeInit. Для каждой из стратегий в контейнере мы вызываем функцию SaveVirtualDeals, которая сохраняет историю проведенных виртуальных сделок.

В качестве имени файла, передаваемого как параметр, мы используем имя стратегии. Затем производится деинициализация стратегий путем вызова функции Deinitialization() и удаление контейнера m_all_strategies:

//+------------------------------------------------------------------+
//| Функция деинициализации адаптивного советника                    |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnDeInit(const int reason)
  {
   // деинициализируем все стратегии
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_Strategy;
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.SaveVirtualDeals(t_Strategy.StrategyName()+"_deals.txt");
      t_Strategy.Deinitialization();
     }
   //удаляем массив объектов со стратегиями 
   delete m_all_strategies;
   return(0);
  }

Если вас не интересуют виртуальные сделки, совершенные стратегиями, уберите строку с вызовом tStrategy.SaveVirtualDeals. Напомню, что при использовании тестера, файлы сохраняются в каталоге: /каталог_тестера/Files/.

Рассмотрим функцию Expert_OnTick класса CAdaptiveStrategy, которая вызывается каждый раз при появлении нового тика:

//+------------------------------------------------------------------+
//| Функция обработки тика адаптивной стратегии                      |
//+------------------------------------------------------------------+
void CAdaptiveStrategy::Expert_OnTick()
  {
   CSampleStrategy *t_Strategy;

   // для всех стратегий пересчитываем данные о позициях 
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.UpdatePositionData();
     }

   // советник должен проверять условия совершения новой торговой операции только при новом баре
   if(IsNewBar()==false) { return; }

   // для всех стратегий проверяем условия для торговли 
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.CheckTradeConditions();
     }

   //осуществляем поиск наилучшей позиции
   //подготавливаем массив performance[] 
   double performance[];
   ArrayResize(performance,m_all_strategies.Total());
   
   //для каждой позиции запрашиваем текущую результативность,
   //каждая стратегия возвращает в функции Strategyperformance()
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      performance[i]=t_Strategy.StrategyPerformance();
     }
   //находим стратегию (точнее ее индекс в контейнере m_all_strategies)
   //с максимальным значением Strategyperformance()
   int best_strategy_index=ArrayMaximum(performance,0,WHOLE_ARRAY);

   //эта стратегия - t_Strategy
   t_Strategy=m_all_strategies.At(best_strategy_index);
   //запрашиваем направление ее текущей позиции
   int best_direction=t_Strategy.PositionDirection();

   string s=s+" "+t_Strategy.StrategyName()+" "+DoubleToString(t_Strategy.GetVirtualEquity())+" "+IntegerToString(best_direction);
   Print(TimeCurrent()," TOTAL=",m_all_strategies.Total(),
                       " BEST IND=",best_strategy_index,
                       " BEST STRATEGY="," ",t_Strategy.StrategyName(),
                       " BEST=",performance[best_strategy_index],"  =",
                       " BEST DIRECTION=",best_direction,
                       " Performance=",t_Strategy.StrategyPerformance());

   //если наилучшая стратегия в минусе и нет открытых позиций, лучше воздержаться от торговли
   if((performance[best_strategy_index]<0) && (RealPositionDirection()==POSITION_NEUTRAL)) {return;}

   if(best_direction!=RealPositionDirection())
     {
      ProceedSignalReal(best_direction,t_Strategy.GetCurrentLotSize());
     }
  }

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

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

Теперь среди всех стратегий, находящихся в массиве m_all_strategies нужно найти наиболее успешную. Для этого мы использовали массив Performance[], в него заносятся значения, которые каждая из стратегий возвращает функцией StrategyPerformance(). В базовом классе CSampleStrategy эта функция представляет собой разность между текущими значениями "виртуальных" Equity и Balance.

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

После этого запрашивается направление виртуальной позиции этой стратегии (best_direction). Если оно отлично от текущего направления реальной позиции, то производится корректировка (при помощи функции ProceedSignalReal) текущего направления реальной позиции в соответствии с направлением best_direction.

2.3. Класс CSampleStrategy

Стратегии, помещенные в контейнер m_all_strategies, рассматривались нами как потомки класса CSampleStrategy.

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

Приведем структуру этого класса.

//+------------------------------------------------------------------+
//|                                              CSampleStrategy.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Object.mqh>

#define POSITION_NEUTRAL   0     // нет позиции
#define POSITION_LONG      1     // длинная позиция
#define POSITION_SHORT    -1     // короткая позиция

#define SIGNAL_OPEN_LONG    10   // сигнал на открытие длинной позиции
#define SIGNAL_OPEN_SHORT  -10   // сигнал на открытие короткой позиции
#define SIGNAL_CLOSE_LONG   -1   // сигнал на закрытие длинной позиции
#define SIGNAL_CLOSE_SHORT   1   // сигнал на закрытие короткой позиции
//+------------------------------------------------------------------+
//| Структура для хранения параметров виртуальной позиции            |
//+------------------------------------------------------------------+
struct virtual_position
  {
   string            symbol;            // символ
   int               direction;         // направление виртуальной позиции (0-нет открытой позиции,+1 покупка,-1 продажа)
   double            volume;            // объем позиции в лотах
   double            profit;            // текущая прибыль открытой виртуальной позиции в пунктах
   double            stop_loss;         // Stop Loss виртуальной позиции
   double            take_profit;       // Take Profit виртуальной позиции
   datetime          time_open;         // дата и время открытия виртуальной позиции
   datetime          time_close;        // дата и время закрытия виртуальной позиции
   double            price_open;        // цена открытия виртуальной позиции
   double            price_close;       // цена закрытия виртуальной позиции
   double            price_highest;     // наибольшая цена за время позиции
   double            price_lowest;      // наименьшая цена за время позиции
   double            entry_eff;         // эффективность входа
   double            exit_eff;          // эффективность выхода
   double            trade_eff;         // эффективность сделки
  };
//+------------------------------------------------------------------+
//| Класс CSampleStrategy                                            |
//+------------------------------------------------------------------+
class CSampleStrategy: public CObject
  {
protected:
   int               m_strategy_id;            // ID стратегии
   string            m_strategy_symbol;        // Символ 
   string            m_strategy_name;          // Наименование стратегии
   string            m_strategy_comment;       // Комментарий

   MqlTick           m_price_last;             // последняя цена
   MqlRates          m_rates[];                // массив для текущих котировок
   bool              m_virtual_trade_allowed;  // флаг разрешения виртуальной торговли 
   int               m_current_signal_state;   // текущее состояние стратегии
   double            m_current_trade_volume;   // количество лотов для торговли
   double            m_initial_balance;        // начальный баланс (задается в конструкторе, по умолчанию 10000)
   int               m_sl_points;              // Stop Loss
   int               m_tp_points;              // Take Profit

   virtual_position  m_position;               // виртуальная позиция
   virtual_position  m_deals_history[];        // массив сделок
   int               m_virtual_deals_total;    // общее количество сделок

   double            m_virtual_balance;           // "виртуальный" баланс
   double            m_virtual_equity;            // "виртуальные" средства
   double            m_virtual_cumulative_profit; // накопленная "виртуальная" прибыль
   double            m_virtual_profit;            // прибыль текущей открытой "виртуальной" позиции

   //проверяет и если нужно, закрывает виртуальную позицию по стопам
   bool              CheckVirtual_Stops(virtual_position &position);
   // пересчет позиции и баланса
   void              RecalcPositionProperties(virtual_position &position);
   // пересчет открытой виртуальной позиции в соответствии с текущими ценами 
   void              Position_RefreshInfo(virtual_position &position);
   // открывает виртуальную позицию на продажу
   void              Position_OpenShort(virtual_position &position);
   // закрывает виртуальную позицию на продажу  
   void              Position_CloseShort(virtual_position &position);
   // открывает виртуальную позицию на покупку
   void              Position_OpenLong(virtual_position &position);
   // закрывает виртуальную позицию на покупку
   void              Position_CloseLong(virtual_position &position);
   // закрывает открытую виртуальную позицию  
   void              Position_CloseOpenedPosition(virtual_position &position);
   // добавляет закрытую позицию в массив m_deals_history[] (истории сделок)
   void              AddDealToHistory(virtual_position &position);
   //вычисляет и возвращает рекомендуемый размер лота, который будет использоваться в торговле
   virtual double    MoneyManagement_CalculateLots(double trade_volume);
public:
   // конструктор
   void              CSampleStrategy();
   // деструктор
   void             ~CSampleStrategy();

   //возвращает текущее размер виртуального баланса
   double            GetVirtualBalance() { return(m_virtual_balance); }
   //возвращает текущий размер виртуальных средств
   double            GetVirtualEquity() { return(m_virtual_equity); }
   //возвращает текущий размер виртуальной прибыли открытой позиции
   double            GetVirtualProfit() { return(m_virtual_profit); }

   //устанавливает Stop Loss и Take Profit в пунктах
   void              Set_Stops(int tp,int sl) {m_tp_points=tp; m_sl_points=sl;};
   //устанавливает текущий размер лота
   void              SetLots(double trade_volume) {m_current_trade_volume=trade_volume;};
   //возвращает текущий размер лота
   double            GetCurrentLots() { return(m_current_trade_volume); }

   // возвращает наименование стратегии
   string            StrategyName() { return(m_strategy_name); }
   // возвращает ID стратегии
   int               StrategyID() { return(m_strategy_id); }
   // возвращает комментарий стратегии
   string            StrategyComment() { return(m_strategy_comment); }
   // задает информацию о стратегии (символ, наименование, ID, стратегии)
   void              SetStrategyInfo(string symbol,string name,int id,string comment);

   // устанавливает флаг виртуальной торговли (разрешена или нет)
   void              SetVirtualTradeFlag(bool pFlag) { m_virtual_trade_allowed=pFlag; };
   // возвращает флаг разрешения виртуальной торговли
   bool              IsVirtualTradeAllowed() { return(m_virtual_trade_allowed); };

   // возвращает текущее состояние стратегии
   int               GetSignalState();
   // устанавливает текущее состояние стратегии (если требуется, производит изменения виртуальной позиции)
   void              SetSignalState(int state);
   // производит изменения виртуальной позиции в соответствии с текущим состоянием 
   void              ProceedSignalState(virtual_position &position);

   // устанавливает значение накопленной "виртуальной" прибыли
   void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_profit; };

   //возвращает результативность стратегии ()
   double            StrategyPerformance();

   //обновляет данные позиции
   void              UpdatePositionData();
   //закрывает открытую виртуальную позицию
   void              CloseVirtualPosition();
   //возвращает направление текущей виртуальной позиции
   int               PositionDirection();
   //виртуальная функция инициализации
   virtual int       Initialization() {return(0);};
   //виртуальная функция проверки торговых условий
   virtual bool      CheckTradeConditions() {return(false);};
   //виртуальная функция деинициализации
   virtual int       Deinitialization() {return(0);};

   //сохраняет виртуальные сделки в файл
   void              SaveVirtualDeals(string file_name);
  };

Подробное описание его реализации мы опустим, детали вы можете найти в файле CSampleStrategy.mqh. Там же расположена функция проверки нового бара IsNewBar.


3. Классы торговых стратегий

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

3.1. Класс CStrategyMA - стратегия торговли по скользящим средним

//+------------------------------------------------------------------+
//|                                                  CStrategyMA.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Класс CStrategyMA для реализации виртуальной торговли            |
//| по стратегии скользящей средней                                  |
//+------------------------------------------------------------------+
class CStrategyMA : public CSampleStrategy
  {
protected:
   int               m_handle;     // хэндл индикатора Moving Average (iMA)
   int               m_period;     // период индикатора Moving Average
   double            m_values[];   // массив для хранения значений индикатора
public:
   // инициализация стратегии
   int               Initialization(int period,bool virtual_trade_flag);
   // деинициализация стратегии
   int               Deinitialization();
   // проверка условий торговли и открытие виртуальных позиций
   bool              CheckTradeConditions();
  };

Класс CStrategyMA является потомком класса CSampleStrategy, в котором реализован весь функционал виртуальной торговли.

В секции protected объявлены внутренние переменные, которые будут использоваться в классе стратегии. Это переменная m_handle - хэндл индикатора iMA, m_period - период средней, и массив m_values[], который будет использован в функции CheckTradeConditions для получения текущих значений индикатора.

В секции public есть три функции, обеспечивающие реализацию торговой стратегии.

  • Функция Initialization. Здесь стратегия инициализируется. Если требуется создать индикаторы, создавайте их здесь.
  • Функция Deinitialization. Здесь стратегия деинициализируется. Здесь производится освобождение индикаторных хэндлов.
  • Функция СheckTradeConditions. Здесь стратегия проверяет условия торговли и генерирует торговые сигналы, на основе которых ведется виртуальная торговля. Для проведения виртуальных торговых операций вызывается функция SetSignalState родительского класса CStrategy, в которую передается один из четырех торговых сигналов:
  1. Сигнал на открытие длинной позиции (SIGNAL_OPEN_LONG)
  2. Сигнал на открытие короткой позиции (SIGNAL_OPEN_SHORT)
  3. Сигнал на закрытие длинной позиции (SIGNAL_CLOSE_LONG)
  4. Сигнал на закрытие короткой позиции (SIGNAL_CLOSE_SHORT)
//+------------------------------------------------------------------+
//| Метод инициализации стратегии                                    |
//+------------------------------------------------------------------+
int CStrategyMA::Initialization(int period,bool virtual_trade_flag)
  {
   // устанавливаем период скользящей средней
   m_period=period;
   // устанавливаем заданный флаг виртуальной торговли
   SetVirtualTradeFlag(virtual_trade_flag);

   //устанавливаем индексацию массивов как в таймсериях
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_values,true);
   
   //создаем хэндл индикатора 
   m_handle=iMA(_Symbol,_Period,m_period,0,MODE_EMA,PRICE_CLOSE);
   if(m_handle<0)
     {
      Alert("Ошибка при создании индикатора MA - номер ошибки: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Метод деинициализации стратегии                                  |
//+------------------------------------------------------------------+
int CStrategyMA::Deinitialization()
  {
   Position_CloseOpenedPosition(m_position);
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| проверка условий торговли и открытие виртуальных позиций         |
//+------------------------------------------------------------------+
bool CStrategyMA::CheckTradeConditions()
  {
   RecalcPositionProperties(m_position);
   double p_close;

   // получаем исторические данные последних 3-х баров
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Ошибка копирования исторических данных - ошибка:",GetLastError(),"!!");
      return(false);
     }
   // скопируем текущую цену закрытия предыдущего бара (это бар 1)
   p_close=m_rates[1].close;  // цена закрытия предыдущего бара          

   if(CopyBuffer(m_handle,0,0,3,m_values)<0)
     {
      Alert("Ошибка копирования буферов индикатора Moving Average - номер ошибки:",GetLastError());
      return(false);
     }

   // условие покупки 1: MA растет
   bool buy_condition_1=(m_values[0]>m_values[1]) && (m_values[1]>m_values[2]);
   // условие покупки 2: предыдущая цена закрытия выше скользяшей средней MA
   bool buy_condition_2=(p_close>m_values[1]);

   // условие продажи 1: // MA падает
   bool sell_condition_1=(m_values[0]<m_values[1]) && (m_values[1]<m_values[2]);
   // условие продажи 2: // предыдущая цена закрытия ниже MA   
   bool sell_condition_2=(p_close<m_values[1]);

   int new_state=0;

   if(buy_condition_1  &&  buy_condition_2) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition_1 && sell_condition_2) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition_1 || buy_condition_2)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition_1 || sell_condition_2)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true);
  };

Принцип работы очень прост: по состояниям индикаторов и цен определяется тип сигнала (new_state), запрашивается текущее состояние виртуальной стратегии (при помощи функции GetSignalState) и если они не равны, вызывается функция SetSignalState, которая производит "корректировку" виртуальной позиции.

3.2. Класс CStrategyStoch - стратегия торговли по стохастику

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

//+------------------------------------------------------------------+
//|                                               CStrategyStoch.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Класс CStrategyStoch для реализации виртуальной торговли         |
//| по стратегии пересечения линий стохастического осциллятора       |
//+------------------------------------------------------------------+
class CStrategyStoch : public CSampleStrategy
  {
protected:
   int               m_handle;          // хэндл индикатора  Stochastic Oscillator (iStochastic)
   int               m_period_k;        // K-период (количество баров для расчетов)
   int               m_period_d;        // D-период (период первичного сглаживания)
   int               m_period_slowing;  // окончательное сглаживание
   double            m_main_line[];     // массив для хранения значений индикатора
   double            m_signal_line[];   // массив для хранения значений индикатора
public:
   // инициализация стратегии
   int               Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag);
   // деинициализация стратегии
   int               Deinitialization();
   // проверка условий торговли и открытие виртуальных позиций
   bool              CheckTradeConditions();
  };
//+------------------------------------------------------------------+
//| Метод инициализации стратегии                                    |
//+------------------------------------------------------------------+
int CStrategyStoch::Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag)
  {
   // задаем периоды осциллятора
   m_period_k=period_k;
   m_period_d=period_d;
   m_period_slowing=period_slowing;

   // устанавливаем заданный флаг виртуальной торговли
   SetVirtualTradeFlag(virtual_trade_flag);

   // устанавливаем индексацию массивов как в таймсериях
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_main_line,true);
   ArraySetAsSeries(m_signal_line,true);

   // создаем хэндл индикатора 
   m_handle=iStochastic(_Symbol,_Period,m_period_k,m_period_d,m_period_slowing,MODE_SMA,STO_LOWHIGH);
   if(m_handle<0)
     {
      Alert("Ошибка при создании индикатора Stochastic - номер ошибки: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Метод деинициализации стратегии                                  |
//+------------------------------------------------------------------+
int CStrategyStoch::Deinitialization()
  {
   // закрываем все открытые позиции
   Position_CloseOpenedPosition(m_position);
   // освобождаем хэндл индикатора
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| Проверка условий торговли и открытие виртуальных позиций         |
//+------------------------------------------------------------------+
bool CStrategyStoch::CheckTradeConditions()
  {
   // вызываем функцию пересчета параметров позиции
   RecalcPositionProperties(m_position);
   double p_close;

   // получаем исторические данные последних 3-х баров
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Ошибка копирования исторических данных - ошибка:",GetLastError(),"!!");
      return(false);
     }
   // копируем текущую цену закрытия предыдущего бара (это бар 1)
   p_close=m_rates[1].close;  // цена закрытия предыдущего бара          

   if((CopyBuffer(m_handle,0,0,3,m_main_line)<3) || (CopyBuffer(m_handle,1,0,3,m_signal_line)<3))
     {
      Alert("Ошибка копирования буферов индикатора Stochastic - номер ошибки:",GetLastError());
      return(false);
     }

   // условие покупки: пересечение сигнальной линии основной снизу вверх
   bool buy_condition=((m_signal_line[2]<m_main_line[2]) && (m_signal_line[1]>m_main_line[1]));
   // условие продажи: пересечение сигнальной линии основной сверху вниз
   bool sell_condition=((m_signal_line[2]>m_main_line[2]) && (m_signal_line[1]<m_main_line[1]));

   int new_state=0;

   if(buy_condition) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true);
  };

Как видно, структура класса CStrategyStoch отличается от класса CStrategyMA лишь функцией инициализации (другие параметры), типом используемого индикатора и торговыми сигналами.

Таким образом, для использования ваших стратегий в адаптивном советнике нужно переписать их в виде классов такого типа и загрузить их в контейнер m_all_strategies.


4. Результаты исследования адаптивных торговых стратегий

В данном разделе мы рассмотрим некоторые аспекты практического применения адаптивных стратегий и методы их улучшения.

4.1. Дополнение системы стратегиями с инверсией сигналов

Скользящие средние плохо работают при отсутствии трендов. Мы уже сталкивались с этой ситуацией - на рисунке 3 видно, что в период с 8 по 20 января тренда не было, поэтому все 10 стратегий, торгующих по скользящим средним, были в виртуальной "просадке". Адаптивная система прекратила торговлю из-за отсутствия стратегии с положительным количеством заработанных средств. Можно ли сделать так, чтобы избежать ухода в минус?

Добавим к нашим десяти стратегиям MA_3, MA_13, ... MA_93 еще десять классов CStrategyMAinv, торговые сигналы которых перевернуты (условия те же, но SIGNAL_OPEN_LONG/SIGNAL_OPEN_SHORT и SIGNAL_CLOSE_LONG/SIGNAL_CLOSE_SHORT в этом классе поменялись местами). Таким образом, наряду с десяти трендовыми стратегиями (экземпляры класса CStrategyMA) мы имеем также десять контр-трендовых стратегий (экземпляторы класса CStrategyMAinv).

Результаты использования адаптивной системы, состоящей из двадцати стратегий, представлены на рис. 5.

Рисунок 5. Графики изменения средств на счете адаптивной стратегии, использующей сигналы 20 торговых систем: 10 средних CAdaptiveMA и 10 "зеркальных" CAdaptiveMAinv

Рисунок 5. Графики изменения средств на счете адаптивной стратегии, использующей сигналы 20 торговых систем: 10 средних CAdaptiveMA и 10 "зеркальных" CAdaptiveMAinv

Как видно из рис. 5, в период, когда все стратегии CAdaptiveMA были в минусе, следование за стратегиями CAdaptiveMAinv позволило советнику избежать нежелательных просадок в самом начале торговли.

Рисунок 6. Временной интервал, когда адаптивная стратегия использовала сигналы "контр-трендовых" стратегий CAdaptiveMAinv

Рисунок 6. Временной интервал, когда адаптивная стратегия использовала сигналы "контр-трендовых" стратегий CAdaptiveMAinv

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

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

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

4.2. Имеет ли смысл инвертировать сигналы наихудшей стратегии?

Представленный выше фокус с инверсией торговых сигналов наводит на мысль о потенциальной возможности использования сигналов наихудшей торговой стратегии. Если стратегия убыточная (причем самая убыточная), то возможно ли получение прибыли, если действовать наоборот?

Можно ли простым изменением торговых сигналов убыточную стратегию превратить в прибыльную? Для ответа на этот вопрос нужно в функции Expert_OnTick()  класса CAdaptiveStrategy заменить ArrayMaximum на ArrayMinimum и реализовать замену направлений путем умножения значения переменной BestDirection на -1.

Также нужно закомментировать ограничение реальной торговли в случае отрицательной эффективности (поскольку мы рассматриваем результативность наихудшей):

//if((Performance[BestStrategyIndex]<0) && (RealPositionDirection()==0)) {return;}

График изменения средств адаптивного советника, торгующего по сигналам, обратным сигналам наихудшей стратегии приведен на рис. 7:

Рисунок 7. Графики изменения средств на счете десяти стратегий и адаптивной системы, торгующей по сигналам, противоположным сигналам наихудшей системы

Рисунок 7. Графики изменения средств на счете десяти стратегий и адаптивной системы, торгующей по сигналам, противоположным сигналам наихудшей системы

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

Копирование (и обращение) сигналов наихудшей стратегии не приводит к улучшению эффективности торговли.

4.2. Почему веер скользящих средних не так эффективен, как кажется?

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

Для этого нужно слегка изменить код в классе CAdaptiveStrategy:

   for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Ошибка создания объекта типа CStrategyMA");
         return(-1);
        }
      //задаем период каждой из стратегий
      int period=3+i*10;
      // инициализация стратегии
      t_StrategyMA.Initialization(period,true);
      // устанавливаем информацию о стратегии
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //добавляем объект стратегии в массив объектов m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

Однако следует понимать, что близкие скользящие средние неизбежно будут пересекаться, лидер будет меняться, а адаптивная система будет переключать свои состояния и открывать/закрывать позиции чаще, чем нужно. Как следствие, получим ухудшение характеристик адаптивной системы. Вы можете убедиться в этом самостоятельно, сравнив статистические характеристики систем (вкладка "Результаты" в тестере).

Не стоит строить адаптивные системы из большого количества стратегий с очень близкими параметрами.


5. Над чем следует подумать

В контейнер m_all_strategies можно поместить тысячи вариантов предложенных стратегий, можно даже добавить все известные стратегии с разными параметрами, однако для победы в Чемпионате Automated Trading Championship 2010 необходимо поработать над системой управления капиталом. Напомним, при проведении тестирования на истории (и в коде классов) мы использовали объем, равный 0.1 лота.

5.1 Как повысить прибыльность адаптивного советника

В классе CSampleStrategy есть виртуальная функция-заглушка MoneyManagement_CalculateLots:

//+------------------------------------------------------------------+
//| Функция возвращает рекомендуемый размер лота для стратегии       |
//| В качестве параметра ей передается текущий размер лота           |
//| Размер лота может задаваться в зависимости от:                   |
//| текущих m_virtual_balance и m_virtual_equity                     |
//| текущей статистики сделок (находящейся в m_deals_history)        |
//| или того, чего захотите                                          |
//| Если в стратегии не требуется изменения размера лота             |
//| можно вернуть переданный размер лота:  return(trade_volume);     |
//+------------------------------------------------------------------+ 
double CSampleStrategy::MoneyManagement_CalculateLots(double trade_volume)
  {
   //вернем что получили 
   return(trade_volume);
  }

Для управления количеством лотов можно использовать статистическую информацию о результатах и характеристиках виртуальных сделок, которая протоколируется в массиве m_deals_history[].

При необходимости увеличить количество лотов (например, удвоить, если последние виртуальные сделки в m_deals_history[] были успешными или уменьшить) следует соответствующим образом изменить возвращаемое значение.

5.2 Использование статистики сделок для расчета результативности

Для расчета результативности стратегий в классе CSampleStrategy реализована функция StrategyPerformance().

//+------------------------------------------------------------------+
//| Функция StrategyPerformance - функция результативности стратегии |
//+------------------------------------------------------------------+ 
double CSampleStrategy::StrategyPerformance()
  {
   //возвращает результативность стратегии
   //в данном случае это разность между количеством 
   //средств на текущий момент и начальным балансом, 
   //т.е. сколько заработала стратегия
   double performance=(m_virtual_equity-m_initial_balance);
   return(performance);
  }

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

Расчет эффективностей входов, выходов и эффективности сделки (поля entry_eff, exit_eff и trade_eff структур массива m_deals_history[]) производится автоматически при виртуальной торговле (см. класс CSampeStrategy). Эту статистическую информацию можно использовать для конструирования своих, более сложных показателей результативности стратегии.

Например, в качестве характеристики эффективности можно взять среднюю прибыль последних трех сделок (используем поле pos_Profit из архива сделок m_deals_history[]):

double CSampleStrategy::StrategyPerformance()
  {
  //если были сделки, то умножим это значение на результаты трех последних сделок
   if(m_virtual_deals_total>0)
     {
      int avdeals=MathRound(MathMin(3,m_virtual_deals_total));
      double sumprofit=0;
      for(int j=0; j<avdeals; j++)
        {
         sumprofit+=m_deals_history[m_virtual_deals_total-1-j].profit;
        }
      double performance=sumprofit/avdeals;
     }
     return(performance);

  }

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

5.3 Использование Take Profit и Stop Loss

Изменить результативность торговых систем можно также при помощи установки фиксированных стопов (это делается вызовом функции Set_Stops), которая позволяет задать уровни стопов при виртуальной торговле (в пунктах). Если уровни заданы, закрытие виртуальных позиций будет происходить автоматически, функционал этого механизма реализован в классе CSampleStrategy.

В нашем примере (см. 2.2, функция инициализации классов скользящих средних) функция установки стопов в виртуальной торговле закомментирована.

5.4. Периодическое обнуление накопленной виртуальной прибыли

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

Для этого в классе CSampleStrategy предусмотрены функции:

// устанавливает значение накопленной "виртуальной" прибыли
 void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_perofit; };
//закрывает открытую виртуальную позицию
 void              CloseVirtualPosition();

CheckPoint такого рода можно применять периодически, например после завершения каждых N баров.

5.4. Чудес не бывает

Следует помнить, что адаптивная система не является граалем (USDJPY H1, 4.01.2010-20.08.2010):

Рисунок 8. График изменения баланса и средств адаптивной системы, торгующей по сигналам наилучшей из 10 стратегий (USDJPY H1)

Рисунок 8. График изменения баланса и средств адаптивной системы, торгующей по сигналам наилучшей из 10 стратегий (USDJPY H1)

Графики динамики средств всех стратегий представлены на рис 9.

Рисунок 9. Графики изменения средств на счете адаптивной системы, построенной на базе 10 стратегий (USDJPY H1)

Рисунок 9. Графики изменения средств на счете адаптивной системы, построенной на базе 10 стратегий (USDJPY H1)

Если в адаптивной системе нет прибыльных стратегий, то их использование не будет эффективным. Используйте прибыльные стратегии.

Следует рассмотреть еще один важный и интересный момент. Обратите внимание на поведение адаптивной стратегии в самом начале торговли:

Рисунок 10. Графики изменения средств на счете 10 стратегий адаптивной системы

Рисунок 10. Графики изменения средств на счете 10 стратегий адаптивной системы

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

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

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

Выводы

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

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

P.S. Для удобства проведения сравнительного анализа свойств адаптивных систем прилагается debug-версия класса CSampleStrategy (архив adaptive-systems-mql5-sources-debug). Отличие данной версии в том, что в процессе работы создаются текстовые файлы, содержащие сводные отчеты по динамике изменения виртуальных баланса/средств стратегий, включенных в систему.

Последние комментарии | Перейти к обсуждению на форуме трейдеров (12)
Sergey
Sergey | 23 сент. 2010 в 02:00
Automated-Trading:

Да, стратегии подключаются в функции CAdaptiveStrategy::Expert_OnInit(). Для удобства лучше использовать debug-версию советника, там добавлено сохранение результатов в файлы. Разобраться в том, сигналы каких стратегий использовались в реальной торговле, можно по файлу direction_res.txt.

при использовании адаптивной стратегии, состоящей из указанных 5 стохастиков (EURUSD H1, тест с начала 2010 года до 1 сентября 2010) получаются следующие результаты:

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

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

Ivan Kornilov
Ivan Kornilov | 21 мар. 2012 в 15:16
MQL5 открывает для нас огромные возможности - не многие из сообщества трейдеров еще могут оценит их - еще меньше использовать.
Oleg Mironov
Oleg Mironov | 24 окт. 2016 в 11:43
Прошу прощения если не увидел в других постах, но не могли бы Вы подсказать: как в адаптированной стратегии вызывать советники, оформленные отдельными файлами (способными работать независимо), возможно с внедрением в них дополнительных функций для внешней коммуникации. Идея отрабатывать и тестировать советники независимо, но с учетом дальнейшего подключения к адаптивной стратегии.
KatanaMatana
KatanaMatana | 25 окт. 2021 в 00:19

Здравствуйте!


Вы не могли бы подправить код советника? А то в современной версии терминала ничего не работает... 

Интервью с Александром Топчило (ATC 2010) Интервью с Александром Топчило (ATC 2010)
Александр Топчило (Better) - победитель Чемпионата Automated Trading Championship 2007. Коньком Александра являются нейронные сети - именно нейроэксперт со значительным отрывом опередил конкурентов в Чемпионате 2007 года. Интересный собеседник и успешный разработчик экспертов рассказывает в этом интервью о своей жизни после Чемпионатов, собственном бизнесе и новых алгоритмах для создания торговых систем.
Как быстро написать советник для Automated Trading Championship 2010 Как быстро написать советник для Automated Trading Championship 2010
Для того чтобы разработать эксперт для участия в чемпионате Automated Trading Championship 2010, воспользуемся шаблоном готового советника. Данная задача будет по силам даже новичку в программировании на MQL5, т.к. для ваших стратегий уже разработаны базовые классы, функции, шаблоны. Достаточно написать минимум кода, чтобы реализовать свою торговую идею.
Контроль наклона кривой баланса во время работы торгового эксперта Контроль наклона кривой баланса во время работы торгового эксперта
Найти правила для торговой системы и запрограммировать их в советнике - это еще полбеды. Необходимо каким-то образом поправлять работу эксперта в процессе накопления результатов торговли. В статье описывается один из подходов, позволяющий улучшить характеристики торгового эксперта при помощи создания обратной связи, измеряющей наклон кривой баланса депозита.
Фрактальный анализ совместного движения валют Фрактальный анализ совместного движения валют
Насколько независимы валютные котировки? Движутся ли они согласованно или информация о направлении движения одной валюты ничего не скажет о движении другой? В этой статье предпринята попытка разобраться в этом вопросе, используя методы нелинейной динамики и фрактальной геометрии.