MQL5: Примеры. - страница 2

 
  • 2.6. iStochastic (Stochastic Oscillator)

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

В примере даны два вида получения данных: 

  • за один запрос возврат значения индикатора на определённом бара
  • за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)

Необходимые приготовления: так как этот пример в котором пользователю разрешено задавать символ индикатора вручную, в "шапке" советника прописываем подключение класса CSymbolInfo

//+------------------------------------------------------------------+
//|                                                  iStochastic.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 working with the indicator iStochastic (Stochastic Oscillator)"
#property description "Receiving data from a certain bar or from several bars"
//---
#include <Trade\SymbolInfo.mqh>  
CSymbolInfo    m_symbol_Stochastic;                         // symbol info object
//--- input parameters
input string            InpSymbol         = "AUDCAD";       // Stochastic: symbol name 
input ENUM_TIMEFRAMES   InpTimeframe      = PERIOD_CURRENT; // Stochastic: timeframe  
input int               InpKperiod        = 5;              // Stochastic: K-period (number of bars for calculations) 
input int               InpDperiod        = 3;              // Stochastic: D-period (period of first smoothing) 
input int               InpSlowing        = 3;              // Stochastic: final smoothing 
input ENUM_MA_METHOD    InpMa_method      = MODE_SMA;       // Stochastic: type of smoothing 
input ENUM_STO_PRICE    InpPrice_field    = STO_LOWHIGH;    // Stochastic: stochastic calculation method 
//---
int                     handle_iStochastic;                 // variable for storing the handle of the iStochastic indicator 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

а также входные параметры и объявляем переменную "handle_iStochastic" в которой будет храниться хендл индикатора.


Следующий шаг - подготовительная работа в OnInit(): проверка корректности заданного символа (на основе ) и непосредственное создание хендла индикатора iStochastic:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create handle of the indicator iStochastic
   string symbol=(InpSymbol=="")?Symbol():InpSymbol;

   if(!m_symbol_Stochastic.Name(symbol)) // sets symbol name
      return(INIT_FAILED);

   handle_iStochastic=iStochastic(m_symbol_Stochastic.Name(),InpTimeframe,
                                  InpKperiod,InpDperiod,InpSlowing,
                                  InpMa_method,InpPrice_field);
//--- if the handle is not created 
   if(handle_iStochastic==INVALID_HANDLE)
     {
      //--- tell about the failure and output the error code 
      PrintFormat("Failed to create handle of the iStochastic indicator for the symbol %s/%s, error code %d",
                  m_symbol_Stochastic.Name(),
                  EnumToString(InpTimeframe),
                  GetLastError());
      //--- the indicator is stopped early 
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+


Забегая на перед: вот две функции получения данных индикатора:

"за один запрос возврат значения индикатора на определённом бара"

Входные параметры: индекс буфера "buffer" (может принимать значения MAIN_LINE и SIGNAL_LINE) и номер бара "index" с которого нужно получить значение индикатора:

//+------------------------------------------------------------------+
//| Get value of buffers for the iStochastic                         |
//|  the buffer numbers are the following:                           |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
double iStochasticGet(const int buffer,const int index)
  {
   double Stochastic[1];
//--- reset error code 
   ResetLastError();
//--- fill a part of the iStochasticBuffer array with values from the indicator buffer that has 0 index 
   if(CopyBuffer(handle_iStochastic,buffer,index,1,Stochastic)<0)
     {
      //--- if the copying fails, tell the error code 
      PrintFormat("Failed to copy data from the iStochastic indicator, error code %d",GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated 
      return(0.0);
     }
   return(Stochastic[0]);
  }

Возвращает значение индикатора на заданном номере бара или "0.0" в случае ошибки.


"за один запрос возврат значений индикатора для некого интервала баров (получение данных в массив)"

Когда может панодобиться такой метод: например нужно пройтись в цикле по нескольким значениям индикатора - в таком случае использовать метод "за один запрос возврат значения индикатора на определённом бара" будет дорого.

Входные параметры: индекс буфера "buffer" (могут принимать значения MAIN_LINE и SIGNAL_LINE), стартовая позиция "start_pos" - с какого номера бара начинать запрос данных, количество запрашиваемых данных "count" и массив "array" (передаваемый по ссылке) в который и будут складываться полученные данные:

//+------------------------------------------------------------------+
//| Get value of buffers for the iStochastic                         |
//|  the buffer numbers are the following:                           |
//|   0 - MAIN_LINE, 1 - SIGNAL_LINE                                 |
//+------------------------------------------------------------------+
bool iStochasticGet(const int buffer,const int start_pos,const int count,double &array[])
  {
//--- reset error code 
   ResetLastError();
//--- fill a part of the iStochastic array with values from the indicator buffer that has 0 index 
   int copy_buffer=CopyBuffer(handle_iStochastic,buffer,start_pos,count,array);
   if(copy_buffer<0)
     {
      //--- if the copying fails, tell the error code 
      PrintFormat("Failed to copy data from the iStochastic 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,"Stoch(",InpKperiod,",",InpDperiod,",",InpSlowing,") #",bar,": ",DoubleToString(iStochasticGet(MAIN_LINE,bar),2),"\n");
   StringConcatenate(text,text,"Signal: ",DoubleToString(iStochasticGet(SIGNAL_LINE,bar),2),"\n");
   double array[];
   int count=3;
   if(iStochasticGet(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,"Stoch(",InpKperiod,",",InpDperiod,",",InpSlowing,") #",i,": ",DoubleToString(array[i],2),"\n");
        }
     }
   Comment(text);
  }

Обратите внимание, что массиву "array", в который складываются данные с индикатора, устанавливается обратный порядок индексации - таки образом элемент массива array[0] на графике будет находится правее элемента array[n].


Проверка работы обоих методов получения данных с индикатора - мышка установлена на баре #0 и можно сравнить "Окно данных" и распечатанные на экране значения:


Файлы:
iStochastic.mq5  13 kb
 

Посмотрите, как просто и понятно можно написать команду "Открыть позицию BUY объёмом 1.0 лот":

   m_trade.Buy(1.0); // open Buy position, volume 1.0 lot


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

#include <Trade\Trade.mqh>
CTrade         m_trade;                      // trading object

Первая строка - это подключение класса, вторая строка - создание объекта этого класса.


Весь скрипт выглядит так:

//+------------------------------------------------------------------+
//|                                                     Open Buy.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"
//---
#include <Trade\Trade.mqh>
CTrade         m_trade;                      // trading object
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   m_trade.Buy(1.0); // open Buy position, volume 1.0 lot
  }

Для открытия позиции BUY используется метод CTrade::Buy.

 

А может разъясните людям почему при пользовании вашего "короткого" примера получается огромной ex5 (47к) ?

Тот же результат реализован обычным способом (на примере из документации) намного короче (8к) !!!

Надеюсь Вы вполне несознательно опустили ту не-маловажную деталь

Кстати разница в объеме кодов незначительна

 
Ivan Ivanov:

А может разъясните людям почему при пользовании вашего "короткого" примера получается огромной код (47к) ?

Тот же результат реализован обычным способом (на примере из документации) намного короче (8к) !!!

Надеюсь Вы вполне несознательно опустили ту не-маловажную деталь

  • 1. Вы просто написали, даже не потрудившись цитировать - кто поймёт на какой пост Вы оставили свой вопрос?
  • 2. Вы банально сравниваете круглое с зеленым :)
    • 2.1. Для тех, кто не понял пункт 2: некорректное сравнение - пользователь сравнивает КОЛИЧЕСТВО СТРОК В КОДЕ с ИТОГОВЫМ РАЗМЕРОМ после компиляции.

 
Ivan Ivanov:

А может разъясните людям почему при пользовании вашего "короткого" примера получается огромной ex5 (47к) ?

Тот же результат реализован обычным способом (на примере из документации) намного короче (8к) !!!

Надеюсь Вы вполне несознательно опустили ту не-маловажную деталь

Кстати разница в объеме кодов незначительна

ну во первых: 

Чем это деталь так значительна ? Экономите место на компьютере ? 

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

Ну и в третьих почитайте что обозначает #include.  Это копирование файла в текст программы. Т.е. весь класс копируется, прежде чем идет вызов открытия ордера. В этом же классе проводятся проверки на корректность подачи приказа. 

В этом и есть одно из основных преимуществ ООП.

 
Dmitiry Ananiev:

Чем это деталь так значительна ? Экономите место на компьютере ?

Не уже ли не понимаете :

Больше *.ex5 == больше времени для выполнение , ето программисты учат на первом курсе.

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

 
Ivan Ivanov:

Не уже ли не понимаете :

Больше *.ex5 == больше времени для выполнение , ето программисты учат на первом курсе.

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

Программа в первую очередь должна делать то, что она должна делать. И только после этого тезиса программа должна все делать как можно быстрее и как можно меньше жрать ресурсов. НО в современном мире на уровне весьма простых задач в виде торговых роботов большинство кодов вообще не проходят оптимизацию на время исполнения. А используя Стандартную библиотеку этого делать в 90% случаев ни не надо

 
  • 5.1. Как автоматически оптимизировать советник по всем таймфреймам

Алгоритм данного решения для советника, который работает только в момент рождения нового бара

1. Советник ВСЕГДА ДОЛЖЕН запускаться на таймфрейме M1.

2. Во входных параметрах добавляем параметр "Timeframe":

input ENUM_TIMEFRAMES InpTimeFrame  = PERIOD_M15;  // Timeframe

Данный параметр позволит в тестере стратегий выполнить перебор всех таймфреймов (провести тестирование на всех таймфреймах и сравнить результаты) 

3. В 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,m_symbol.Name(),InpTimeFrame);
   if(time_0==PrevBars)
      return;
   PrevBars=time_0;
//---
...
//+------------------------------------------------------------------+ 
//| Get Time for specified bar index                                 | 
//+------------------------------------------------------------------+ 
datetime iTime(const int index,string symbol=NULL,ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT)
  {
   if(symbol==NULL)
      symbol=m_symbol.Name();
   if(timeframe==0)
      timeframe=Period();
   datetime Time[1];
   datetime time=0; // D'1970.01.01 00:00:00'
   int copied=CopyTime(symbol,timeframe,index,1,Time);
   if(copied>0)
      time=Time[0];
   return(time);
  }
Здесь "m_symbol" - объект класса CSymbolInfo. Подробнее о применении смотрите в посте  .
 
  • 4.2. Задаём MagicNumber и проверяем в OnTradeTransaction

MagicNumber задаётся при помощи метода Ctrade::SetExpertMagicNumber

Немного изменю пример 4.1. Как открыть позицию (самый минимальный код) - так как "Sert MagicNumber.mq5" - это скрипт, а не эксперт, то для отображения входных параметров нужно прописать директиву script_show_inputs. Обратите внимание, что входной параметр "magic number" должен иметь тип ulong.

//+------------------------------------------------------------------+
//|                                             Sert MagicNumber.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 script_show_inputs
//--- input parameters
input ulong    m_magic=15489;                // magic number
//---
#include <Trade\Trade.mqh>
CTrade         m_trade;                      // trading object
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   m_trade.SetExpertMagicNumber(m_magic);    // set magic number
   m_trade.Buy(1.0);                         // open Buy position, volume 1.0 lot
  }
//+------------------------------------------------------------------+

Скрипт выполняет два действия: устанавливает идентификатор эксперта (magic number) и отсылает торговый приказ на открытие позиции BUY объёмом 1.0 лот.

Файлы:
 
  • 2.3. iFractals (Fractals). Два важных фактора, которые нужно учитывать

При работе с фракталами нужно учитывать два фактора:

Фактор первый

фрактал на барах от "rates_total-5" до "rates_total-3" уже не может перерисоваться - это вытекает из конструкции индикатора "Fractals.mq5" (открытый код индикатора расположен в [data folder]\MQL5\Indicators\Examples\Fractals.mq5) - так как бары в промежутке от "rates_total-5" до "rates_total-3"  уже сформировавшиеся

//---
   if(prev_calculated<7)
     {
      limit=2;
      //--- clean up arrays
      ArrayInitialize(ExtUpperBuffer,EMPTY_VALUE);
      ArrayInitialize(ExtLowerBuffer,EMPTY_VALUE);
     }
   else limit=rates_total-5;

   for(i=limit;i<rates_total-3 && !IsStopped();i++)
     {
      //---- Upper Fractal
      if(high[i]>high[i+1] && high[i]>high[i+2] && high[i]>=high[i-1] && high[i]>=high[i-2])
         ExtUpperBuffer[i]=high[i];
      else ExtUpperBuffer[i]=EMPTY_VALUE;

      //---- Lower Fractal
      if(low[i]<low[i+1] && low[i]<low[i+2] && low[i]<=low[i-1] && low[i]<=low[i-2])
         ExtLowerBuffer[i]=low[i];
      else ExtLowerBuffer[i]=EMPTY_VALUE;
     }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }

Fractals

Рис. 1. Так работает индикатор "Fractals". Нумерация файлов с позиции индикатора "Fractals.mq5"

Это означает, что когда в советнике получаем данные с индикатора iFractals, на барах "rates_total-5", "rates_total-4" (нумерация баров с позиции индикатора "Fractals.mq5") будет фрактал, который уже гарантированно не перерисуется. 


А вот момент когда фрактал формируется на баре "rates_total-5" - в момент когда появляется новый бар справа 2016 года 2 января 03:00 (нумерация файлов с позиции индикатора "Fractals.mq5") :

Fractals, 5 bar

Рис. 2. Момент когда фрактал формируется на на баре "rates_total-5"

Фактор второй

Индикатор фрактал в качестве пустого значения использует не "0.0", а EMPTY_VALUE. Исключение - первая инициализация, когда индикаторные буфера инициализируются нулём (это делает не индикатор "Fractals.mq5", а MQL5 при создании массива и связявании его с индикаторным буфером).

Причина обращения: