
Рецепты MQL5 - Использование индикаторов для формирования условий торговли в эксперте
Введение
В этой статье мы подключим к эксперту индикаторы, по значениям которых будут проверяться условия на открытие позиции. Чтобы было интересней, сделаем во внешних параметрах выпадающий список, в котором можно будет выбрать один из трех индикаторов для торговли.
На всякий случай напомню: мы продолжаем модифицировать эксперта, над которым до этого работали на протяжении всех последних статей по программированию на 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 не получится: нужно сначала открыть позицию, а потом уже модифицировать ее, установив эти уровни.
В структуру свойств символа добавим режим заключения сделок:
//--- Свойства символа 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. Настройки тестера.
Далее настроим параметры эксперта для оптимизации (см. также *.set файл с настройками в приложении к статье):
Рис. 2. Настройки эксперта.
На двухъядерном процессоре оптимизация длилась приблизительно 40 минут. На графике оптимизации можно частично оценить качество торговой системы по количеству результатов в прибыльной зоне:
Рис. 3. График оптимизации.
Результат по максимальному значению фактора восстановления получился вот таким:
Рис. 4. Результат теста по максимальному значению фактора восстановления.
Заключение
В приложении к статье вы можно скачать архив с исходными кодами эксперта. Архив нужно распаковать и поместить папку \TestIndicatorConditions с файлами в директорию <терминал Metatrader 5>\MQL5\Experts. Также для правильной работы эксперта нужно скачать индикатор MultiRange_PCH. Его нужно поместить в директорию <терминал Metatrader 5>\MQL5\Indicators.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Я так понимаю, пункт 2) может настраиваться брокером... может есть смысл это указывать в Документации?
указывать в документации что "пункт 2) может настраиваться брокером" ?
указывать в документации что "пункт 2) может настраиваться брокером" ?
Угу. Почему так... а потому что заметил, что по однотипным счетам (фикс-спред, исполнение FOK) у разных брокеров функция
может вернуть разные значения...
Угу.
неясно только зачем.
ведь по сути фразу "может настраиваться брокером" нужно писать на каждой странице документации по торговле :)
но то, что вы спрашиваете - это относится не к брокеру, а к символу.
один символ - маркет исполнение, второй - биржевой.
у них разные условия, разные типы заливок и много чего разного
для этого и даны все функции опроса состояния торгового окружения.
sergeev:
...но то, что вы спрашиваете - это относится не к брокеру, а к символу.
один символ - маркет исполнение, второй - биржевой.
у них разные условия, разные типы заливок и много чего разного...
sergeev, Вы не не обратили внимание, что я написал ранее:
Набросал простенький скрипт. В нём получил у разных брокеров по символу EURUSD такую инфу:
Брокер "А":
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":
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
Можно заметить, что при одном и том же режиме заключения сделок есть разный режим заливки ордеров. Да режимы спредов отличаются (что я упустил, заявляя, что у брокеров однотипные счета)... вопрос, режимы (заключения сделок и заливки оредеров) как-то связаны?
вопрос, режимы (заключения сделок и заливки оредеров) как-то связаны?
нет, конечно.
и с режимом "заключения сделок" также не связано время экспирации, и способ расчета прибыли.