MQL5: Примеры.

 

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

Каждую функцию Вы можете использовать как есть.

 

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

  • проверить, может символ уже выбран в окне в окне 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)

Задавать символ индикатора можно двумя способами: задать символ вручную или использовать символ на котором работает советник. Если разрешить пользователю задавать символ вручную (то есть во входных параметрах явно создать входной параметр "Символ индикатора", тогда возникает необходимость проверки корректности введённого имени символа. Алгоритм проверки, в таком случае, будет использоваться из  - при этом лучше создать для индикатора свой объект класса 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(): проверка корректности заданного символа (на основе ) и непосредственное создание хендла индикатора 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 и можно сравнить "Окно данных" и распечатанные на экране значения:


Файлы:
iADX.mq5  12 kb
 
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. 

 
Dmitiry Ananiev:


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

Почему не использовать

В чем принципиальная разница ? Я больше сторонник ООП, н зачем все усложнять и создавать объект там где и без него хорошо ? 

Я так понимаю, что 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) для бара на котором найдено пересечение.

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

  1. оба вида сигналов отсутствуют (например перед первым запуском)
  2. присутствует только один из сигналов
  3. нет результатов проверки размещения отложенного ордера

Оба вида сигналов буду хранить в двух флагах:

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.

Файлы:
ADX_EA.mq5  18 kb
 
Vladimir Karputov:

Я так понимаю, что 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)

Задавать символ индикатора можно двумя способами: задать символ вручную или использовать символ на котором работает советник. Если разрешить пользователю задавать символ вручную (то есть во входных параметрах явно создать входной параметр "Символ индикатора", тогда возникает необходимость проверки корректности введённого имени символа. Алгоритм проверки, в таком случае, будет использоваться из  - при этом лучше создать для индикатора свой объект класса 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(): проверка корректности заданного символа (на основе ) и непосредственное создание хендла индикатора 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" задано во входных параметрах данного советника):


Файлы:
iCCI.mq5  11 kb
 
  • 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() НА КАЖДОМ тике проверяется соответсвие счетов: если счет не соотвествует - то НА КАЖДОМ тике во вкладку "Эксперты" будет печаться предупреждение.

Файлы: