Повышаем эффективность линейных торговых систем

laplacianlab | 20 ноября, 2013

Введение

В сегодняшней статье речь пойдет о том, как MQL5-программисты со средним уровнем подготовки могут повысить прибыльность своих линейных торговых систем (торговля фиксированным лотом), используя так называемую технику возведения в степень (technique of exponentiation). Понятие возведения в степень в данном случае используется для моделей управления средствами, в которых размер или количество рыночных позиций зависит от величины потенциального риска. Данная техника названа именно так, потому что при ее использовании кривая средств приобретает геометрическую, или экспоненциальную, форму, становясь похожей на параболу. Понятие "линейный" в данной статье используется частично в математическом значении, а частично в значении, более близком к программированию. В статье на языке MQL5 реализуется фиксированно-фракционный (Fixed Fractional) метод Ральфа Винса.

Рис. 1. Парабола


Рис. 1. Парабола

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


1. Что такое модели управления капиталом?

Говоря кратко, модели управления капиталом представляют собой основополагающие установки, исходя из которых вы выбираете размер своих позиций, планируете использование стоп-лоссов, рассчитываете маржу и торговые издержки. Существует множество моделей управления капиталом! При желании вы можете найти в Интернете информацию о таких классических методиках, как торговля фиксированным лотом (Fixed Lot), фиксированно-фракционный метод (Fixed Fractional), фиксированный коэффициент (Fixed Ratio), критерий Келли (Kelly's Percentage) или эффективная стоимость (Effective Cost). Как я уже сказал, в данной статье речь пойдет только о фиксированно-фракционном методе.


1.2. Фиксированно-фракционный метод

Идея данной модели управления капиталом заключается в зависимости размера позиции от ожидаемого риска. При этом размер риска при каждой сделке одинаков. 

Уравнение для определения числа контрактов при использовании фиксированно-фракционного метода:



N = f * Equity / Trade Risk

N – это количество контрактов, f – фиксированная доля (число от 0 до 1), Equity – текущее значение средств на счете и Trade Risk – торговый риск по одному контракту. Пожалуйста, прочтите статью "Fixed Fractional Position Sizing" (Размер позиции в фиксированно-фракционном методе) Майкла Брайанта (Michael R. Bryant), чтобы узнать больше об этом методе.

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


2. Вводим фиксированную долю в торговую систему


2.1. Линейная торговая система

Разумеется, для испытания данного метода нам нужна линейная торговая система. Под линейной системой я понимаю такую торговую систему, которая оказалась прибыльной в течение определенного периода времени и чья кривая средств выглядит как прямая линия. Например, советник HawaiianTsunamiSurfer, доступный в Code Base, представляет собой линейную торговую систему. Кривая средств данного советника выглядит как прямая линия с января по март 2012 года.

Рис. 2. Кривая средств советника HawaiianTsunamiSurfer с января по март 2012 года

Рис. 2. Кривая средств советника HawaiianTsunamiSurfer с января по март 2012 года

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

2.2. CEvolution - основной MQL5-класс для повышения эффективности вашей торговой системы

Итак, мы снова используем объектно-ориентированный подход при разработке нашего советника. Я рекомендую вам сначала прочитать статьи "Приобщаемся к объектно-ориентированному программированию в MQL5" и "Создание новостного торгового советника", чтобы узнать больше о данном подходе. Если вы уже сделали это, помните, что коды, обсуждавшиеся в этих статьях, содержат очень важный элемент – CEvolution. Это позволяет нам отслеживать некоторую важную временную информацию, включая состояние робота в данный момент, историю проведенных операций и т.д.

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

Класс CEvolution.mqh:

//+------------------------------------------------------------------+
//|                                                   CEvolution.mqh |
//|                               Copyright © 2013, Jordi Bassagañas |
//+------------------------------------------------------------------+
#include <Mine\Enums.mqh>
//+------------------------------------------------------------------+
//| Класс CEvolution                                                 |
//+------------------------------------------------------------------+
class CEvolution
  {
protected:
   ENUM_STATUS_EA                   m_status;            // Текущее состояние советника
   ENUM_EXP_EQUITY_CURVE_LEVEL      m_expEquityLevel;    // Текущий экспоненциальный уровень средств
   double                           m_originalEquity;    // Начальное значение средств
   double                           m_lotSize;           // Текущий размер лота

public:
   //--- Конструктор и деструктор
                                    CEvolution(ENUM_STATUS_EA status,ENUM_EXP_EQUITY_CURVE_LEVEL exp_equity_level);
                                    ~CEvolution(void);
   //--- Геттеры
   ENUM_STATUS_EA                   GetStatus(void);
   ENUM_EXP_EQUITY_CURVE_LEVEL      GetExpEquityLevel(void);
   double                           GetOriginalEquity(void);
   double                           GetLotSize(void);
   //--- Сеттеры
   void                             SetStatus(ENUM_STATUS_EA status);
   void                             SetExpEquityLevel(ENUM_EXP_EQUITY_CURVE_LEVEL exp_equity_level);
   void                             SetOriginalEquity(double equity);
   void                             SetLotSize(double size);
   //--- Специальные методы CEvolution
   double                           CalcEquityGrowth(double currentEquity);
   void                             RefreshExpEquityLevel(double currentEquity);
   void                             RefreshLotSize();
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status,ENUM_EXP_EQUITY_CURVE_LEVEL exp_equity_level)
  {
   m_status=status;
   m_expEquityLevel=exp_equity_level;
   RefreshLotSize();
   m_originalEquity=AccountInfoDouble(ACCOUNT_EQUITY);
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CEvolution::~CEvolution(void)
  {
  }
//+------------------------------------------------------------------+
//| GetStatus                                                        |
//+------------------------------------------------------------------+
ENUM_STATUS_EA CEvolution::GetStatus(void)
  {
   return m_status;
  }
//+------------------------------------------------------------------+
//| GetExpEquityLevel                                                |
//+------------------------------------------------------------------+
ENUM_EXP_EQUITY_CURVE_LEVEL CEvolution::GetExpEquityLevel(void)
  {
   return m_expEquityLevel;
  }
//+------------------------------------------------------------------+
//| GetEquity                                                        |
//+------------------------------------------------------------------+
double CEvolution::GetOriginalEquity(void)
  {
   return m_originalEquity;
  }
//+------------------------------------------------------------------+
//| GetLotSize                                                       |
//+------------------------------------------------------------------+
double CEvolution::GetLotSize(void)
  {
   return m_lotSize;
  }
//+------------------------------------------------------------------+
//| SetStatus                                                        |
//+------------------------------------------------------------------+
void CEvolution::SetStatus(ENUM_STATUS_EA status)
  {
   m_status=status;
  }
//+------------------------------------------------------------------+
//| SetExpEquityLevel                                                |
//+------------------------------------------------------------------+
void CEvolution::SetExpEquityLevel(ENUM_EXP_EQUITY_CURVE_LEVEL exp_equity_level)
  {
   m_expEquityLevel=exp_equity_level;
  }
//+------------------------------------------------------------------+
//| SetEquity                                                        |
//+------------------------------------------------------------------+
void CEvolution::SetOriginalEquity(double equity)
  {
   m_originalEquity=equity;
  }
//+------------------------------------------------------------------+
//| SetLotSize                                                       |
//+------------------------------------------------------------------+
void CEvolution::SetLotSize(double lot_size)
  {
   m_lotSize=lot_size;
  }
//+------------------------------------------------------------------+
//| CalcEquityGrowth                                                 |
//+------------------------------------------------------------------+
double CEvolution::CalcEquityGrowth(double currentEquity)
  {
   return NormalizeDouble(currentEquity * 100 / m_originalEquity - 100,2);
  }
//+------------------------------------------------------------------+
//| RefreshExpEquityLevel                                            |
//+------------------------------------------------------------------+
void CEvolution::RefreshExpEquityLevel(double currentEquity)
  {
   double growth = CalcEquityGrowth(currentEquity);
   //--- составляет ли уровень текущих средств менее 10% от изначального?
   if(growth <= 10)
   {
      SetExpEquityLevel(LEVEL_ONE);
   }
   //--- составляет ли уровень текущих средств более 10%, но менее 20% от изначального?
   else if(growth > 10 && growth <= 20)
   {
      SetExpEquityLevel(LEVEL_TWO);
   }
   //--- составляет ли уровень текущих средств более 20%, но менее 30% от изначального?
   else if(growth > 20 && growth <= 30)
   {
      SetExpEquityLevel(LEVEL_THREE);
   }
   //--- составляет ли уровень текущих средств более 30%, но менее 40% от изначального?
   else if(growth > 30 && growth <= 40)
   {
      SetExpEquityLevel(LEVEL_FOUR);
   }
   //--- составляет ли уровень текущих средств более 40%, но менее 50% от изначального?
   else if(growth > 40 && growth <= 50)
   {
      SetExpEquityLevel(LEVEL_FIVE);
   }
   //--- составляет ли уровень текущих средств более 50%, но менее 60% от изначального?
   else if(growth > 50 && growth <= 60)
   {
      SetExpEquityLevel(LEVEL_SEVEN);
   }
   //--- составляет ли уровень текущих средств более 60%, но менее 70% от изначального?  
   else if(growth > 60 && growth <= 70)
   {
      SetExpEquityLevel(LEVEL_EIGHT);
   }
   //--- составляет ли уровень текущих средств более 70%, но менее 80% от изначального?   
   else if(growth > 70 && growth <= 80)
   {
      SetExpEquityLevel(LEVEL_NINE);
   }
   //--- составляет ли уровень текущих средств более 90% от изначального?
   else if(growth > 90)
   {
      SetExpEquityLevel(LEVEL_TEN);
   }
  }
//+------------------------------------------------------------------+
//| RefreshLotSize                                                   |
//+------------------------------------------------------------------+
void CEvolution::RefreshLotSize()
  {
   switch(m_expEquityLevel)
   {
      case LEVEL_ONE:
         SetLotSize(0.01);
         break;
         
      case LEVEL_TWO:
         SetLotSize(0.02);
         break;
         
      case LEVEL_THREE:
         SetLotSize(0.03);
         break;
         
      case LEVEL_FOUR:
         SetLotSize(0.04);
         break;
         
      case LEVEL_FIVE:
         SetLotSize(0.05);
         break;
         
      case LEVEL_SIX:
         SetLotSize(0.06);
         break;
         
      case LEVEL_SEVEN:
         SetLotSize(0.07);
         break;

      case LEVEL_EIGHT:
         SetLotSize(0.08);
         break;
         
      case LEVEL_NINE:
         SetLotSize(0.09);
         break;
         
      case LEVEL_TEN:
         SetLotSize(0.1);
         break;
   }
  }
//+------------------------------------------------------------------+

Рассмотрим некоторые наиболее важные части этого класса. 

При создании торгового советника значение начальных средств хранится в m_originalEquity:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status,ENUM_EXP_EQUITY_CURVE_LEVEL exp_equity_level)
  {
   m_status=status;
   m_expEquityLevel=exp_equity_level;
   RefreshLotSize();
   m_originalEquity=AccountInfoDouble(ACCOUNT_EQUITY);
  }

Метод CEvolution::CalcEquityGrowth предназначен для расчета роста кривой средств с учетом начального значения:

//+------------------------------------------------------------------+
//| CalcEquityGrowth                                                 |
//+------------------------------------------------------------------+
double CEvolution::CalcEquityGrowth(double currentEquity)
  {
   return NormalizeDouble(currentEquity * 100 / m_originalEquity - 100,2);
  }

Наконец, CEvolution::RefreshExpEquityLevel предназначен для обновления уровня средств на каждом тике (обратите внимание, как данный метод зависит от роста средств), а CEvolution::RefreshLotSize обновляет размер лота на каждом тике. Предполагается, что вы обновляете эти данные в методе OnTick вашего советника следующим образом:

GetEvolution().RefreshExpEquityLevel(AccountInfoDouble(ACCOUNT_EQUITY));
GetEvolution().RefreshLotSize();

Кстати, данное решение предполагает использование следующего пользовательского MQL5-перечисления:

//+------------------------------------------------------------------+
//| Перечисление уровней экспоненциальной кривой средств             |
//+------------------------------------------------------------------+
enum ENUM_EXP_EQUITY_CURVE_LEVEL
  {
   LEVEL_ONE,
   LEVEL_TWO,
   LEVEL_THREE,
   LEVEL_FOUR,
   LEVEL_FIVE,
   LEVEL_SIX,
   LEVEL_SEVEN,
   LEVEL_EIGHT,
   LEVEL_NINE,
   LEVEL_TEN
  };
Использование данного перечисления представляет собой вариант реализации фиксированно-фракционного метода, однако имеет некоторые особенности. Например, кривая средств будет расти в геометрической прогрессии до достижения так называемого десятого уровня, после чего система становится линейной. Тем не менее, CEvolution сохраняет основную идею постоянного увеличения размера позиций пропорционально кривой средств.

2.3. Управление капиталом

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

Для этого в метод OnTick вашего торгового советника необходимо ввести следующий код:

switch(GetEvolution().GetStatus())
     {
      case BUY:

         tp = ask + m_takeProfit * _Point;
         sl = bid - m_stopLoss * _Point;

         GetTrade().PositionOpen(GetBrain().GetSymbol(),ORDER_TYPE_BUY,m_evolution.GetLotSize(),ask,sl,tp);
         
         break;

      case SELL:

         sl = ask + m_takeProfit * _Point;
         tp = bid - m_stopLoss * _Point;

         GetTrade().PositionOpen(GetBrain().GetSymbol(),ORDER_TYPE_SELL,m_evolution.GetLotSize(),bid,sl,tp);
         
         break;

      case DO_NOTHING:

         // Ничего не происходит...

         break;
     }

Я переименовал свою новую экспоненциальную систему в ExponentialHawaiian.


3. Тестируем обновленную систему на истории

Не забудьте протестировать вашу систему после введения в нее всех описанных выше изменений! Ниже приведен результат тестирования советника ExponentialHawaiian – фиксированно-фракционной версии HawaiianTsunamiSurfer:

Рис. 3. Кривая средств ExponentialHawaiian с января по март 2012 года

Рис. 3. Кривая средств ExponentialHawaiian с января по март 2012 года

Кривая на рисунке выше остается экспоненциальной, в то время как сама система остается линейной. Если это условие перестает соблюдаться, система становится нестабильной с риском разорения.


Заключение

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

Вначале мы познакомились с некоторыми классическими методами управления капиталом (торговля фиксированным лотом, фиксированно-фракционный метод, фиксированный коэффициент, критерий Келли, эффективная стоимость) и сосредоточили наше внимание на фиксированно-фракционном методе – простой модели, в которой объем операций пропорционален чистому балансу счета. Наконец, мы использовали торговую систему, показывавшую линейные результаты в течение некоторого времени, внедрили в нее разновидность фиксированно-фракционного метода на языке MQL5 и протестировали в тестере стратегий MetaTrader.

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