Скачать MetaTrader 5

Приобщаемся к объектно-ориентированному программированию в MQL5

28 августа 2013, 13:03
Jordi Bassaganas
0
7 855

Введение

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

Фрагмент фрески "Афинская школа" Рафаэля Санти

Рис. 1. Фрагмент фрески "Афинская школа" Рафаэля Санти. Здесь мы видим беседующих философов Платона и Аристотеля.
Платон отстаивает мир идей, в то время как Аристотель является сторонником эмпирического (опытного) подхода к познанию

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

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

Это классическая проблема в разработке программного обеспечения, которая привела к созданию разнообразных методологий разработки, таких как Scrum, Разработка через тестирование (Test Driven Development, TDD), Экстремальное программирование (Extreme Programming, XP) и других. Очень важно знать о подводных камнях языка. Кстати, как утверждает Википедия, "методологии разработки программного обеспечения или методологии разработки систем являются основой структурирования, планирования и контроля процесса разработки информационной системы".

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


1. Первые шаги в освоении новой парадигмы


1.1. Преимущества ООП при разработке торговых советников для рынка Forex

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

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

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

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

Здесь необходимо упомянуть абстрактные типы данных (abstract data types, ADT, АТД). ООП позволяет создавать АТД. АТД это абстрактная модель типов данных, присутствующих во всех языках программирования. Их основное назначение определение предметной области приложений. Примерами абстрактных типов данных в MQL5 могут послужить Include\Arrays\Array.mqh, Include\Arrays\List.mqh и Include\Arrays\Tree.mqh.

Другими словами, объектно-ориентированная парадигма позволяет разрабатывать приложения на концептуальном уровне, давая возможность повторного использования кода, а также сохраняя высокую надежность, гибкость и простоту обслуживания.

1.2. Привыкли рассуждать концептуально? UML спешит на помощь

Известно ли вам что-нибудь о UML? UML (англ. Unified Modeling Language унифицированный язык моделирования) язык графического описания для разработки объектно-ориентированных систем. Разрабатывая систему, мы обычно сначала представляем ее в абстрактном виде, а затем пишем код на том или ином языке программирования. Движение сверху вниз кажется наиболее разумным решением для разработчиков. Тем не менее, мой опыт аналитика подсказывает, что иногда такое движение невозможно по ряду причин: сжатые сроки разработки приложения, ни у кого из членов команды нет возможности быстро применить свои навыки в области UML либо некоторые разработчики не знают тех или иных элементов UML.

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

Логотип UML

Рис. 2. Логотип UML


1.3. Hello World! Ваш первый класс при изучении ООП

Если вы абсолютный новичок в объектно-ориентированном программировании, то я рекомендую для начала ознакомиться со Справочником MQL5, а затем прочитать статью Написание советника в MQL5 с использованием объектно-ориентированного подхода. Также обязательно прочтите какие-нибудь дополнительные материалы по теме. С этого момента я предполагаю, что вы уже знакомы с OOП и с легкостью поймете стандартный пример класса Person, который на MQL5 выглядит так:

//+------------------------------------------------------------------+
//| Класс CPerson                                                    |
//+------------------------------------------------------------------+
class CPerson
  {
protected:
   string            m_first_name;
   string            m_surname;
   datetime          m_birth;

public:
   //--- Конструктор и деструктор
                     CPerson(void);
                    ~CPerson(void);
   //--- Геттеры
   string            GetFirstName(void);
   string            GetSurname(void);
   datetime          GetBirth(void);
   //--- Сеттеры
   void              SetFirstName(string first_name);
   void              SetSurname(string surname);
   void              SetBirth(datetime birth);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPerson::CPerson(void)
  {
   Alert("Hello world! Отображается при создании объекта типа CPerson!");
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPerson::~CPerson(void)
  {
   Alert("Goodbye world! Запускается при уничтожении объекта!");
  }
//+------------------------------------------------------------------+
//| GetFirstName                                                     |
//+------------------------------------------------------------------+
string CPerson::GetFirstName(void)
  {
   return m_first_name;
  }
//+------------------------------------------------------------------+
//| GetSurname                                                       |
//+------------------------------------------------------------------+
string CPerson::GetSurname(void)
  {
   return m_surname;
  }
//+------------------------------------------------------------------+
//| GetBirth                                                         |
//+------------------------------------------------------------------+
datetime CPerson::GetBirth(void)
  {
   return m_birth;
  }
//+------------------------------------------------------------------+
//| SetFirstName                                                     |
//+------------------------------------------------------------------+
void CPerson::SetFirstName(string first_name)
  {
   m_first_name=first_name;
  }
//+------------------------------------------------------------------+
//| SetSurname                                                       |
//+------------------------------------------------------------------+
void CPerson::SetSurname(string surname)
  {
   m_surname=surname;
  }
//+------------------------------------------------------------------+
//| SetBirth                                                         |
//+------------------------------------------------------------------+
void CPerson::SetBirth(datetime birth)
  {
   m_birth=birth;
  }
//+------------------------------------------------------------------+

Следующая информация предназначена для разработчиков, стремящихся к созданию безукоризненного кода. В отличие от некоторых примеров, представленных в статьях о программировании на MQL5, а также многих приложений, опубликованных в Code Base, в представленном выше классе действительно используются соглашения (conventions), применяемые компанией MetaQuotes Software Corp при разработке среды MQL5. Я призываю вас писать код так, как это делают в MetaQuotes. Кстати, тема About conventions in OOP MQL5 programs ('О соглашениях в ООП приложений на MQL5') в англоязычной части форума затрагивает этот вопрос.

Вкратце, самые важные соглашения, реализованные в коде Person.mqh, заключаются в следующем:

  • Имя класса CPerson начинается с заглавной буквы С.
  • Имена методов пишутся без пробелов ("горбатый регистр") с заглавной буквы, например, GetFirstName, SetSurname и т.д.
  • Имена защищенных свойств начинаются с префикса m_, например, m_first_name, m_surname и m_birth.
  • Зарезервированное слово this не используется для обозначения членов класса внутри самого класса.

Пожалуйста, взгляните на некоторые файлы среды MQL5, например, Include\Arrays\Array.mqh, Include\Arrays\List.mqh, Include\Trade\Trade.mqh и обратите внимание на оригинальный MQL5-код.


2. Программируем наш первый объектно-ориентированный советник


2.1. Суть торговой системы

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


2.2. Основа объектно-ориентированного робота

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

Мозгом является та часть советника, которая содержит данные, необходимые для работы, то есть что-то вроде ПЗУ (read-only memory, ROM). График это совокупность данных, эмулирующих рабочий график торгового робота. Наконец, эволюция – это совокупность данных, содержащих временную информацию, включая состояние робота в данный момент, историю проведенных операций и т.д. Это можно сравнить с созданием человеческого существа, своеобразного Франкенштейна, путем проектирования каждого отдельного органа при разработке приложения для сферы здравоохранения. Каждый орган в данном случае представляет собой уникальное понятие, связанное с другими частями целого.

Прежде всего, создадим папку MQL5\Include\Mine для хранения наших пользовательских компонентов. Это просто идея организации вашего кода. Она может оказаться весьма полезной в ваших разработках, однако это не означает, что вы обязаны использовать именно ее. Затем создадим файл MQL5\Include\Mine\Enums.mqh для хранения создаваемых нами перечислений:

//+------------------------------------------------------------------+
//| Статусы                                                          |
//+------------------------------------------------------------------+
enum ENUM_STATUS_EA
  {
   BUY,
   SELL,
   DO_NOTHING
  };
//+------------------------------------------------------------------+
//| Временные интервалы                                              |
//+------------------------------------------------------------------+
enum ENUM_LIFE_EA
  {
   HOUR,
   DAY,
   WEEK,
   MONTH,
   YEAR
  };
//+------------------------------------------------------------------+

Теперь настала пора создать заготовку нашего торгового советника, которая будет названа ExpertSimpleRandom.mq5! Создадим папку MQL5\Experts\SimpleRandom, а внутри нее – файл ExpertSimpleRandom.mq5 , содержащий следующий код:

//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagañas |
//+------------------------------------------------------------------+

#property copyright     "Copyright © 2013, laplacianlab"
#property link          "https://www.mql5.com/en/articles"
#property version       "1.00"

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Indicators.mqh>
#include <Mine\Enums.mqh>
#include <..\Experts\SimpleRandom\CSimpleRandom.mqh>

input int               StopLoss;
input int               TakeProfit;
input double            LotSize;
input ENUM_LIFE_EA      TimeLife;

MqlTick tick;
CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife);
//+------------------------------------------------------------------+
//| Функция инициализации                                            |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   SR.Init();
   return(0);
  }
//+------------------------------------------------------------------+
//| Функция деинициализации                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   SR.Deinit();
   delete(SR);
  }
//+------------------------------------------------------------------+
//| Функция события OnTick                                           |
//+------------------------------------------------------------------+
void OnTick()
  {
   SymbolInfoTick(_Symbol,tick);
   SR.Go(tick.ask,tick.bid);
  }
//+------------------------------------------------------------------+

Это лишь один из возможных подходов. Пример приведен, чтобы показать, как ООП работает в MQL5. Как вы видите, основной класс торгового советника называется CSimpleRandom.mqh. Пожалуйста, сохраните его в MQL5\Experts\SimpleRandom\CSimpleRandom.mqh:

//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagañas |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Mine\Enums.mqh>
#include <..\Experts\SimpleRandom\CBrain.mqh>
#include <..\Experts\SimpleRandom\CEvolution.mqh>
#include <..\Experts\SimpleRandom\CGraphic.mqh>
//+------------------------------------------------------------------+
//| Класс CSimpleRandom                                              |
//+------------------------------------------------------------------+
class CSimpleRandom
  {
protected:
   CBrain           *m_brain;
   CEvolution       *m_evolution;
   CGraphic         *m_graphic;
   CTrade           *m_trade;
   CPositionInfo    *m_positionInfo;
public:
   //--- Конструктор и деструктор
                     CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life);
                    ~CSimpleRandom(void);
   //--- Геттеры
   CBrain           *GetBrain(void);
   CEvolution       *GetEvolution(void);
   CGraphic         *GetGraphic(void);
   CTrade           *GetTrade(void);
   CPositionInfo    *GetPositionInfo(void);
   //--- Специальные методы CSimpleRandom
   bool              Init();
   void              Deinit(void);
   bool              Go(double ask,double bid);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CSimpleRandom::CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life)
  {
   int lifeInSeconds;

   switch(time_life)
     {
      case HOUR:

         lifeInSeconds=3600;

         break;

      case DAY:

         lifeInSeconds=86400;

         break;

      case WEEK:

         lifeInSeconds=604800;

         break;

      case MONTH:

         lifeInSeconds=2592000;

         break;

         // Один год

      default:

         lifeInSeconds=31536000;

         break;
     }

   m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit);
   m_evolution=new CEvolution(DO_NOTHING);
   m_graphic=new CGraphic(_Symbol);
   m_trade=new CTrade();
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CSimpleRandom::~CSimpleRandom(void)
  {
   delete(m_brain);
   delete(m_evolution);
   delete(m_graphic);
   delete(m_trade);
  }
//+------------------------------------------------------------------+
//| GetBrain                                                         |
//+------------------------------------------------------------------+
CBrain *CSimpleRandom::GetBrain(void)
  {
   return m_brain;
  }
//+------------------------------------------------------------------+
//| GetBrain                                                         |
//+------------------------------------------------------------------+
CEvolution *CSimpleRandom::GetEvolution(void)
  {
   return m_evolution;
  }
//+------------------------------------------------------------------+
//| GetGraphic                                                       |
//+------------------------------------------------------------------+
CGraphic *CSimpleRandom::GetGraphic(void)
  {
   return m_graphic;
  }
//+------------------------------------------------------------------+
//| GetTrade                                                         |
//+------------------------------------------------------------------+
CTrade *CSimpleRandom::GetTrade(void)
  {
   return m_trade;
  }
//+------------------------------------------------------------------+
//| GetPositionInfo                                                  |
//+------------------------------------------------------------------+
CPositionInfo *CSimpleRandom::GetPositionInfo(void)
  {
   return m_positionInfo;
  }
//+------------------------------------------------------------------+
//| Инициализация CSimpleRandom                                      |
//+------------------------------------------------------------------+
bool CSimpleRandom::Init(void)
  {
// Здесь располагается логика инициализации...
   return true;
  }
//+------------------------------------------------------------------+
//| Деинициализация CSimpleRandom                                    |
//+------------------------------------------------------------------+
void CSimpleRandom::Deinit(void)
  {
// Здесь располагается логика деинициализации...
   delete(m_brain);
   delete(m_evolution);
   delete(m_graphic);
   delete(m_trade);
  }
//+------------------------------------------------------------------+
//| CSimpleRandom Go                                                 |
//+------------------------------------------------------------------+
bool CSimpleRandom::Go(double ask,double bid)
  {
   double tp;
   double sl;

   int coin=m_brain.GetRandomNumber(0,1);

// Есть ли открытая позиция?     

   if(!m_positionInfo.Select(_Symbol))
     {
      // Если нет, открываем

      if(coin==0)
        {
         GetEvolution().SetStatus(BUY);
        }
      else
        {
         GetEvolution().SetStatus(SELL);
        }
     }

// В этом случае советник отрабатывает мат. ожидание

   else GetEvolution().SetStatus(DO_NOTHING);

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

         tp = ask + m_brain.GetTakeProfit() * _Point;
         sl = bid - m_brain.GetStopLoss() * _Point;

         GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp);

         break;

      case SELL:

         sl = ask + m_brain.GetStopLoss() * _Point;
         tp = bid - m_brain.GetTakeProfit() * _Point;

         GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp);

         break;

      case DO_NOTHING:

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

         break;
     }

// В случае ошибки должен возвращать false, пока же всегда возвращает true  

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


2.3. Привязка CSimpleRandom к объектам сложного типа

Обратите внимание, как пользовательские объекты типа CBrain, CEvolution и CGraphic связаны с CSimpleRandom.

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

protected:
   CBrain           *m_brain;
   CEvolution       *m_evolution;
   CGraphic         *m_graphic; 

И сразу после этого мы создаем эти объекты внутри конструктора:

m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit);         
m_evolution=new CEvolution(DO_NOTHING);      
m_graphic=new CGraphic(_Symbol);

Мы динамически создаем объекты сложного типа, как указано в разделе Указатели объектов в официальной документации. При использовании такой схемы мы получаем доступ к функционалу CBrain, CEvolution и CGraphic напрямую из CSimpleRandom. Например, мы могли бы запустить следующий код в ExpertSimpleRandom.mq5:

//+------------------------------------------------------------------+
//| Функция события OnTick                                           |
//+------------------------------------------------------------------+
void OnTick()
   {              
      // ...
      
      int randNumber=SR.GetBrain().GetRandomNumber(4, 8);

      // ...          
  }

В завершение данного раздела приведу ниже код классов CBrain, CEvolution и CGraphic. Пожалуйста, имейте в виду, что есть некоторые детали, программный код для которых не приведен, так как они не столь важны при обратном тестировании SimpleRandom. Написание кода для недостающих частей данных классов я оставляю вам в качестве упражнения. Вы можете поэкспериментировать с ними. Например, m_death фактически не используется, однако его предназначение – дать знать с самого начала, когда конкретно робот прекратит работу.

//+------------------------------------------------------------------+
//|                                               ExpertSimpleRandom |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Класс CBrain                                                     |
//+------------------------------------------------------------------+
class CBrain
  {
protected:
   ENUM_TIMEFRAMES   m_period;               // всегда должен быть инициализирован в виде PERIOD_M1 для соответствия идее, заложенной в систему
   datetime          m_birth;               // Дата и время первой инициализации робота
   datetime          m_death;               // Дата и время остановки робота
   double            m_size;                // Размер позиций
   int               m_stopLoss;            // Stop Loss
   int               m_takeProfit;          // Take Profit

public:
   //--- Конструктор и деструктор
                     CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit);
                    ~CBrain(void);
   //--- Геттеры
   datetime          GetBirth(void);
   datetime          GetDeath(void);
   double            GetSize(void);
   int               GetStopLoss(void);
   int               GetTakeProfit(void);
   //--- Сеттеры
   void              SetBirth(datetime birth);
   void              SetDeath(datetime death);
   void              SetSize(double size);
   void              SetStopLoss(int stopLoss);
   void              SetTakeProfit(int takeProfit);
   //--- Особая логика
   int               GetRandomNumber(int a,int b);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBrain::CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit)
  {
   MathSrand(GetTickCount());

   m_period=PERIOD_M1;
   m_birth=birth;
   m_death=death;
   m_size=size;
   m_stopLoss=stopLoss;
   m_takeProfit=takeProfit;
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CBrain::~CBrain(void)
  {
  }
//+------------------------------------------------------------------+
//| GetBirth                                                         |
//+------------------------------------------------------------------+
datetime CBrain::GetBirth(void)
  {
   return m_birth;
  }
//+------------------------------------------------------------------+
//| GetDeath                                                         |
//+------------------------------------------------------------------+
datetime CBrain::GetDeath(void)
  {
   return m_death;
  }
//+------------------------------------------------------------------+
//| GetSize                                                          |
//+------------------------------------------------------------------+
double CBrain::GetSize(void)
  {
   return m_size;
  }
//+------------------------------------------------------------------+
//| GetStopLoss                                                      |
//+------------------------------------------------------------------+
int CBrain::GetStopLoss(void)
  {
   return m_stopLoss;
  }
//+------------------------------------------------------------------+
//| GetTakeProfit                                                    |
//+------------------------------------------------------------------+
int CBrain::GetTakeProfit(void)
  {
   return m_takeProfit;
  }
//+------------------------------------------------------------------+
//| SetBirth                                                         |
//+------------------------------------------------------------------+
void CBrain::SetBirth(datetime birth)
  {
   m_birth=birth;
  }
//+------------------------------------------------------------------+
//| SetDeath                                                         |
//+------------------------------------------------------------------+
void CBrain::SetDeath(datetime death)
  {
   m_death=death;
  }
//+------------------------------------------------------------------+
//| SetSize                                                          |
//+------------------------------------------------------------------+
void CBrain::SetSize(double size)
  {
   m_size=size;
  }
//+------------------------------------------------------------------+
//| SetStopLoss                                                      |
//+------------------------------------------------------------------+
void CBrain::SetStopLoss(int stopLoss)
  {
   m_stopLoss=stopLoss;
  }
//+------------------------------------------------------------------+
//| SetTakeProfit                                                    |
//+------------------------------------------------------------------+
void CBrain::SetTakeProfit(int takeProfit)
  {
   m_takeProfit=takeProfit;
  }
//+------------------------------------------------------------------+
//| GetRandomNumber                                                  |
//+------------------------------------------------------------------+
int CBrain::GetRandomNumber(int a,int b)
  {
   return(a+(MathRand()%(b-a+1)));
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                               ExpertSimpleRandom |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+
#include <Indicators\Indicators.mqh>
#include <Mine\Enums.mqh>
//+------------------------------------------------------------------+
//| Класс CEvolution                                                 |
//+------------------------------------------------------------------+
class CEvolution
  {
protected:
   ENUM_STATUS_EA    m_status;            // Текущее состояние торгового советника
   CArrayObj*        m_operations;        // История операций, совершенных советником

public:
   //--- Конструктор и деструктор
                     CEvolution(ENUM_STATUS_EA status);
                    ~CEvolution(void);
   //--- Геттеры
   ENUM_STATUS_EA    GetStatus(void);
   CArrayObj        *GetOperations(void);
   //--- Сеттеры
   void              SetStatus(ENUM_STATUS_EA status);
   void              SetOperation(CObject *operation);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvolution::CEvolution(ENUM_STATUS_EA status)
  {
   m_status=status;
   m_operations=new CArrayObj;
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CEvolution::~CEvolution(void)
  {
   delete(m_operations);
  }
//+------------------------------------------------------------------+
//| GetStatus                                                        |
//+------------------------------------------------------------------+
ENUM_STATUS_EA CEvolution::GetStatus(void)
  {
   return m_status;
  }
//+------------------------------------------------------------------+
//| GetOperations                                                    |
//+------------------------------------------------------------------+
CArrayObj *CEvolution::GetOperations(void)
  {
   return m_operations;
  }
//+------------------------------------------------------------------+
//| SetStatus                                                        |
//+------------------------------------------------------------------+
void CEvolution::SetStatus(ENUM_STATUS_EA status)
  {
   m_status=status;
  }
//+------------------------------------------------------------------+
//| SetOperation                                                     |
//+------------------------------------------------------------------+
void CEvolution::SetOperation(CObject *operation)
  {
   m_operations.Add(operation);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                           ExpertSimpleRandom.mq5 |
//|                               Copyright © 2013, Jordi Bassagaсas |
//+------------------------------------------------------------------+
#include <Trade\SymbolInfo.mqh>
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Класс CGrapic                                                    |
//+------------------------------------------------------------------+
class CGraphic
  {
protected:
   ENUM_TIMEFRAMES   m_period;            // Период 
   string            m_pair;              // Пара
   CSymbolInfo*      m_symbol;            // Объект CSymbolInfo
   CArrayObj*        m_bars;              // Массив баров

public:
   //--- Конструктор и деструктор
                     CGraphic(string pair);
                    ~CGraphic(void);
   //--- Геттеры
   string            GetPair(void);
   CSymbolInfo      *GetSymbol(void);
   CArrayObj        *GetBars(void);
   //--- Сеттеры
   void              SetPair(string pair);
   void              SetSymbol(CSymbolInfo *symbol);
   void              SetBar(CObject *bar);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CGraphic::CGraphic(string pair)
  {
   m_period=PERIOD_M1;
   m_pair=pair;
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CGraphic::~CGraphic(void)
  {
  }
//+------------------------------------------------------------------+
//| GetPair                                                          |
//+------------------------------------------------------------------+
string CGraphic::GetPair(void)
  {
   return m_pair;
  }
//+------------------------------------------------------------------+
//| GetSymbol                                                        |
//+------------------------------------------------------------------+
CSymbolInfo *CGraphic::GetSymbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//| GetBars                                                          |
//+------------------------------------------------------------------+
CArrayObj *CGraphic::GetBars(void)
  {
   return m_bars;
  }
//+------------------------------------------------------------------+
//| SetPair                                                          |
//+------------------------------------------------------------------+
void CGraphic::SetPair(string pair)
  {
   m_pair=pair;
  }
//+------------------------------------------------------------------+
//| SetSymbol                                                        |
//+------------------------------------------------------------------+
void CGraphic::SetSymbol(CSymbolInfo *symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//| SetBar                                                           |
//+------------------------------------------------------------------+
void CGraphic::SetBar(CObject *bar)
  {
   m_bars.Add(bar);
  }
//+------------------------------------------------------------------+

3. Обратное тестирование ExpertSimpleRandom.mq5

Как и ожидалось, данная торговая система оказалась подходящей только для определенных уровней Stop Loss и Take Profit. Разумеется, данные прибыльные интервалы SL/TP не совпадают для разных символов. Это происходит потому, что каждый символ имеет свои особенности или, другими словами, каждая валютная пара движется по-своему относительно других. Поэтому перед запуском ExpertSimpleRandom.mq5 в реальных условиях определите эти уровни, используя обратное тестирование.

Ниже приведены некоторые данные, при которых идея, описанная в статье, оказалась выигрышной. Эти данные могут быть выведены после многократных прогонов ExpertSimpleRandom.mq5 в тестере стратегий MetaTrader 5.

Некоторые выигрышные входные данные для EURUSD (январь, 2012 г.):

  • StopLoss: 400
  • TakeProfit: 600
  • LotSize: 0.01
  • TimeLife: MONTH

Прогон 1:

EURUSD, январь 2012

Прогон 2:

EURUSD, январь 2012

Прогон 3:

EURUSD, январь 2012


Заключение

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

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

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


Перевод с английского произведен MetaQuotes Software Corp.
Оригинальная статья: https://www.mql5.com/en/articles/703

Прикрепленные файлы |
cbrain.mqh (5.58 KB)
cevolution.mqh (2.96 KB)
cgraphic.mqh (3.5 KB)
enums.mqh (0.92 KB)
csimplerandom.mqh (6.38 KB)
Генератор торговых сигналов пользовательского индикатора Генератор торговых сигналов пользовательского индикатора

Как сделать генератор торговых сигналов основанный на пользовательском индикаторе. Как создать пользовательский индикатор. Как получить доступ к данным пользовательского индикатора. Зачем нужна конструкция IS_PATTERN_USAGE(0) и model 0.

Рецепты MQL5 - Записываем историю сделок в файл и строим графики балансов для каждого символа в Excel Рецепты MQL5 - Записываем историю сделок в файл и строим графики балансов для каждого символа в Excel

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

Создание нейросетевых торговых роботов на базе MQL5 Wizard и Hlaiman EA Generator Создание нейросетевых торговых роботов на базе MQL5 Wizard и Hlaiman EA Generator

В статье рассматривается метод автоматизированного создания нейросетевых торговых роботов на базе MQL5 Wizard и Hlaiman EA Generator. Узнайте, как легко начать работать с нейронными сетями, минуя длительные этапы изучения теоретических материалов и написания собственного кода.

Рецепты MQL5 - Уменьшаем эффект подгонки и решаем проблему недостаточного количества котировок Рецепты MQL5 - Уменьшаем эффект подгонки и решаем проблему недостаточного количества котировок

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