Задавать символ индикатора можно двумя способами: задать символ вручную или использовать символ на котором работает советник. Если разрешить пользователю задавать символ вручную, тогда возникает необходимость проверки корректности введённого имени символа. Алгоритм проверки, как правило, следующий:
- проверить, может символ уже выбран в окне в окне MarketWatch (Обзор рынка)
- если ещё не выбран, то явно выбрать его
Лично мне нравиться использовать для этих целей торговый класс CSymbolInfo и его метод Name (Получает/устанавливает наименование финансового инструмента). Этот метод в коде прописывается одной строкой (здесь "m_symbol" - объект класса CSymbolInfo)
if(!m_symbol.Name(задаваемый символ)) // sets symbol name return(INIT_FAILED);
, а выполняет несколько важных проверок:
//+------------------------------------------------------------------+ //| Set name | //+------------------------------------------------------------------+ bool CSymbolInfo::Name(const string name) { m_name=name; //--- if(!CheckMarketWatch()) return(false); //--- if(!Refresh()) { m_name=""; Print(__FUNCTION__+": invalid data of symbol '"+name+"'"); return(false); } //--- succeed return(true); }
Проверка CSymbolInfo::Name -> CSymbolInfo::CheckMarketWatch
//+------------------------------------------------------------------+ //| Checks if symbol is selected in the MarketWatch | //| and adds symbol to the MarketWatch, if necessary | //+------------------------------------------------------------------+ bool CSymbolInfo::CheckMarketWatch(void) { //--- check if symbol is selected in the MarketWatch if(!Select()) { if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOL) { printf(__FUNCTION__+": Unknown symbol '%s'",m_name); return(false); } if(!Select(true)) { printf(__FUNCTION__+": Error adding symbol %d",GetLastError()); return(false); } } //--- succeed return(true); }
Проверка CSymbolInfo::CheckMarketWatch -> CSymbolInfo::Select()
//+------------------------------------------------------------------+ //| Get the property value "SYMBOL_SELECT" | //+------------------------------------------------------------------+ bool CSymbolInfo::Select(void) const { return((bool)SymbolInfoInteger(m_name,SYMBOL_SELECT)); }
возвращает свойство SYMBOL_SELECT (Признак того, что символ выбран в Market Watch (Обзор рынка)). "SYMBOL_SELECT" вернёт "true" если символ уже есть в окне Market Watch (Обзор рынка) и вернёт "false" если символа там нет.
Если "Проверка CSymbolInfo::CheckMarketWatch -> CSymbolInfo::Select()"
- вернула "true" делаем попытку обновить данные по символу (ВНИМАНИЕ! НЕ КОТИРОВКИ!) - выполняем CSymbolInfo::Name -> CSymbolInfo::Refresh().
- вернула "false" - анализируем почему было возвращено "false"
- если ошибка ERR_MARKET_UNKNOWN_SYMBOL - значит такого символа в принципе нет
- иначе пытаемся принудительно выбрать символ в Market Watch (Обзор рынка) CSymbolInfo::Select(true) - используя SymbolSelect
//+------------------------------------------------------------------+ //| Set the property value "SYMBOL_SELECT" | //+------------------------------------------------------------------+ bool CSymbolInfo::Select(const bool select) { return(SymbolSelect(m_name,select)); }
Таким образом сама SymbolSelect используется в самом конце алгоритма, а перед этим производится несколько необходимых проверок. При этом каждый этап распечатает описание ошибки, если такая будет. И самое главное - все проверки скрыты от пользователя, а в программе достаточно прописать проверку одной строкой (здесь "m_symbol" - объект класса CSymbolInfo)
if(!m_symbol.Name(задавамый символ))) // sets symbol name return(INIT_FAILED);
- 2.1. iADX (Average Directional Movement Index, ADX)
Задавать символ индикатора можно двумя способами: задать символ вручную или использовать символ на котором работает советник. Если разрешить пользователю задавать символ вручную (то есть во входных параметрах явно создать входной параметр "Символ индикатора", тогда возникает необходимость проверки корректности введённого имени символа. Алгоритм проверки, в таком случае, будет использоваться из #1 - при этом лучше создать для индикатора свой объект класса CSymbolInfo: в примере ниже это будет объект "m_symbol_ADX".
В примере даны два вида получения данных:
- за один запрос возврат значения индикатора на определённом бара
- за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)
Необходимые приготовления: так как этот пример в котором пользователю разрешено задавать символ индикатора вручную, в "шапке" советника прописываем подключение класса CSymbolInfo,
//+------------------------------------------------------------------+ //| iADX.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.002" #property description "An example of working with the indicator iADX (Average Directional Movement Index, ADX)" #property description "Receiving data from a certain bar or from several bars" //--- #include <Trade\SymbolInfo.mqh> CSymbolInfo m_symbol_ADX; // symbol info object //--- input parameters input string InpSymbol=""; // ADX: symbol name input ENUM_TIMEFRAMES InpPeriod=PERIOD_CURRENT; // ADX: timeframe input int InpAdxPeriod=14; // ADX: averaging period //--- int handle_iADX; // variable for storing the handle of the iADX indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+
также входные параметры и объявляем переменную "handle_iADX" в которой будет храниться хендл индикатора.
Следующий шаг - подготовительная работа в OnInit(): проверка корректности заданного символа (на основе #1) и непосредственное создание хендла индикатора iADX:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iADX string symbol=(InpSymbol=="")?Symbol():InpSymbol; if(!m_symbol_ADX.Name(symbol)) // sets symbol name return(INIT_FAILED); handle_iADX=iADX(m_symbol_ADX.Name(),InpPeriod,InpAdxPeriod); //--- if the handle is not created if(handle_iADX==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iADX indicator for the symbol %s/%s, error code %d", m_symbol_ADX.Name(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+
Забегая на перед: вот две функции получения данных индикатора:
"за один запрос возврат значения индикатора на определённом бара"
Входные параметры: индекс буфера "buffer" (могут принимать значения MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE) и номер бара "index" с которого нужно получить значение индикатора:
//+------------------------------------------------------------------+ //| Get value of buffers for the iADX | //| the buffer numbers are the following: | //| 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE | //+------------------------------------------------------------------+ double iADXGet(const int buffer,const int index) { double ADX[]; //--- reset error code ResetLastError(); //--- fill a part of the iADXBuffer array with values from the indicator buffer that has 0 index if(CopyBuffer(handle_iADX,buffer,index,1,ADX)<0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iADX indicator, error code %d",GetLastError()); //--- quit with zero result - it means that the indicator is considered as not calculated return(0.0); } return(ADX[0]); }
Возвращает значение индикатора на заданном номере бара или "0.0" в случае ошибки.
"за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)"
Когда может панодобиться такой метод: например нужно пройтись в цикле по нескольким значениям индикатора - в таком случае использовать метод "за один запрос возврат значения индикатора на определённом бара" будет дорого.
Входные параметры: индекс буфера "buffer" (могут принимать значения MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE), стартовая позиция "start_pos" - с какого номера бара начинать запрос данных, количество запрашиваемых данных "count" и массив "array" (передаваемый по ссылке) в который и будут складываться полученные данные:
//+------------------------------------------------------------------+ //| Get value of buffers for the iADX | //| the buffer numbers are the following: | //| 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE | //+------------------------------------------------------------------+ bool iADXGet(const int buffer,const int start_pos,const int count,double &array[]) { //--- reset error code ResetLastError(); //--- fill a part of the iADXBuffer array with values from the indicator buffer that has 0 index int copy_buffer=CopyBuffer(handle_iADX,buffer,start_pos,count,array); if(copy_buffer<0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iADX indicator, error code %d",GetLastError()); //--- quit with false result - it means that the indicator is considered as not calculated return(false); } else if(copy_buffer!=count) { //--- if it is copied less, than set PrintFormat("%d elements have been requested, only %d are copied",count,copy_buffer); //--- quit with zero result - it means that the indicator is considered as not calculated return(false); } //--- return(true); }
Эта функция уже имеет тип "bool" и возвращает в случае удачного копирования (когда количество полученных данных равно количеству запрошенных) "true".
Заключительный этап - работа в OnTick() и пример обращения за данными двумя способами
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int bar=0; string text="Request of data from one bar:\n"; StringConcatenate(text,text,"ADX(",InpAdxPeriod,") #",bar,": ",DoubleToString(iADXGet(MAIN_LINE,bar),2),"\n"); StringConcatenate(text,text,"+DI(",InpAdxPeriod,") #",bar,": ",DoubleToString(iADXGet(PLUSDI_LINE,bar),2),"\n"); StringConcatenate(text,text,"-DI(",InpAdxPeriod,") #",bar,": ",DoubleToString(iADXGet(MINUSDI_LINE,bar),2),"\n\n"); double array[]; int count=3; if(iADXGet(MAIN_LINE,0,count,array)) { ArraySetAsSeries(array,true); StringConcatenate(text,text,"Request of data from three bars:\n"); for(int i=0;i<3;i++) { StringConcatenate(text,text,"ADX(",InpAdxPeriod,") #",i,": ",DoubleToString(array[i],2),"\n"); } } Comment(text); } //+------------------------------------------------------------------+
Обратите внимание, что массиву "array", в который складываются данные с индикатора, устанавливается обратный порядок индексации - таки образом элемент массива array[0] на графике будет находится правее элемента array[n].
Проверка работы обоих методов получения данных с индикатора - мышка установлена на баре #0 и можно сравнить "Окно данных" и распечатанные на экране значения:
if(!m_symbol.Name(Symbol())) // sets symbol name return(INIT_FAILED);
Я конечно понимаю, что проверки это хорошо. Но мы ведь не проверяем на каждом тике - наличие связи с сервером, а может сразу после тика закончилась сессия, или такой символ бльше не доступен и пр.
Почему не использовать
if(!SymbolSelect(_Symbol, true) return(INIT_FAILED);
В чем принципиальная разница ? Я больше сторонник ООП, н зачем все усложнять и создавать объект там где и без него хорошо ?
Это похоже не пример:
double с = a+b;
или с = Sum(a,b);
Считаю что первый пример гораздо более наглядный и правильный чем второй. Хоть и не проводится проверок, что за значения принимает a и b.
Я конечно понимаю, что проверки это хорошо. Но мы ведь не проверяем на каждом тике - наличие связи с сервером, а может сразу после тика закончилась сессия, или такой символ бльше не доступен и пр.
Почему не использовать
В чем принципиальная разница ? Я больше сторонник ООП, н зачем все усложнять и создавать объект там где и без него хорошо ?
Я так понимаю, что SymbolSelect - это асинхронная операция, и возможно дорогая, - то есть ответ может прийти позже. А в самом объекте проходит несколько проверок, перед непосредственным вызовом SymbolSelect. Также отдельный объект сильно упрощает в дальнейшей работе получение данных и котировок - кода намного меньше, так вся работа скрыта от глаз пользователя.
- 2.1.1. Пример советника по индикатору iADX (Average Directional Movement Index, ADX)
Справка терминала так описывает торговлю по индикатору:
Технический индикатор Индекс Среднего Направления Движения (Average Directional Movement Index, ADX) помогает определить наличие ценовой тенденции. Он построен на подходах, описанных в книге "Новые концепции технических торговых систем" Уэллса Уайлдера.
Простейший метод торговли на основе системы направленного движения предполагает сравнение двух индикаторов направленности 14-периодного +DI и 14-периодного -DI. Для этого либо графики индикаторов наносятся один на другой, либо +DI вычитается из -DI. У. Уайлдер предлагает покупать, если +DI поднимается выше -DI, и продавать, когда +DI опускается ниже -DI.
Эти простые торговые правила У. Уайлдер дополняет также "правилом экстремальных точек"». Оно служит для устранения ложных сигналов и уменьшения числа заключаемых сделок. Согласно принципу экстремальных точек, в момент пересечения +DI и -DI необходимо отметить "экстремальную точку". Если +DI поднимается выше -DI, этой точкой является максимальная цена дня пересечения. Если +DI опускается ниже -DI, эта точка — минимальная цена дня пересечения.
Экстремальная точка затем используется как уровень вхождения в рынок. Так, после сигнала к покупке (+DI поднялся выше -DI) необходимо дождаться, когда цена поднимется выше экстремальной точки, и лишь после этого покупать. Если же цене не удается преодолеть уровень экстремальной точки, следует сохранять короткую позицию.
Для максимально возможного короткого кода в коде нет проверок:
- выставление отложенного ордера слишком близко к текущей цене или даже по неправильной цене
- никак не контролируется баланс торгового счёта и свободная маржа
Сам советник кроме нахождения пересечений должен ещё и помнить "экстремальные точки" - то есть цены (high или low) для бара на котором найдено пересечение.
В данной версии небольшое отступление от описанной в справке стратегии: полученный сигнал означает удаление отложенный ордеров по противоположному сигналу, а также закрытие позиций открытых по противоположному сигналу. То есть советник может иметь два состояния сигналов:
- оба вида сигналов отсутствуют (например перед первым запуском)
- присутствует только один из сигналов
- нет результатов проверки размещения отложенного ордера
Оба вида сигналов буду хранить в двух флагах:
bool point_extremum_high=false; // "false" - pending order BUY STOP is not placed bool point_extremum_low=false; // "false" - pending order SELL STOP is not placed
Сама идея поиска пересечения подразумевает обход в цикле нескольких значений индикаторных буферов - поэтому здесь будет использоваться метод "за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)" из iADX (Average Directional Movement Index, ADX). Пересечение ищется по самому простому алгоритму: сравниваются два значения - на текущем баре и на предыдущем.
Итак, "шапка" советника, в которой подключаются необходимые классы (CPositionInfo для работы с позициями, CTrade для размещения отложенных ордеров и COrderInfo для работы с отложенными ордерами) и объявляются объкты этиъ классов, прописывается входной параметр и объявляются флаги для сигналов и переменная для хранения хендла индикатора:
//+------------------------------------------------------------------+ //| ADX EA.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" #property description "Expert Advisor on the ADX indicator" //--- #include <Trade\PositionInfo.mqh> #include <Trade\Trade.mqh> #include <Trade\OrderInfo.mqh> CPositionInfo m_position; // trade position object CTrade m_trade; // trading object COrderInfo m_order; // pending orders object //--- input parameters input int InpAdxPeriod = 14; // ADX: averaging period //--- bool point_extremum_high=false; // "false" - pending order BUY STOP is not placed bool point_extremum_low=false; // "false" - pending order SELL STOP is not placed int handle_iADX; // variable for storing the handle of the iADX indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+
В OnInit() инициализирую флаги и получаю хендл индикатора:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { point_extremum_high=false; // "false" - pending order BUY STOP is not placed point_extremum_low=false; // "false" - pending order SELL STOP is not placed //--- handle_iADX=iADX(Symbol(),Period(),InpAdxPeriod); //--- if the handle is not created if(handle_iADX==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iADX indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+
Главная функция OnTick().
Работа идёт только в момент рождения нового бара:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- we work only at the time of the birth of new bar static datetime PrevBars=0; datetime time_0=iTime(0); if(time_0==PrevBars) return; PrevBars=time_0;
Далее объявляются два массива ("plus_di_array" и "minus_di_array") в которые будут копироваться данные индикатора с буферов PLUSDI_LINE и MINUSDI_LINE соответственно. При этом копирование данных производится с бара с индексом #1 и всего копируется 30 значений.
//+------------------------------------------------------------------+ //| Get value of buffers for the iADX | //| the buffer numbers are the following: | //| 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE | //+------------------------------------------------------------------+ int start_pos = 1; // start position for copying int count = 30; // count of copied bars double plus_di_array[]; double minus_di_array[]; ArraySetAsSeries(plus_di_array,true); ArraySetAsSeries(minus_di_array,true); if(!iADXGet(PLUSDI_LINE,start_pos,count,plus_di_array) || !iADXGet(MINUSDI_LINE,start_pos,count,minus_di_array)) return;
Данные в массивы копируются в bool функции iADXGet (функция описана в метод "за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)" из iADX (Average Directional Movement Index, ADX)).
Следующий шаг - копируем за один запрос исторические данные (OHLC, время открытия бара и другое) - также копирование данных производится с бара с индексом #1 и всего копируется 30 значений.
MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(Symbol(),0,start_pos,count,rates); if(copied!=count) return;
Когда все данные удачно скопированы начиется поиск пересечения - проверяем условие для размещения BUY STOP и для SELL STOP:
for(int i=0;i<count-1;i++) { if(!point_extremum_high) // "false" - pending order BUY STOP is not placed { if(plus_di_array[i]>minus_di_array[i] && plus_di_array[i+1]<minus_di_array[i+1]) { point_extremum_high=true; // "false" - pending order BUY STOP is not placed point_extremum_low=false; // "false" - pending order SELL STOP is not placed DebugBreak(); m_trade.BuyStop(1.0,rates[i].high,Symbol()); DeleteOrders(ORDER_TYPE_SELL_STOP); ClosePositions(POSITION_TYPE_SELL); break; } } else if(!point_extremum_low) // "false" - pending order SELL STOP is not placed { if(plus_di_array[i]<minus_di_array[i] && plus_di_array[i+1]>minus_di_array[i+1]) { point_extremum_high=false; // "false" - pending order BUY STOP is not placed point_extremum_low=true; // "false" - pending order SELL STOP is not placed DebugBreak(); m_trade.SellStop(1.0,rates[i].low,Symbol()); DeleteOrders(ORDER_TYPE_BUY_STOP); ClosePositions(POSITION_TYPE_BUY); break; } } }
Обратите внимание: при получении сигнала на размещение отложенного ордера BUY STOP удалем отложенный ордер SELL STOP и закрываем позицию SELL. Аналогично поступаем при получении чигнала SELL STOP.
Я так понимаю, что SymbolSelect - это асинхронная операция, и возможно дорогая, - то есть ответ может прийти позже. А в самом объекте проходит несколько проверок, перед непосредственным вызовом SymbolSelect. Также отдельный объект сильно упрощает в дальнейшей работе получение данных и котировок - кода намного меньше, так вся работа скрыта от глаз пользователя.
В справке про асинхронность молчок, а в классе проверяется двумя способами (это не тебе, а тем, кто не знает :)) Зачем тут впихнули обязательный вызов Refresh(), непонятно.
bool CSymbolInfo::Name(const string name) { m_name=name; //--- if(!CheckMarketWatch()) return(false); //--- if(!Refresh()) // а вот этот длиннющий метод вызывать явно лишнее { m_name=""; Print(__FUNCTION__+": invalid data of symbol '"+name+"'"); return(false); } //--- succeed return(true); } //+------------------------------------------------------------------+ //| Checks if symbol is selected in the MarketWatch | //| and adds symbol to the MarketWatch, if necessary | //+------------------------------------------------------------------+ bool CSymbolInfo::CheckMarketWatch(void) { //--- check if symbol is selected in the MarketWatch if(!Select()) { if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOL) { printf(__FUNCTION__+": Unknown symbol '%s'",m_name); return(false); } if(!Select(true)) { printf(__FUNCTION__+": Error adding symbol %d",GetLastError()); return(false); } } //--- succeed return(true); } bool CSymbolInfo::Select(void) const { return((bool)SymbolInfoInteger(m_name,SYMBOL_SELECT)); } //+------------------------------------------------------------------+ //| Set the property value "SYMBOL_SELECT" | //+------------------------------------------------------------------+ bool CSymbolInfo::Select(const bool select) { return(SymbolSelect(m_name,select)); } bool CSymbolInfo::Refresh(void) { long tmp=0; //--- if(!SymbolInfoDouble(m_name,SYMBOL_POINT,m_point)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE,m_tick_value)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT,m_tick_value_profit)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_VALUE_LOSS,m_tick_value_loss)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_TICK_SIZE,m_tick_size)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_TRADE_CONTRACT_SIZE,m_contract_size)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MIN,m_lots_min)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_MAX,m_lots_max)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_STEP,m_lots_step)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_VOLUME_LIMIT,m_lots_limit)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_LONG,m_swap_long)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_SWAP_SHORT,m_swap_short)) return(false); if(!SymbolInfoInteger(m_name,SYMBOL_DIGITS,tmp)) return(false); m_digits=(int)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_ORDER_MODE,tmp)) return(false); m_order_mode=(int)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_EXEMODE,tmp)) return(false); m_trade_execution=(ENUM_SYMBOL_TRADE_EXECUTION)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_CALC_MODE,tmp)) return(false); m_trade_calcmode=(ENUM_SYMBOL_CALC_MODE)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_TRADE_MODE,tmp)) return(false); m_trade_mode=(ENUM_SYMBOL_TRADE_MODE)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_MODE,tmp)) return(false); m_swap_mode=(ENUM_SYMBOL_SWAP_MODE)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_SWAP_ROLLOVER3DAYS,tmp)) return(false); m_swap3=(ENUM_DAY_OF_WEEK)tmp; if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_INITIAL,m_margin_initial)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_MAINTENANCE,m_margin_maintenance)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LONG,m_margin_long)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_SHORT,m_margin_short)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_LIMIT,m_margin_limit)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOP,m_margin_stop)) return(false); if(!SymbolInfoDouble(m_name,SYMBOL_MARGIN_STOPLIMIT,m_margin_stoplimit)) return(false); if(!SymbolInfoInteger(m_name,SYMBOL_EXPIRATION_MODE,tmp)) return(false); m_trade_time_flags=(int)tmp; if(!SymbolInfoInteger(m_name,SYMBOL_FILLING_MODE,tmp)) return(false); m_trade_fill_flags=(int)tmp; //--- succeed return(true); }
- 2.1.2. Очень простой пример работы с iADX (Average Directional Movement Index, ADX)
Весь код сразу:
//+------------------------------------------------------------------+ //| iADX very simple.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" #property description "A very simple example of working with the indicator iADX (Average Directional Movement Index, ADX)" #property description "Receiving data from a certain bar" //--- input parameters input int InpAdxPeriod=14; // ADX: averaging period //--- int handle_iADX; // variable for storing the handle of the iADX indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { handle_iADX=iADX(Symbol(),Period(),InpAdxPeriod); //--- if the handle is not created if(handle_iADX==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iADX indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //+------------------------------------------------------------------+ //| Get value of buffers for the iADX | //| the buffer numbers are the following: | //| 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE | //+------------------------------------------------------------------+ int buffer = MAIN_LINE; // ADX buffer numbers int start_pos = 0; // start position int count = 1; // amount to copy double array[]; // array to copy //--- reset error code ResetLastError(); //--- fill a part of the iADXBuffer array with values from the indicator buffer that has 0 index int copy_buffer=CopyBuffer(handle_iADX,buffer,start_pos,count,array); if(copy_buffer<0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iADX indicator, error code %d",GetLastError()); //--- quit with false result - it means that the indicator is considered as not calculated return; } else if(copy_buffer!=count) { //--- if it is copied less, than set PrintFormat("%d elements have been requested, only %d are copied",count,copy_buffer); //--- quit with zero result - it means that the indicator is considered as not calculated return; } string text="Request of data from one bar:\n"; StringConcatenate(text,text,"ADX(",InpAdxPeriod,") #",start_pos,": ",DoubleToString(array[0],2),"\n"); Comment(text); } //+------------------------------------------------------------------+
Описание
В "шапке" объявляем переменную "handle_iADX" в которой будет храниться хендл индикатора.
В OnInit() создаём индикатор и проверяем хендл - а вдруг он получился некорректный
В OnTick() объявляем переменные "buffer", "start_pos", "count" и массив "array". Получаем значение индикатора при помощи CopyBuffer и проверям на ошибки.
- 2.2. iCCI (Commodity Channel Index, CCI)
Задавать символ индикатора можно двумя способами: задать символ вручную или использовать символ на котором работает советник. Если разрешить пользователю задавать символ вручную (то есть во входных параметрах явно создать входной параметр "Символ индикатора", тогда возникает необходимость проверки корректности введённого имени символа. Алгоритм проверки, в таком случае, будет использоваться из #1 - при этом лучше создать для индикатора свой объект класса CSymbolInfo: в примере ниже это будет объект "m_symbol_CCI".
В примере даны два вида получения данных:
- за один запрос возврат значения индикатора на определённом бара
- за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)
Необходимые приготовления: так как этот пример в котором пользователю разрешено задавать символ индикатора вручную, в "шапке" советника прописываем подключение класса CSymbolInfo,
//+------------------------------------------------------------------+ //| iCCI.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.002" #property description "An example of working with the indicator iCCI (Commodity Channel Index, CCI)" #property description "Receiving data from a certain bar or from several bars" //--- #include <Trade\SymbolInfo.mqh> CSymbolInfo m_symbol_CCI; // symbol info object //--- input parameters input string InpSymbol="USDJPY"; // CCI: symbol name input ENUM_TIMEFRAMES InpTimeframe=PERIOD_CURRENT; // CCI: timeframe input int InpAveraging=14; // CCI: averaging period input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_TYPICAL;// CCI: type of price //--- int handle_iCCI; // variable for storing the handle of the iCCI indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+
, входные параметры и объявляем переменную "handle_iCCI" в которой будет храниться хендл индикатора.
Следующий шаг - подготовительная работа в OnInit(): проверка корректности заданного символа (на основе #1) и непосредственное создание хендла индикатора iCCI:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iCCI string symbol=(InpSymbol=="")?Symbol():InpSymbol; if(!m_symbol_CCI.Name(symbol)) // sets symbol name return(INIT_FAILED); handle_iCCI=iCCI(m_symbol_CCI.Name(),InpTimeframe,InpAveraging,InpAppliedPrice); //--- if the handle is not created if(handle_iCCI==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCCI indicator for the symbol %s/%s, error code %d", m_symbol_CCI.Name(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+
Забегая на перед: вот две функции получения данных индикатора:
"за один запрос возврат значения индикатора на определённом бара"
Входной параметр: номер бара "index" с которого нужно получить значение индикатора:
//+------------------------------------------------------------------+ //| Get value of buffers for the iCCI | //+------------------------------------------------------------------+ double iCCIGet(const int index) { double CCI[1]; //--- reset error code ResetLastError(); //--- fill a part of the iCCIBuffer array with values from the indicator buffer that has 0 index if(CopyBuffer(handle_iCCI,0,index,1,CCI)<0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iCCI indicator, error code %d",GetLastError()); //--- quit with zero result - it means that the indicator is considered as not calculated return(0.0); } return(CCI[0]); }
Возвращает значение индикатора на заданном номере бара или "0.0" в случае ошибки.
"за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)"
Когда может панодобиться такой метод: например нужно пройтись в цикле по нескольким значениям индикатора - в таком случае использовать метод "за один запрос возврат значения индикатора на определённом бара" будет дорого.
Входные параметры: стартовая позиция "start_pos" - с какого номера бара начинать запрос данных, количество запрашиваемых данных "count" и массив "array" (передаваемый по ссылке) в который и будут складываться полученные данные:
//+------------------------------------------------------------------+ //| Get value of buffers for the iCCI | //+------------------------------------------------------------------+ bool iCCIGet(const int start_pos,const int count,double &array[]) { //--- reset error code ResetLastError(); //--- fill a part of the iADXBuffer array with values from the indicator buffer that has 0 index int buffer=0; int copy_buffer=CopyBuffer(handle_iCCI,buffer,start_pos,count,array); if(copy_buffer<0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iADX indicator, error code %d",GetLastError()); //--- quit with false result - it means that the indicator is considered as not calculated return(false); } else if(copy_buffer!=count) { //--- if it is copied less, than set PrintFormat("%d elements have been requested, only %d are copied",count,copy_buffer); //--- quit with zero result - it means that the indicator is considered as not calculated return(false); } //--- return(true); }
Эта функция уже имеет тип "bool" и возвращает в случае удачного копирования (когда количество полученных данных равно количеству запрошенных) "true".
Заключительный этап - работа в OnTick() и пример обращения за данными двумя способами
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int bar=0; string text="Request of data from one bar:\n"; StringConcatenate(text,m_symbol_CCI.Name(),": ",text,"CCI(",InpAveraging,") #",bar,": ",DoubleToString(iCCIGet(bar),2),"\n"); double array[]; int count=3; if(iCCIGet(0,count,array)) { ArraySetAsSeries(array,true); StringConcatenate(text,text,"Request of data from three bars:\n"); for(int i=0;i<3;i++) { StringConcatenate(text,text,m_symbol_CCI.Name(),": ","CCI(",InpAveraging,") #",i,": ",DoubleToString(array[i],2),"\n"); } } Comment(text); }
Обратите внимание, что массиву "array", в который складываются данные с индикатора, устанавливается обратный порядок индексации - таки образом элемент массива array[0] на графике будет находится правее элемента array[n].
Проверка работы обоих методов получения данных с индикатора - мышка установлена на баре #0 символа EURUSD, а данные индикатора берутся с символа "USDJPY" ("USDJPY" задано во входных параметрах данного советника):
- 3.1. Номер счета
Почему пример такой простой функции дан не в виде скрипта, а в виде советника? А вот почему - AccountInfoInteger(ACCOUNT_LOGIN) выдаёт информацию с терминала (или не так: выдаёт информацию не только с подключения, но и с терминала, если нет подключения). Состояние перед экспериментом: терминал выгружен, интернет отключён.
Эксперимент: загружаем терминал (обратите внимание, интернета нет, но терминал остался залогинен на счёте 7884130)
и так как нет интернета закономерно видим это во вкладке "Журнал" терминала:
2018.03.06 06:54:13.722 '7884130': no connection to MetaQuotes-Demo 2018.03.06 06:54:13.782 authorization failed 2018.03.06 06:54:15.162 authorization failed
Прикрепляем советник "ACCOUNT_LOGIN.mq5" на график:
Вкладка "Журнал" | Вкладка "Эксперты" |
---|---|
2018.03.06 06:55:54.401 expert ACCOUNT_LOGIN (EURCAD,M1) loaded successfully | 2018.03.06 06:56:01.255 OnInit, ACCOUNT_LOGIN = 7884130 |
Обратите внимание, что при отсутствии интернета советник был прикреплён на график в 55:54.401, а OnInit() его прошёл только в 56:01.255. То есть в отсутствии интернета советник стартовал аж целых семь секунд! И при этом, хотя интернета нет, AccountInfoInteger(ACCOUNT_LOGIN) выдаёт информацию с терминала о подключении к счёту 7884130.
Имейте это в виду - наличие подключения к счёту ещё не означает, что с торговым сервером в данный момент есть связь.
Ну, а получить номер счёта можно одной строкой:
long account_login=AccountInfoInteger(ACCOUNT_LOGIN);
Советник "Only one account.mq5" - реализует запрет работы на счёте, если этот счёт не совпадает заданному. Заданный номер счёта задаётся хранится в переменной "specified_account".
//+------------------------------------------------------------------+ //| Only one account.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" #property description "An example of the work permit is strictly on the specified account" //--- long specified_account=7884130; // specified account //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { long account_login=AccountInfoInteger(ACCOUNT_LOGIN); if(account_login!=0 && account_login!=specified_account) { string text=""; StringConcatenate(text,"Account ",IntegerToString(account_login)," - work on this account is prohibited!"); Alert(text); Print(text); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { long account_login=AccountInfoInteger(ACCOUNT_LOGIN); if(account_login!=0 && account_login!=specified_account) { Print("Account ",IntegerToString(account_login)," - work on this account is prohibited!"); } } //+------------------------------------------------------------------+
Советник работает так: если в OnInit() обнаружится несовпадение заданного счёта и текущего счёта, то будет алерт и принт во вкладку "Эксперты" терминала. В OnTick() НА КАЖДОМ тике проверяется соответсвие счетов: если счет не соотвествует - то НА КАЖДОМ тике во вкладку "Эксперты" будет печаться предупреждение.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
В данной теме я соберу пользовательские MQL5 функции по работе с индикаторами, торговые и вспомогательные функции. В первом сообщении будет обновляемый рубрикатор. Возможно эьто даже будет дополнение к справке - ведь не для всех функций в справке есть примеры.
Каждую функцию Вы можете использовать как есть.