English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Создай торгового робота за 6 шагов!

Создай торгового робота за 6 шагов!

MetaTrader 5Примеры | 1 июня 2012, 09:43
27 084 62
MetaQuotes
MetaQuotes

Еще раз о Мастере MQL5

Окружающий мир стремительно меняется, и мы стараемся за ним поспевать. У нас нет времени на изучение чего-то нового,  и в этом отношении человек неизменчив. Трейдеры такие же люди, как и все, они хотят получить хороший результат за минимум усилий. И поэтому в редакторе MetaEditor 5 есть такая замечательная штука как Мастер MQL5.  Как с его помощью создать готовую автоматическую торговую систему написано уже не раз, начиная от "лайт-версии" MQL5 Wizard для "чайников" и заканчивая "версией от создателей" - Мастер MQL5: Новая версия.

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


Пять страшных классов

Да, Мастер MQL5 сильно упрощает создание экспертов, но предварительно нужно изучить, что же ему подавать на вход. Для того чтобы автоматически создать эксперта с помощью Мастера MQL5, необходимо чтобы его компоненты придерживались пяти базовых классов из раздела Базовые классы экспертов:

  • CExpertBase - некий базовый класс для четырех других классов;
  • CExpert - собственно, сам класс для создания торгового робота, именно он и торгует;
  • CExpertSignal - класс для создания модуля торговых сигналов, о нем и будет статья;
  • CExpertTrailing - класс для подтягивания защитного Stop Loss;
  • CExpertMoney - класс для реализации управления капиталом.

Перед нами вся сила "великого и ужасного" подхода, именуемого Объектно-Ориентированным Программированием (ООП). Но пугаться не нужно, ведь в наше время почти у каждого есть мобильный телефон с кучей функцией, и практически никто не знает как это работает. Нам не нужно всё это изучать, мы только немного затронем класс CExpertSignal, да и то несколько его функций.


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


1. Создание своего класса с нуля

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



Теперь правой кнопкой мышки жмем на созданную папку, выбираем "New File" и создаем новый класс для нашего будущего модуля торговых сигналов.


Заполняем поля:

  • Class Name - имя класса. Мы будем создавать модуль для генерации сигналов по пересечению двух скользящих средних, поэтому класс назовем однозначно и понятно - MA_Cross.
  • Base Name - от кого наследуемся. А наследоваться мы должны от базового класса CExpertSignal.

Жмем "Готово" и черновик нашего модуля перед нами. Пока всё идет хорошо и просто. Допишем только в получившийся файл объявление #include, чтобы компилятор знал, где ему искать этот самый базовый класс CExpertSignal

#include "..\ExpertSignal.mqh"   // класс CExpertSignal находится в файле ExpertSignal

Результат:

//+------------------------------------------------------------------+
//|                                                     MA_Cross.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "..\ExpertSignal.mqh"   // класс CExpertSignal находится в файле ExpertSignal
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class MA_Cross : public CExpertSignal
  {
private:

public:
                     MA_Cross();
                    ~MA_Cross();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MA_Cross::~MA_Cross()
  {
  }
//+------------------------------------------------------------------+

Проверяем полученный класс на отсутствие ошибок компиляции - жмем кнопку F7. Ошибок нет и мы можем двигаться дальше.


2. Дескриптор модуля

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


Впрочем, откуда ему взяться? Мы же не дали ему никаких опознавательных знаков, чтобы Мастер MQL5 мог понять, что наш класс может быть чем-то полезен. Исправим эту ситуацию  - при внимательном изучении любого модуля из стандартной поставки можно обнаружить, что каждый их них содержит шапку в начале файла. Это дескриптор (описатель) модуля, составленный по определенным правилам. И правила эти очень простые.

Откроем, например, исходный код модуля торговых сигналов на основе индикатора AMA (описание логики в разделе Сигналы индикатора Adaptive Moving Average). И запустим Мастер MQL5, в котором выберем этот модуль. Сравните:

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

  • Title  - имя модуля для отображения в Мастере MQL5;
  • Type - указывает на версию модуля сигналов. Всегда должен быть SignalAdvanced;
  • Name - отображает имя модуля после его выбора в Мастере MQL5 и используется в комментариях для описания внешних параметров сгенерированного эксперта (желательно указывать);
  • ShortName - префикс для автоматического формирования имен внешних параметров в сгенерированном эксперте (в виде Signal_<ShortName>_<ParameterName>);
  • Class - имя класса, который содержится в данном модуле;
  • Page - параметр для вызова справки по данному модулю (только для модулей из стандартной поставки).

Далее идут описания параметров в виде Parameter=список_значений, в котором через запятую перечислено следующее:

  1. Имя функции для установки значения параметра при запуске эксперта;
  2. Тип параметра, может быть перечислением;
  3. Значение по умолчанию для параметра, то есть то значение, которое будет установлено параметру, если вы его не измените в Мастере MQL5;
  4. Описание параметра, которое вы увидите при запуске эксперта, сгенерированного Мастером MQL5.

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

  • FastPeriod - период быстрой скользящей средней;
  • FastMethod - тип сглаживания быстрой скользящей средней;
  • SlowPeriod - период медленной скользящей средней;
  • SlowMethod - тип сглаживания медленной скользящей средней.

Можно было бы еще добавить смещение и тип цен для расчета каждой скользящей средней, но это принципиально ничего не меняет. Итак текущий вариант таков:

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Сигналы на пересечении двух средних                        |
//| Type=SignalAdvanced                                              |
//| Name=My_MA_Cross                                                 |
//| ShortName=MaCross                                                |
//| Class=MA_Cross                                                   |
//| Page=не нужно                                                    |
//| Parameter=FastPeriod,int,13,Period of fast MA                    |
//| Parameter=FastMethod,ENUM_MA_METHOD,MODE_SMA,Method of fast MA   |
//| Parameter=SlowPeriod,int,21,Period of slow MA                    |
//| Parameter=SlowMethod,ENUM_MA_METHOD,MODE_SMA,Method of slow MA   |
//+------------------------------------------------------------------+
// wizard description end

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

  1. Отображаемое имя в Мастере MQL5 - "Сигналы на пересечении двух средних".
  2. Четыре внешних параметра для настройки торговых сигналов.
    • FastPeriod - период быстрой скользящей средней со значением по умолчанию 13;
    • FastMethod - тип сглаживания быстрой скользящей средней, по умолчанию простое сглаживание;
    • SlowPeriod - период медленной скользящей средней со значением по умолчанию 21;
    • SlowMethod - тип медленной быстрой скользящей средней, по умолчанию простое сглаживание.

Сохраняем изменения и компилируем - ошибок быть не должно! Запускаем для проверки Мастер MQL5 - и, о чудо! Наш модуль стал доступен для выбора и в нем показаны все наши параметры!

Поздравляю, наш модуль торговых сигналов выглядит как живой!


3. Методы для установки параметров

Теперь настала очередь взяться за внешние параметры. Так как наш торговый модуль  представлен классом MA_Cross, то его параметры должны храниться внутри самого класса в виде private членов. Добавим в объявление класса четыре строчки по числу параметров. Тип параметра мы уже описали в дескрипторе и знаем:

class MA_Cross : public CExpertSignal
  {
private:
   //--- настраиваемые параметры модуля
   int               m_period_fast;    // период быстрой скользящей средней
   int               m_period_slow;    // период медленной скользящей средней
   ENUM_MA_METHOD    m_method_fast;    // тип сглаживания быстрой скользящей средней
   ENUM_MA_METHOD    m_method_slow;    // тип сглаживания медленной скользящей средней

Но каким же образом значения внешних параметров модуля попадут в соответствующие члены нашего класса MA_Cross? Оказывается, всё очень просто, достаточно в классе объявить одноименные публичные методы, а именно, добавить четыре строчки в раздел public:

class MA_Cross : public CExpertSignal
  {
private:
   //--- настраиваемые параметры модуля
   int               m_period_fast;    // период быстрой скользящей средней
   int               m_period_slow;    // период медленной скользящей средней
   ENUM_MA_METHOD    m_method_fast;    // тип сглаживания быстрой скользящей средней
   ENUM_MA_METHOD    m_method_slow;    // тип сглаживания медленной скользящей средней

public:
   //--- конструктор класса
                     MA_Cross();
   //--- деструктор класса
                    ~MA_Cross();
   //--- методы для установки 
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   };

Когда вы сгенерируете советник на основе данного модуля с помощью Мастера MQL5 и запустите его на графике, именно эти четыре метода будут автоматически вызваны при инициализации советника. Отсюда выведем простое правило:

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

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

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

class MA_Cross : public CExpertSignal
  {
private:
   //--- настраиваемые параметры модуля
   int               m_period_fast;    // период быстрой скользящей средней
   ENUM_MA_METHOD    m_method_fast;     // тип сглаживания быстрой скользящей средней
   int               m_period_slow;    // период медленной скользящей средней
   ENUM_MA_METHOD    m_method_slow;     // тип сглаживания медленной скользящей средней

public:
   //--- конструктор класса
                     MA_Cross(void);
   //--- деструктор класса
                    ~MA_Cross(void);
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
MA_Cross::MA_Cross(void) : m_period_fast(13),          // по умолчанию период быстрой MA=13
                           m_method_fast(MODE_SMA),    // по умолчанию простой метод усреднения быстрой средней
                           m_period_slow(21),          // по умолчанию период медленной MA=21
                           m_method_slow(MODE_SMA)     // по умолчанию простой метод усреднения медленной средней
  {
  }

Инициализация членов класса здесь производится с помощью списка инициализации.

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


4. Нужно проверить входные параметры на корректность

Параметры для нашего торгового модуля мы создали, методы для установки им значений написали, и теперь наступает следующий важный этап - параметры нужно проверять на корректность. В нашем случае мы должны проверять периоды скользящих средних и тип сглаживания для их расчета. Для этого вы должны в классе написать свой метод ValidationSettings(). Этот метод определен еще в самом родительском классе CExpertBase, а во всех его потомках просто переопределяется в обязательном порядке.

Но если вы ничего не знаете об объектно-ориентированном программировании, то просто запомните - в нашем классе нужно написать функцию ValidationSettings(), которая не требует параметров и возвращает true или false.

class MA_Cross : public CExpertSignal
  {
...
   //--- конструктор класса
                     MA_Cross(void);
   //--- деструктор класса
                    ~MA_Cross(void);
   //--- проверка корректности входных данных
   bool              ValidationSettings();
...
   };
//+------------------------------------------------------------------+
//| провереряет входные параметры и возвращает true если всё OK      |
//+------------------------------------------------------------------+
bool MA_Cross:: ValidationSettings()
  {
   //--- вызываем метод базового класса
   if(!CExpertSignal::ValidationSettings())  return(false);
   //--- проверим периоды, количество баров для расчета скользящей >=1
   if(m_period_fast<1 || m_period_slow<1)
     {
      PrintFormat("Неверно задано значение одного из периодов! FastPeriod=%d, SlowPeriod=%d",
                  m_period_fast,m_period_slow);
      return false;
     }
//--- период медленной должен быть больше периода быстрой скользящей
   if(m_period_fast>m_period_slow)
     {
      PrintFormat("SlowPeriod=%d должен быть больше чем FastPeriod=%d!",
                  m_period_slow,m_period_fast);
      return false;
     }
//--- тип сглаживания быстрой скользящей должен быть один из четырех значений перечисления
   if(m_method_fast!=MODE_SMA && m_method_fast!=MODE_EMA && m_method_fast!=MODE_SMMA && m_method_fast!=MODE_LWMA)
     {
      PrintFormat("Недопустимый тип сглаживания быстрой скользящей средней!");
      return false;
     }
//--- тип сглаживания медленной скользящей должен быть один из четырех значений перечисления
   if(m_method_slow!=MODE_SMA && m_method_slow!=MODE_EMA && m_method_slow!=MODE_SMMA && m_method_slow!=MODE_LWMA) 
     {
      PrintFormat("Недопустимый тип сглаживания медленной скользящей средней!");
      return false;
     }
//--- все проверки прошли, значит, всё хорошо
   return true;
  }
Как видите, мы добавили в публичной части класса MA_Cross объявление метода ValidationSettings(), а затем добавили само тело метода в виде
bool MA_Cross:: ValidationSettings()

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

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

//--- вызываем метод базового класса
   if(!CExpertSignal::ValidationSettings())  return(false);
//--- наш код для проверки значений параметров

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


5. Где же наши индикаторы?

Пора заняться индикаторами, вся подготовительная работа с параметрами для них сделана. Каждый модуль торговых сигналов содержит метод InitIndicators(), который будет автоматически вызван при запуске сгенерированного эксперта. Именно в этом методе мы должны обеспечить наш модуль индикаторами скользящих средних.

Сначала объявляем в классе метод InitIndicators() и вставляем его заготовку:

public:
   //--- конструктор класса
                     MA_Cross(void);
   //--- деструктор класса
                    ~MA_Cross(void);
   //--- методы для установки 
   void              FastPeriod(int value)               { m_period_fast=value;        }
   void              FastMethod(ENUM_MA_METHOD value)    { m_method_fast=value;        }
   void              SlowPeriod(int value)               { m_period_slow=value;        }
   void              SlowMethod(ENUM_MA_METHOD value)    { m_method_slow=value;        }
   //--- проверка корректности входных данных
   bool              ValidationSettings();
   //--- создание индикаторов и таймсерий для работы модуля сигналов
   bool              InitIndicators(CIndicators *indicators);
  };
...
//+------------------------------------------------------------------+
//| Создает индикаторы                                               |
//| На входе:  указатель на коллекцию индикаторов                    |
//| На выходе: true-при успешном выполнении, иначе false             |
//+------------------------------------------------------------------+
bool MA_Сross::InitIndicators(CIndicators* indicators)
  {
//--- стандартная проверка коллекции индикаторов на NULL
   if(indicators==NULL)                           return(false);
//--- инициализация индикаторов и таймсерий в дополнительных фильтрах
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- создание наших индикаторов скользящих средних
   ... тут какой-то код
//--- дошли до этого места, значит, функция выполнена успешно - вернем true
   return(true);
  }

То есть ничего сложного, объявляем метод и далее просто создаем тело метода, как мы уже делали для метода ValidationSettings(). Главное, не забудьте при определении функции поставить имя класса и оператор ::. Заготовка есть, теперь в нее необходимо вставить код для создания скользящих средних. Сделаем это правильно - создадим для каждого индикатора отдельную функцию в классе, которая возвращает true в случае успеха. Имя этой функции может быть любым, но пусть оно отражает её предназначение, поэтому назовем их CreateFastMA() и CreateSlowMA().

protected:
   //--- создание индикаторов скользящих средних
   bool              CreateFastMA(CIndicators *indicators);
   bool              CreateSlowMA(CIndicators *indicators);
  };
//+------------------------------------------------------------------+
//| Создает индикаторы                                               |
//| На входе:  указатель на коллекцию индикаторов                    |
//| На выходе: true-при успешном выполнении, иначе false             |
//+------------------------------------------------------------------+
bool MA_Cross::InitIndicators(CIndicators *indicators)
  {
//--- стандартная проверка коллекции индикаторов на NULL
   if(indicators==NULL) return(false);
//--- инициализация индикаторов и таймсерий в дополнительных фильтрах
   if(!CExpertSignal::InitIndicators(indicators)) return(false);
//--- создание наших индикаторов скользящих средних
   if(!CreateFastMA(indicators))                  return(false);
   if(!CreateSlowMA(indicators))                  return(false);
//--- дошли до этого места, значит, функция выполнена успешно - вернем true
   return(true);
  }
//+------------------------------------------------------------------+
//| Создает индикатор "Быстрая MA"                                   |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
... какой-то код
//--- дошли до этого места, значит, функция выполнена успешно - вернем true
   return(true);
  }
//+------------------------------------------------------------------+
//| Создает индикатор "Медленная MA"                                 |
//+------------------------------------------------------------------+
bool MA_Cross::CreateSlowMA(CIndicators *indicators)
  {
... какой-то код
//--- дошли до этого места, значит, функция выполнена успешно - вернем true
   return(true);
  }

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

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

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

class MA_Cross : public CExpertSignal
  {
private:
   CiCustom          m_fast_ma;            // индикатор в виде объекта
   CiCustom          m_slow_ma;            // индикатор в виде объекта
   //--- настраиваемые параметры модуля
   int              m_period_fast;   // период быстрой скользящей средней
   ENUM_MA_METHOD    m_method_fast;    // тип сглаживания быстрой скользящей средней
   int              m_period_slow;   // период медленной скользящей средней
   ENUM_MA_METHOD    m_method_slow;    // тип сглаживания медленной скользящей средней

Конечно, вы можете создать свой собственный класс индикатора, который будет являться потомком CIndicator и реализовывать все необходимые методы для использования с Мастером MQL5. Но в данном случае мы хотим показать, как с помощью CiCustom можно использовать любой пользовательский индикатор в модуле торговых сигналов.

Вот как это выглядит в коде:

//+------------------------------------------------------------------+
//| Создает индикатор "Быстрая MA"                                   |
//+------------------------------------------------------------------+
bool MA_Cross::CreateFastMA(CIndicators *indicators)
  {
//--- проверка указателя
   if(indicators==NULL) return(false);
//--- добавление объекта в коллекцию
   if(!indicators.Add(GetPointer(m_fast_ma)))
     {
      printf(__FUNCTION__+": ошибка добавления объекта быстрой MA");
      return(false);
     }
//--- задание параметров быстрой MA
   MqlParam parameters[4];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="Examples\\Custom Moving Average.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_period_fast;      // период
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=0;                  // смещение
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_method_fast;      // метод усреднения
//--- инициализация объекта  
   if(!m_fast_ma.Create(m_symbol.Name(),m_period,IND_CUSTOM,4,parameters))
     {
      printf(__FUNCTION__+": ошибка инициализации объекта быстрой MA");
      return(false);
     }
//--- количество буферов
   if(!m_fast_ma.NumBuffers(1)) return(false);
//--- дошли до этого места, значит, функция выполнена успешно - вернем true
   return(true);
  }

В методе CreateFastMA() сначала проверяем указатель коллекции индикаторов, и затем добавляем в эту коллекцию указатель быстрой средней m_fast_ma. Затем объявлем структуру MqlParam, которая разработана специально для хранения параметров пользовательского индикатора, и заполняем её значениями.

В качестве пользовательского индикатора скользящей средней использован Custom Moving Average из стандартной поставки терминала. Имя индикатора обязательно указывается относительно папки каталог_данных/MQL5/Indicators/. Так как пользовательский индикатор 'Custom Moving Average.mq5' в стандартной поставке расположен в папке каталог_данных/MQL5/Indicators/Examples/, то путь к нему мы указываем вместе с папкой Examples:

parameters[0].string_value="Examples\\Custom Moving Average.ex5";

Если заглянуть в код этого индикатора, то можно увидеть все необходимые данные:

//--- input parameters
input int            InpMAPeriod=13;       // Period
input int            InpMAShift=0;         // Shift
input ENUM_MA_METHOD InpMAMethod=MODE_SMMA;  // Method

Значения структуры содержат попарно тип-значение:

  1. тип параметра - string (для передачи имени индикатора);
  2. имя исполняемого файла пользовательского индикатора - "Custom Moving Averages.exe";
  3. тип параметра - int (значение периода);
  4. период скользящей средней;
  5. тип параметра - int (значение смещения);
  6. смещение средней в барах по горизонтали;
  7. тип параметра - int (значение перечисления является целочисленным);
  8. метод усреднения.

После заполнения структуры производится инициализация индикатора методом Create() всех необходимых параметров: имя символа и таймфрейм на которых он рассчитывается, тип индикатора из перечисления ENUM_INDICATOR, количество параметров индикатора и сама структура MqlParam со значениями параметров. Последним идет указание индикатору количества индикаторных буферов в нем с помощью метода NumBuffers().

Метод CreateSlowMA() для создания медленной скользящей средней выглядит аналогично. При использовании пользовательских индикаторов в модуле нужно не забывать, что сгенерированный Мастером MQL5 эксперт будет также запускаться и в тестере. Поэтому обязательно добавим в начале нашего файла свойство #property tester_indicator, указывающее тестеру на местонахождение необходимых для работы индикаторов:

#include "..\ExpertSignal.mqh"   // класс CExpertSignal находится в файле ExpertSignal
#property tester_indicator "Examples\\Custom Moving Average.ex5"

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

   //--- проверка корректности входных данных
   bool              ValidationSettings(void);
   //--- создание индикаторов и таймсерий для работы модуля сигналов
   bool              InitIndicators(CIndicators *indicators);
   //--- доступ к данным индикаторов
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }

Как видите, методы очень простые, в них использован метод GetData() родительского класса СIndicator, который возвращает значение из указанного индикаторного буфера на указанной позиции.

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


6. Определяем методы LongCondition и ShortCondition

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

  • LongCondition() - проверяет выполнения условия на покупку и возвращает силу сигнала Long от 0 до 100;
  • ShortCondition() - проверяет выполнения условия на продажу и возвращает силу сигнала Short от 0 до 100.

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

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

   ...
   bool              InitIndicators(CIndicators *indicators);
   //--- доступ к данным индикаторов
   double            FastMA(const int index)             const { return(m_fast_ma.GetData(0,index)); }
   double            SlowMA(const int index)             const { return(m_slow_ma.GetData(0,index)); }
   //--- проверка условий на покупку и продажу
   virtual int       LongCondition();
   virtual int       ShortCondition();

И создадим описание функций, вот как выглядит проверка сигнала на покупку (на продажу всё аналогично):

//+------------------------------------------------------------------+
//| Возвращает силу сигнала на покупку                               |
//+------------------------------------------------------------------+
int MA_Cross::LongCondition()
  {
   int signal=0;
//--- для режима работы по тикам idx=0, в режиме работы по сформировавшимся барам idx=1
   int idx=StartIndex();
//--- значения средних на последнем сформировавшемся баре
   double last_fast_value=FastMA(idx);
   double last_slow_value=SlowMA(idx);
//--- значения средних на предпоследнем сформировавшемся баре
   double prev_fast_value=FastMA(idx+1);
   double prev_slow_value=SlowMA(idx+1);
//--- если быстрая скользящая пробила снизу вверх медленную на последних двух закрытых барах
   if((last_fast_value>last_slow_value) && (prev_fast_value<prev_slow_value))
     {
      signal=100; // сигнал на покупку есть
     }
//--- вернем значение сигнала
   return(signal);
  }

Обратите внимание, что объявлена переменная idx, которой присваивается значение_ возвращаемое функцией StartIndex() родительского класса CExpertBase. Функция StartIndex() возвращает 0, если советник предназначен для работы на всех тиках, и в этом случае анализ начинается с текущего бара. Если советник предназначен для работы по ценам открытия, функция StartIndex() возвращает 1 и анализ начинается с последнего сформировавшегося бара.

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

Как устанавливать этот режим и для чего он может понадобиться будет рассказано чуть ниже в последнем штрихе.

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


Проверка эксперта в тестере

Для проверки работоспособности нашего модуля сгенерируем теперь с помощью Мастера MQL5 эксперта на его основе и запустим на графике. На появившемся окне запуска во "Входных параметрах" мы увидим параметры модуля MA_Cross.

Все остальные параметры также добавил Мастер MQL5 при генерации советника на основе выбранных  модуля управления капиталом и модуля сопровождения позиций (Trailing Stop). Таким образом, мы сосредоточились только на написании модуля торговых сигналов и получили готовое решение. В этом и есть главное достоинство использования  Мастера MQL5! 

Осталось проверить работу торгового робота в тестере стратегий MetaTrader 5, для этого попробуем провести быструю оптимизацию основных параметров.

При данных настройках входных параметров требуется более полумиллиона проходов для проведения полной оптимизации. Поэтому выберем быструю оптимизацию (генетический алгоритм) и задействуем дополнительно MQL5 Cloud Network для ускорения. За 10 минут оптимизация завершена и мы получили результаты.


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


Последний штрих

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

Если вы откроете сгенерированный Мастером MQL5 исходный код советника, то найдете там глобальную переменную Expert_EveryTick со значением false. Именно на основании этой переменной возвращает своё значение функция StartIndex(), которая сообщает эксперту в каком режиме он должен работать.

//+------------------------------------------------------------------+
//|                                                 TestMA_Cross.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\MySignals\MA_Cross.mqh>
//--- available trailing
#include <Expert\Trailing\TrailingNone.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string         Expert_Title             ="TestMA_Cross";  // Document name
ulong               Expert_MagicNumber       =22655;          // Идентификатор эксперта
bool                  Expert_EveryTick             =false;          // Работа эксперта внутри бара
//--- inputs for main signal
input int            Signal_ThresholdOpen     =10;             // Signal threshold value to open [0...100]
input int            Signal_ThresholdClose    =10;             // Signal threshold value to close [0...100]

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

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

input bool          Expert_EveryTick         =false;          // Работа эксперта внутри бара

И сейчас настало время для подведения кратких итогов.


6 шагов по созданию модуля торговых сигналов

Если вы освоили MQL5, то вам уже не требуется писать эксперт полностью с нуля. Достаточно создать свой модуль торговых сигналов и на его основе автоматически сгенерировать торговый робот с подключенными модулями трейлинга и управления объемами сделок. И даже если вы не сильны в ООП или не хотите сильно вникать в то, как устроены торговые классы, то вам достаточно пройти 6 шагов:

  1. Создать новый класс с помощью Мастера MQL5 в отдельной папке MQL5/Include/MySignals/. В нем будет наш модуль торговых сигналов.
  2. Составить дескриптор модуля в котором описаны параметры, их тип и значения по умолчанию.
  3. Объявить параметры модуля в самом классе и добавить методы для инициализации в конструкторе.
  4. Проверить входные параметры на корректность и не забыть вызвать метод ValidationSettings() базового класса CExpertSignal.
  5. Создать объекты-индикаторы и добавить предопределенный метод инициализации InitIndicators().
  6. Определить условия возникновения торговых сигналов в методах LongCondition() и ShortCondition().

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


От простого к сложному

Помните, что ваш торговый робот, собранный в Мастере MQL5, будет реализовывать настолько сложную торговую стратегию, насколько будет сложен модуль торговых сигналов в нем. Но прежде чем начинать строить комплексную торговую систему на множестве правил входа и выхода, разбейте её на несколько простых систем и проверьте каждую в отдельности.

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

Прикрепленные файлы |
testma_cross.mq5 (7.15 KB)
ma_cross.mqh (11.8 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (62)
BillionerClub
BillionerClub | 20 дек. 2018 в 14:40
Статья еще актуальна?  6 лет прошло, Метатрейдер получил столько обновлении. Хотелось бы попробывать, но в такое старье лезть имеет ли смысл.?
Rashid Umarov
Rashid Umarov | 20 дек. 2018 в 14:55
BillionerClub:
Статья еще актуальна?  6 лет прошло, Метатрейдер получил столько обновлении. Хотелось бы попробывать, но в такое старье лезть имеет ли смысл.?

В статье написано, как за 6 кликов (грубо) собрать робота. Вы не смогли осилить эти 6 шагов, чтобы проверить?

Тогда это не для вас

BillionerClub
BillionerClub | 20 дек. 2018 в 15:14
Rashid Umarov:

В статье написано, как за 6 кликов (грубо) собрать робота. Вы не смогли осилить эти 6 шагов, чтобы проверить?

Тогда это не для вас

Я так понял Вы автор этого 8 чуда метатрейдера. Очень профессионально все написано. Спасибо! за работу!. Осилим.

Buy_sell
Buy_sell | 16 нояб. 2020 в 09:46
Очень хорошая статья, спасибо авторам.
Nikita Gamolin
Nikita Gamolin | 7 янв. 2023 в 00:02
MetaQuotes:

Опубликована статья Создай торговый робот за 6 шагов!:


Автор: MetaQuotes

Коллеги, помогите.  Как в итоге из Модуля сигналов генерировать сигнал на закрытие через CheckCloseLong/Short? Не нашёл ответа в статье.
MetaTrader 5 - больше, чем можно представить! MetaTrader 5 - больше, чем можно представить!
Клиентский терминал MetaTrader 5 был написан полностью с нуля и, конечно же, выгодно отличается от своего предшественника. Новая торговая платформа предоставляет трейдерам практически безграничные возможности для торговли на любых рынках. При этом функционал продолжает расширяться, и преимущества MetaTrader 5 перечислить с ходу становится уже затруднительно даже экспертам, настолько их много. Мы попробовали кратко описать их в одной статье и сами удивились полученному результату - кратко не получилось!
Ядерная оценка неизвестной плотности вероятности Ядерная оценка неизвестной плотности вероятности
Статья посвящена созданию программного инструмента, позволяющего производить оценку неизвестной плотности вероятности. Для реализации был выбран метод ядерной оценки плотности (Kernel Density Estimation). Статья содержит исходные коды программной реализации данного метода, примеры его использования и иллюстрации.
OpenCL: от наивного кодирования - к более осмысленному OpenCL: от наивного кодирования - к более осмысленному
В данной статье продемонстрированы некоторые возможности оптимизации, открывающиеся при хотя бы поверхностном учете особенностей "железа", на котором исполняется кернел. Полученные цифры весьма далеки от предельных, но даже они показывают, что при том наборе возможностей, который имеется здесь и сейчас (OpenCL API в реализации разработчиков терминала не позволяет контролировать некоторые важные для оптимизации параметры - - в частности, размер локальной группы), выигрыш в производительности в сравнении с исполнением хостовой программы очень существенен.
OpenCL: Мост в параллельные миры OpenCL: Мост в параллельные миры
В конце января 2012 года компания-разработчик терминала MetaTrader 5 анонсировала нативную поддержку OpenCL в MQL5. В статье на конкретном примере изложены основы программирования на OpenCL в среде MQL5 и приведены несколько примеров "наивной" оптимизации программы по быстродействию.