English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Рецепты MQL5 - Использование индикаторов для формирования условий торговли в эксперте

Рецепты MQL5 - Использование индикаторов для формирования условий торговли в эксперте

MetaTrader 5Примеры | 14 мая 2013, 12:53
9 038 9
Anatoli Kazharski
Anatoli Kazharski

Введение

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

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

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

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

 

Процесс разработки эксперта

Поместим исходный код эксперта (*.mq5) из предыдущей статьи в отдельную папку TestIndicatorConditions. В этой же папке создайте папку Include. Именно в ней нужно будет создать подключаемые файлы (*.mqh). Файлы можно сгенерировать с помощью Мастера MQL5 (Ctrl+N), либо создать вручную обычные текстовые файлы (*.txt) в нужной директории, а затем переименовать их расширение в *.mqh.

Ниже перечислены названия всех созданных подключаемых файлов и пояснения к ним:

  • Enums.mqh - в этом файле будут расположены все перечисления;
  • InfoPanel.mqh - здесь будут функции для установки информационной панели, создания графических объектов и их удаления;
  • Errors.mqh - сюда перенесем все функции, которые возвращают коды ошибок и причины деинициализации;
  • TradeSignals.mqh - здесь будут сосредоточены функции, которые заполняют массивы ценами, значениями индикаторов и блок сигналов;
  • TradeFunctions.mqh - этот файл будет содержать в себе торговые функции;
  • ToString.mqh - здесь будут функции, которые конвертируют числовые значения в строковые;
  • Auxiliary.mqh - здесь будут прочие вспомогательные функции.

Чтобы подключить эти библиотеки к основному файлу нужно использовать директиву #include. Так как основной файл эксперта и папка подключаемых файлов (Include) находятся в одной общей папке, подключение файлов в коде будет выглядеть так:

//--- Подключаем свои библиотеки
#include "Include\Enums.mqh"
#include "Include\InfoPanel.mqh"
#include "Include\Errors.mqh"
#include "Include\TradeSignals.mqh"
#include "Include\TradeFunctions.mqh"
#include "Include\ToString.mqh"
#include "Include\Auxiliary.mqh"

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

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

//--- Связь с основным файлом эксперта
#include "..\TestIndicatorConditions.mq5"
//--- Подключаем свои библиотеки
#include "Enums.mqh"
#include "InfoPanel.mqh"
#include "Errors.mqh"
#include "TradeSignals.mqh"
#include "ToString.mqh"
#include "Auxiliary.mqh"

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

В файле Enums.mqh добавим перечисление для индикаторов. В качестве примера используем в этом эксперте два стандартных индикатора (Moving Average и Commodity Channel Index) и один пользовательский (MultiRange_PCH). Перечисление будет выглядеть так:

//--- Индикаторы
enum ENUM_INDICATORS
  {
   MA       = 0, // Moving Average
   CCI      = 1, // CCI
   PCH      = 2  // Price Channel
  };

Во внешние параметры внесем изменения, как это показано ниже:

//--- Внешние параметры эксперта
sinput   long              MagicNumber=777;        // Магический номер
sinput   int               Deviation=10;           // Проскальзывание
input    ENUM_INDICATORS   Indicator=MA;           // Индикатор
input    int               IndicatorPeriod=5;      // Период индикатора
input    int               IndicatorSegments=2;    // Кол-во однонаправленных отрезков индикатора
input    double            Lot=0.1;                // Лот
input    double            VolumeIncrease=0.1;     // Приращение объема позиции
input    double            VolumeIncreaseStep=10;  // Шаг для приращения объема
input    double            StopLoss=50;            // Стоп Лосс
input    double            TakeProfit=100;         // Тейк Профит
input    double            TrailingStop=10;        // Трейлинг Стоп
input    bool              Reverse=true;           // Разворот позиции
sinput   bool              ShowInfoPanel=true;     // Показ информационной панели

Как уже было написано выше, в выпадающем списке параметра Indicator можно будет выбрать один из трех индикаторов.

Для всех индикаторов есть только один параметр, в котором задается период индикатора IndicatorPeriod. Параметр NumberOfBars из предыдущего эксперта переименован в IndicatorSegments и теперь означает количество баров, в течение которых указанный индикатор должен быть направленным вверх/вниз, чтобы условие на открытие позиции исполнилось.

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

Ранее корректировка значения переменной AllowedNumberOfBars (теперь AllowedNumberOfSegments) производилась в пользовательской функции GetBarsData(). Теперь вынесем ее в отдельную функцию и будем вызывать ее только при инициализации.

Так как теперь проверка условия на открытие позиции будет производиться по значениям индикатора, то значение всегда будет присваиваться на два больше. То есть, если внешней переменной IndicatorSegments присвоено значение 1, то переменной AllowedNumberOfSegments будет присвоено значение 3, так как для выполнения условия (например, для BUY) необходимо, чтобы значение индикатора на сформировавшемся баре было больше, чем на предыдущем, а для этого нужно получить три последних значения индикатора.

Ниже код функции CorrectInputParameters():

//+------------------------------------------------------------------+
//| Корректирует входные параметры                                   |
//+------------------------------------------------------------------+
void CorrectInputParameters()
  {
//--- Скорректируем значение количества баров для условия открытия позиции
   if(AllowedNumberOfSegments<=0)
     {
      if(IndicatorSegments<=1)
         AllowedNumberOfSegments=3;                     // Нужно не менее трех баров
      if(IndicatorSegments>=5)
         AllowedNumberOfSegments=5;                     // и не более 7
      else
         AllowedNumberOfSegments=IndicatorSegments+1;   // и всегда на два больше
     }
  }

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

//+------------------------------------------------------------------+
//| Проверяет возможность вести торговлю                             |
//+------------------------------------------------------------------+
bool CheckTradingPermission()
  {
//--- Для режима реального времени
   if(IsRealtime())
     {
      //--- Проверка соединения с сервером
      if(!TerminalInfoInteger(TERMINAL_CONNECTED))
         return(1);
      //--- Разрешение на торговлю на уровне данной запущенной программы
      if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED))
         return(2);
      //--- Разрешение на торговлю на уровне терминала
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
         return(3);
      //--- Разрешение на торговлю для текущего счета
      if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED))
         return(4);
      //--- Разрешение на автоматическую торговлю для текущего счета
      if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
         return(5);
     }
//---
   return(0);
  }

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

Например, для индикатора Moving Average есть функция iMA(). Хэндлы всех стандартных индикаторов в терминале MetaTrader 5 можно получить с помощью таких функций. С полным списком можно ознакомиться в Справочнике MQL5 в разделе Технические индикаторы. Если же нужно получить хэндл пользовательского индикатора, необходимо использовать функцию iCustom().

Реализуем функцию GetIndicatorHandle(), в которой в зависимости от того, какой индикатор был выбран во внешнем параметре Indicator, глобальной переменной indicator_handle будет присваиваться значение хэндла соответствующего индикатора. Код функции находится в нашей библиотеке функций торговых сигналов (файл \Include\TradeSignals.mqh), а переменная с хэндлом индикатора - в основном файле эксперта.

//+------------------------------------------------------------------+
//| Получает хэндл индикатора                                        |
//+------------------------------------------------------------------+
void GetIndicatorHandle()
  {
//--- Если выбран индикатор Moving Average
   if(Indicator==MA)
      indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE);
//--- Если выбран индикатор CCI
   if(Indicator==CCI)
      indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE);
//--- Если выбран индикатор MultiRange_PCH
   if(Indicator==PCH)
      indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod);
//--- Если не удалось получить хэндл индикатора
   if(indicator_handle==INVALID_HANDLE)
      Print("Не удалось получить хэндл индикатора!");
  }

Далее мы создадим функцию GetDataIndicators(), в которой с помощью полученных хэндлов индикаторов можно получить их значения. Выполняется это с помощью функции CopyBuffer() аналогично получению значения баров с помощью функций CopyTime(), CopyClose(), CopyOpen(), CopyHigh() и CopyLow(), которые рассматривались в статье Рецепты по MQL5 - Изучение свойств позиции в тестере MetaTrader 5.

Так как у индикатора может быть несколько буферов (рядов значений), то в качестве второго параметра в функцию CopyBuffer() нужно передается номер буфера. Номера буферов для стандартных индикаторов можно узнать в Справке. В пользовательских индикаторах, если есть исходный код, номера буферов можно посмотреть в коде. Если кода нет, то придется подобрать номер экспериментальным путем, наблюдая, как исполняются условия в тестере стратегий в режиме визуализации.

Предварительно в главном файле советника создадим динамические массивы для значений индикаторных буферов:

//--- Массивы для значений индикатора
double indicator_buffer1[];
double indicator_buffer2[];

Код функции GetIndicatorsData() представлен ниже:

//+------------------------------------------------------------------+
//| Получает значения индикаторов                                    |
//+------------------------------------------------------------------+
bool GetIndicatorsData()
  {
//--- Если хэндл индикатора был получен
   if(indicator_handle!=INVALID_HANDLE)
     {
      //--- Для индикаторов Moving Average или CCI
      if(Indicator==MA || Indicator==CCI)
        {
         //--- Установим обратный порядок индексации (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         //--- Получим значения индикатора
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments)
           {
            Print("Не удалось скопировать значения ("+
                  _Symbol+"; "+TimeframeToString(Period())+") в массив indicator_buffer1! Ошибка ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //--- Для индикатора MultiRange_PCH
      if(Indicator==PCH)
        {
         //--- Установим обратный порядок индексации (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         ArraySetAsSeries(indicator_buffer2,true);
         //--- Получим значения индикатора
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || 
            CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments)
           {
            Print("Не удалось скопировать значения ("+
                  _Symbol+"; "+TimeframeToString(Period())+") в массивы indicator_buffer1 или indicator_buffer2! Ошибка ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //---
      return(true);
     }
//--- Если хэндл индикатора не получен, попробуем получить его еще раз
   else
      GetIndicatorHandle();
//---
   return(false);
  }

Функция GetTradingSignal() весьма заметно изменилась. В момент отсутствия позиции и в момент, когда позиция есть, условия отличаются. Для индикаторов Moving Average и CCI условия одинаковые. А для MultiRange_PCH сделан отдельный блок. Чтобы сделать код удобочитаемым и исключить повторы, написана вспомогательная функция GetSignal(), которая возвращает сигнал на открытие позиции или на ее разворот, если она существует и это разрешено внешним параметром.

Ниже представлен код функции GetSignal():

//+------------------------------------------------------------------+
//| Проверяет условие и возвращает сигнал                            |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetSignal()
  {
//--- Проверка условий для индикаторов Moving Average и CCI
   if(Indicator==MA || Indicator==CCI)
     {
      //--- Сигнал на продажу
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]<indicator_buffer1[2])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5] && 
         indicator_buffer1[5]<indicator_buffer1[6])
         return(ORDER_TYPE_SELL);

      //--- Сигнал на покупку
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]>indicator_buffer1[2])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5] && 
         indicator_buffer1[5]>indicator_buffer1[6])
         return(ORDER_TYPE_BUY);
     }
//--- Блок для проверки условий для индикатора MultiRange_PCH
   if(Indicator==PCH)
     {
      //--- Сигнал на продажу
      if(close_price[1]<indicator_buffer2[1] && 
         open_price[1]>indicator_buffer2[1])
         return(ORDER_TYPE_SELL);
      //--- Сигнал на покупку
      if(close_price[1]>indicator_buffer1[1] && 
         open_price[1]<indicator_buffer1[1])
         return(ORDER_TYPE_BUY);
     }
//--- Отсутствие сигнала
   return(WRONG_VALUE);
  }

Код функции GetTradingSignal() выглядит теперь так:

//+------------------------------------------------------------------+
//| Определяет торговые сигналы                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- Если позиции нет
   if(!pos.exists)
     {
      //--- Сигнал на продажу
      if(GetSignal()==ORDER_TYPE_SELL)
         return(ORDER_TYPE_SELL);
      //--- Сигнал на покупку
      if(GetSignal()==ORDER_TYPE_BUY)
         return(ORDER_TYPE_BUY);
     }
//--- Если позиция есть
   if(pos.exists)
     {
      //--- Получим тип позиции
      GetPositionProperties(P_TYPE);
      //--- Получим цену последней сделки
      GetPositionProperties(P_PRICE_LAST_DEAL);
      //--- Блок для проверки условий для индикаторов Moving Average и CCI
      if(Indicator==MA || Indicator==CCI)
        {
         //--- Сигнал на продажу
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_SELL)
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_SELL && 
            close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_SELL);
         //--- Сигнал на покупку
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_BUY)
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
      //--- Блок для проверки условий для индикатора MultiRange_PCH
      if(Indicator==PCH)
        {
         //--- Сигнал на продажу
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]<indicator_buffer2[1] && 
            open_price[1]>indicator_buffer2[1])
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 
            close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_SELL);
         //--- Сигнал на покупку
         if(pos.type==POSITION_TYPE_SELL && 
            close_price[1]>indicator_buffer1[1] && 
            open_price[1]<indicator_buffer1[1])
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
     }
//--- Отсутствие сигнала
   return(WRONG_VALUE);
  }

Теперь нам осталось лишь разобраться с режимами Instant Execution и Market Execution, в которые относятся к свойствам символа, и соответственным образом дополнить код функции открытия позиции OpenPosition(). В справке можно прочитать такое объяснение.

  • Instant Execution - торговля по потоковым ценам.
  • Market Execution - исполнение ордеров по рынку.

Напомню, для режима исполнения Market Execution открыть позицию сразу с установленными уровнями Stop Loss и Take Profit не получится: нужно сначала открыть позицию, а потом уже модифицировать ее, установив эти уровни.

Начиная с 803 билда, для типов исполнения Market Execution и Exchange Execution при открытии позиции можно устанавливать уровни Stop Loss и Take Profit.

В структуру свойств символа добавим режим заключения сделок:

//--- Свойства символа
struct symbol_properties
  {
   int               digits;           // Количество знаков в цене после запятой
   int               spread;           // Размер спреда в пунктах
   int               stops_level;      // Ограничитель установки Stop ордеров
   double            point;            // Значение одного пункта
   double            ask;              // Цена ask
   double            bid;              // Цена bid
   double            volume_min;       // Минимальный объем для заключения сделки
   double            volume_max;       // Максимальный объем для заключения сделки
   double            volume_limit;     // Максимально допустимый объем для позиции и ордеров в одном направлении
   double            volume_step;      // Минимальный шаг изменения объема для заключения сделки
   double            offset;           // Отступ от максимально возможной цены для операции
   double            up_level;         // Цена верхнего уровня stop level
   double            down_level;       // Цена нижнего уровня stop level
   ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Режим заключения сделок
  };

Соответствующим образом необходимо дополнить перечисление ENUM_SYMBOL_PROPERTIES

//--- Перечисление свойств символа
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS          = 0,
   S_SPREAD          = 1,
   S_STOPSLEVEL      = 2,
   S_POINT           = 3,
   S_ASK             = 4,
   S_BID             = 5,
   S_VOLUME_MIN      = 6,
   S_VOLUME_MAX      = 7,
   S_VOLUME_LIMIT    = 8,
   S_VOLUME_STEP     = 9,
   S_FILTER          = 10,
   S_UP_LEVEL        = 11,
   S_DOWN_LEVEL      = 12,
   S_EXECUTION_MODE  = 13,
   S_ALL             = 14
  };

и функцию GetSymbolProperties():

case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);   break;
      //---
      case S_ALL           :
         symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits);
         symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits);
         symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits);
         symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits);
         symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits);
         symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);                       break;
         //---

В итоге код функции OpenPosition() теперь выглядит вот так:

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Установим номер мэджика в торговую структуру
   trade.SetExpertMagicNumber(MagicNumber);
//--- Установим размер проскальзывания в пунктах
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- Режим Instant Execution
//    Позицию можно открыть сразу с установленными Stop Loss и Take Profit
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      //--- Если позиция не открылась, вывести сообщение об этом
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
         Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
     }
//--- Режим Market Execution 
//    Сначала нужно открыть позицию и только после этого можно установить Stop Loss и Take Profit
//    *** Начиная с 803 билда, уровни Stop Loss и Take Profit можно устанавливать при открытии позиции ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- Если позиции нет, то сначала откроем позицию, а затем установим Stop Loss и Take Profit
      if(!pos.exists)
        {
         //--- Если позиция не открылась, вывести сообщение об этом
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment))
            Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
         //--- Получим флаг наличия/отсутствия позиции
         pos.exists=PositionSelect(_Symbol);
         //--- Если есть позиция
         if(pos.exists)
           {
            //--- Установим Stop Loss и Take Profit
            if(!trade.PositionModify(_Symbol,sl,tp))
               Print("Ошибка при модификации позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
      //--- Если позиция есть, то увеличим ее объем и оставим прежнее значение уровней Stop Loss и Take Profit
      else
        {
         //--- Если позиция не открылась, вывести сообщение об этом
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
            Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
  }

Нам осталось внести финальные и очень важные штрихи в функции обработки событий:

  • OnInit
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- Скорректируем входные параметры
       CorrectInputParameters();
    //--- Получим хэндлы индикаторов
       GetIndicatorHandle();
    //--- Инициализируем новый бар
       CheckNewBar();
    //--- Получим свойства
       GetPositionProperties(P_ALL);
    //--- Установим информационную панель
       SetInfoPanel();
    //---
       return(0);
      }
  • OnDeinit
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- Вывести в журнал причину деинициализации
       Print(GetDeinitReasonText(reason));
    //--- При удалении с графика
       if(reason==REASON_REMOVE)
         {
          //--- Удалить все объекты с графика, которые относятся к информационной панели
          DeleteInfoPanel();
          //--- Удалим хэндл индикатора
          IndicatorRelease(indicator_handle);
         }
      }
  • OnTick
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
    //--- Если бар не новый, выходим
       if(!CheckNewBar())
         {
          if(IsVisualMode() || IsRealtime())
            {
             //--- Получим свойства и обновим значения на панели
             GetPositionProperties(P_ALL);
             //--- Установим/обновим информационную панель
             SetInfoPanel();
            }
          return;
         }
    
    //--- Если есть новый бар
       else
         {
          //--- Если не запрещено торговать
          if(CheckTradingPermission()==0)
            {
             if(!GetIndicatorsData())
                return;
             GetBarsData();          // Получим данные баров
             TradingBlock();         // Проверим условия и торгуем
             ModifyTrailingStop();   // Изменим уровень Trailing Stop
            }
         }
    //--- Получим свойства
       GetPositionProperties(P_ALL);
    //--- Обновим информационную панель
       SetInfoPanel();
      }

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

 

Оптимизация параметров и тестирование эксперта.

Настройки тестера нужно установить так, как показано на рисунке ниже:

Рис. 1. Настройки тестера

Рис. 1. Настройки тестера.

Далее настроим параметры эксперта для оптимизации (см. также *.set файл с настройками в приложении к статье):

Рис. 2. Настройки эксперта

Рис. 2. Настройки эксперта.

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

Рис. 3. График оптимизации

Рис. 3. График оптимизации.

Результат по максимальному значению фактора восстановления получился вот таким:

Рис. 4. Результат теста по максимальному значению фактора восстановления

Рис. 4. Результат теста по максимальному значению фактора восстановления.

 

Заключение

В приложении к статье вы можно скачать архив с исходными кодами эксперта. Архив нужно распаковать и поместить папку \TestIndicatorConditions с файлами в директорию <терминал Metatrader 5>\MQL5\Experts. Также для правильной работы эксперта нужно скачать индикатор MultiRange_PCH. Его нужно поместить в директорию <терминал Metatrader 5>\MQL5\Indicators.

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (9)
---
--- | 15 мая 2013 в 13:46
denkir:

Я так понимаю, пункт 2) может настраиваться брокером... может есть смысл это указывать в Документации?

указывать в документации что "пункт 2) может настраиваться брокером" ?

Denis Kirichenko
Denis Kirichenko | 15 мая 2013 в 13:56
sergeev:

указывать в документации что "пункт 2) может настраиваться брокером" ?

Угу. Почему так... а потому что заметил, что по однотипным счетам (фикс-спред, исполнение FOK) у разных брокеров функция

SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);

может вернуть разные значения...

---
--- | 15 мая 2013 в 14:00
denkir:

Угу.

неясно только зачем.

ведь по сути фразу  "может настраиваться брокером" нужно писать на каждой странице документации по торговле :)

но то, что вы спрашиваете - это относится не к брокеру, а к символу.

один символ - маркет исполнение, второй - биржевой.

у них разные условия, разные типы заливок и много чего разного

для этого и даны все функции опроса состояния торгового окружения.

Denis Kirichenko
Denis Kirichenko | 18 мая 2013 в 20:50

sergeev:

...но то, что вы спрашиваете - это относится не к брокеру, а к символу.

один символ - маркет исполнение, второй - биржевой.

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


sergeev, Вы не не обратили внимание, что я написал ранее:

...а потому что заметил, что по однотипным счетам (фикс-спред, исполнение FOK) у разных брокеров...


Набросал простенький скрипт. В нём получил у разных брокеров по символу EURUSD такую инфу:


Брокер "А":

NQ    0    22:39:37    test_symbol (EURUSD,H1)    Число знаков после запятой: 4
GI    0    22:39:37    test_symbol (EURUSD,H1)    Плавающий спред: 1
KQ    0    22:39:37    test_symbol (EURUSD,H1)    Режим заключения сделок: SYMBOL_TRADE_EXECUTION_INSTANT
OK    0    22:39:37    test_symbol (EURUSD,H1)    Режим заливки оредеров: 1
DS    0    22:39:37    test_symbol (EURUSD,H1)    Путь в дереве символов: Forex\EURUSD


Брокер "B":

ON    0    22:45:00    test_symbol (EURUSD,H1)    Число знаков после запятой: 4
EQ    0    22:45:00    test_symbol (EURUSD,H1)    Плавающий спред: 0
RN    0    22:45:00    test_symbol (EURUSD,H1)    Режим заключения сделок: SYMBOL_TRADE_EXECUTION_INSTANT
LS    0    22:45:00    test_symbol (EURUSD,H1)    Режим заливки оредеров: 3
OK    0    22:45:00    test_symbol (EURUSD,H1)    Путь в дереве символов: Forex-Fix\EURUSD


Можно заметить, что при одном и том же режиме заключения сделок есть разный режим заливки ордеров. Да режимы спредов отличаются (что я упустил, заявляя, что у брокеров однотипные счета)... вопрос, режимы (заключения сделок и заливки оредеров) как-то связаны?

//+------------------------------------------------------------------+
//|                                                  test_symbol.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

#define SymInt SymbolInfoInteger 
#define SymS SymbolInfoString 

string _symbol="EURUSD";
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 1) SymbolInfoInteger()
   long symb_digits=SymInt(_symbol,SYMBOL_DIGITS);
   PrintFormat("Число знаков после запятой: %u",symb_digits);
   bool symb_spread_float=SymInt(_symbol,SYMBOL_SPREAD_FLOAT);
   PrintFormat("Плавающий спред: %d",symb_spread_float);
   string trade_mode=EnumToString((ENUM_SYMBOL_TRADE_EXECUTION)SymInt(_symbol,SYMBOL_TRADE_EXEMODE));
   PrintFormat("Режим заключения сделок: %s",trade_mode);
   long fil_mode=SymInt(_symbol,SYMBOL_FILLING_MODE);
   PrintFormat("Режим заливки оредеров: %u",fil_mode);
//--- 2) SymbolInfoString()
   string path=SymS(_symbol,SYMBOL_PATH);
   PrintFormat("Путь в дереве символов: %s",path);
  }
//+------------------------------------------------------------------+
---
--- | 18 мая 2013 в 21:30
denkir:

вопрос, режимы (заключения сделок и заливки оредеров) как-то связаны?

нет, конечно.

и с режимом "заключения сделок"  также не связано время экспирации, и способ расчета прибыли.

Рецепты MQL5 - Разработка схемы для торговой системы типа "Три экрана Элдера" Рецепты MQL5 - Разработка схемы для торговой системы типа "Три экрана Элдера"
В этой статье мы разработаем схему для торговой системы типа "Три экрана Элдера" на MQL5. Писать эксперта будем не с нуля, а просто модифицируем уже практически готовую под эту схему программу из предыдущей статьи "Рецепты MQL5 - Использование индикаторов для формирования условий торговли в эксперте". То есть, целью статьи будет также показать, как можно легко модифицировать схемы уже готовых программ.
Рецепты MQL5 - История сделок и библиотека функций для получения свойств позиции Рецепты MQL5 - История сделок и библиотека функций для получения свойств позиции
Пришло время подвести краткий итог по материалам предыдущих статей о свойствах позиции. В этой статье мы создадим несколько дополнительных функций для получения тех свойств, которые можно получить только после обращения к истории сделок. Мы также познакомимся со структурами данных, что сделает доступ к свойствам позиции и символа еще удобнее.
Рецепты MQL5 - Мультивалютный эксперт: пример простой, точной и быстрой схемы Рецепты MQL5 - Мультивалютный эксперт: пример простой, точной и быстрой схемы
В этой статье мы рассмотрим реализацию простой схемы для мультивалютного эксперта. В данном случае имеется в виду, что эксперт можно будет настроить на тестирование/торговлю по одинаковым условиям, но с разными параметрами для каждого символа. В качестве примера создадим схему для двух символов, но сделаем это так, чтобы при необходимости можно было добавлять дополнительные символы, внося небольшие изменения в код.
Три аспекта ручного автотрейдинга. Часть 1 - Торговля Три аспекта ручного автотрейдинга. Часть 1 - Торговля
Эта статья открывает цикл статей по вопросам автоматизации ручного трейдинга на платформе МetaТrader 4. Каждая из них будет посвящена одному отдельному аспекту ручного автотрейдинга: автоматизация ручной торговли, автоматизация отображения текущего состояния торговли и автоматизация формирования отчетов о результатах торговли. В этой статье я расскажу об одной интересной технике для написания советников, управляемых трейдером вручную.