Как написать индикатор

Программирование прибыли: от азов к секретам мастерства. Читайте, спрашивайте, делитесь опытом.
Бонус за сообщение 0.5$
Ответственный Модератор - Haos

Как написать индикатор

Сообщение Рэндом » 21 апр 2016, 05:05

Рассмотрим индикатор моментум:
Код: выделить все
//+------------------------------------------------------------------+
//|                                                     Momentum.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
#property description "Momentum"
#property strict

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 DodgerBlue
//--- input parameter
input int InpMomPeriod=14;  // Momentum Period
//--- buffers
double ExtMomBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   string short_name;
//--- indicator line
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMomBuffer);
//--- name for DataWindow and indicator subwindow label
   short_name="Mom("+IntegerToString(InpMomPeriod)+")";
   IndicatorShortName(short_name);
   SetIndexLabel(0,short_name);
//--- check for input parameter
   if(InpMomPeriod<=0)
     {
      Print("Wrong input parameter Momentum Period=",InpMomPeriod);
      return(INIT_FAILED);
     }
//---
   SetIndexDrawBegin(0,InpMomPeriod);
//--- initialization done
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Momentum                                                         |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   int i,limit;
//--- check for bars count and input parameter
   if(rates_total<=InpMomPeriod || InpMomPeriod<=0)
      return(0);
//--- counting from 0 to rates_total
   ArraySetAsSeries(ExtMomBuffer,false);
   ArraySetAsSeries(close,false);
//--- initial zero
   if(prev_calculated<=0)
     {
      for(i=0; i<InpMomPeriod; i++)
         ExtMomBuffer[i]=0.0;
      limit=InpMomPeriod;
     }
   else
      limit=prev_calculated-1;
//--- the main loop of calculations
   for(i=limit; i<rates_total; i++)
      ExtMomBuffer[i]=close[i]*100/close[i-InpMomPeriod];
//--- done
   return(rates_total);
  }
//+------------------------------------------------------------------+


Код: выделить все
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 DodgerBlue


Начнем с блока свойств индикатора.
#property indicator_separate_window — это свойство задает что индикатор будет в отдельном окне.
#property indicator_buffers 1 — это свойство задает количество буферов индикатора.
Буфер индикатора — это особый массив который является динамическим и связан с количеством баров на графике. Буфер индикатора отображается на графике или в отдельном окне. Как будет отображаться буфер зависит от его свойств.
#property indicator_color1 DodgerBlue Это свойство задает цвет буфера.

double ExtMomBuffer[]; Эта строка задает массив для буфера индикатора. Массив задается без размера.

Далее идет функция:
Код: выделить все
int OnInit(void)
  {
   string short_name;
//--- indicator line
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMomBuffer);
//--- name for DataWindow and indicator subwindow label
   short_name="Mom("+IntegerToString(InpMomPeriod)+")";
   IndicatorShortName(short_name);
   SetIndexLabel(0,short_name);
//--- check for input parameter
   if(InpMomPeriod<=0)
     {
      Print("Wrong input parameter Momentum Period=",InpMomPeriod);
      return(INIT_FAILED);
     }
//---
   SetIndexDrawBegin(0,InpMomPeriod);
//--- initialization done
   return(INIT_SUCCEEDED);
  }

Эта функция вызывается всякий раз как только индикатор прикрепляется к графику. Она вызывается один раз за время жизни индикатора. Служит она для начальной инициализации и используется для инициализации буфера индикатора.
Продолжение следует.
Аватар пользователя
Рэндом
Специалист MQL
 
Сообщений: 13700
Зарегистрирован: 18 июл 2013, 08:05
Средств на руках: 31.45 Доллар
Группа: Администраторы
Благодарил (а): 1131 раз.
Поблагодарили: 3174 раз.
Каждый заблуждается в меру своих возможностей.

Re: Как написать индикатор

Сообщение Рэндом » 22 апр 2016, 05:25

Рассмотрим инициализацию индикатора.

SetIndexStyle(0,DRAW_LINE); Здесь задается стиль отрисовки линия для буфера 0.

SetIndexBuffer(0,ExtMomBuffer); Массив который мы определили ранее необходимо сделать буфером индикатора. И здесь он делается буфером с номером 0.

IndicatorShortName(short_name); Задает имя индикатора которое будет отображаться в окне индикатора. Это делать не обязательно.

SetIndexLabel(0,short_name); А здесь задается всплывающая подсказка для окна индикатора.

SetIndexDrawBegin(0,InpMomPeriod); Эта функция задает с какого индекса рисовать буфер 0 индикатора.

Для отрисовки индикатора надо как минимум связать его массив с буфером и задать стиль отрисовки индикатора.


А тепреь рассмотрим следующий код:

Код: выделить все
 if(InpMomPeriod<=0)
     {
      Print("Wrong input parameter Momentum Period=",InpMomPeriod);
      return(INIT_FAILED);
     }


Если в параметрах задан период индикатора меньше или равно 0, то выводиться сообщение об ошибки и return(INIT_FAILED); возврашает сообщение о неудачной инициализации индикатора. Это значит что индикатор не будет выполняться дальше. Т.е. функция расчета индикатора не будет вызвана.

Еще одно замечание о нумерации элементов буфера индикатора. Последний бар буфера имеет индекс 0. Т.е. нумерация происходит в обратном порядке по сравнению с массивом.

Продолжение следует.
Аватар пользователя
Рэндом
Специалист MQL
 
Сообщений: 13700
Зарегистрирован: 18 июл 2013, 08:05
Средств на руках: 31.45 Доллар
Группа: Администраторы
Благодарил (а): 1131 раз.
Поблагодарили: 3174 раз.
Каждый заблуждается в меру своих возможностей.

Re: Как написать индикатор

Сообщение Haos » 22 апр 2016, 10:14

Рэндом писал(а):
Код: выделить все
 if(InpMomPeriod<=0)
     {
      Print("Wrong input parameter Momentum Period=",InpMomPeriod);
      return(INIT_FAILED);
     }


Если в параметрах задан период индикатора меньше или равно 0, то выводиться сообщение об ошибки и return(INIT_FAILED); возврашает сообщение о неудачной инициализации индикатора. Это значит что индикатор не будет выполняться дальше. Т.е. функция расчета индикатора не будет вызвана.

Вот эта вот функция Print() имеет свою не слишком удачную составляющую в виде вывода сообщения не на экран, а в журнал экспертов и часто может происходить так, что или пользователь не заметит изменений или не поймет почему индикатор не работает, так как не знает или забыл, что можно увидеть инфу в соотв. месте.
Поэтому я бы предпочел функцию Alert() для вывода информации в диалоговое окно.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Re: Как написать индикатор

Сообщение Рэндом » 25 апр 2016, 05:20

Код: выделить все
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

Эта функция вызывается сразу же после Init, а затем при каждом тике. Функция вызывается только в индикаторах.
Первый параметр rates_total содержит количество баров, доступных индикатору для расчета, и соответствует количеству баров, доступных на графике.
Необходимо отметить связь между значением, возвращаемым функцией OnCalculate() и вторым входным параметром prev_calculated. Параметр prev_calculated при вызове функции содержит значение, которое вернула функция OnCalculate() на предыдущем вызове. Это позволяет реализовать экономные алгоритмы расчета пользовательского индикатора с тем, чтобы избежать повторных расчетов для тех баров, которые не изменились с предыдущего запуска этой функции.
Для этого обычно достаточно вернуть значение параметра rates_total, которое содержит количество баров при текущем вызове функции. Если с момента последнего вызова функции OnCalculate() ценовые данные были изменены (подкачана более глубокая история или были заполнены пропуски истории), то значение входного параметра prev_calculated будет установлено в нулевое значение самим терминалом.
Остальные параметры должны быть понятны без объяснения. Это данные графика к которому прикреплен индикатор. Здесь стоит отменить что порядок следования эдементов этих массивов обратен буферу индикатора. Для задания направления нумерации существует функция ArrayAsSeries().

Продолжение следует.
Аватар пользователя
Рэндом
Специалист MQL
 
Сообщений: 13700
Зарегистрирован: 18 июл 2013, 08:05
Средств на руках: 31.45 Доллар
Группа: Администраторы
Благодарил (а): 1131 раз.
Поблагодарили: 3174 раз.
Каждый заблуждается в меру своих возможностей.

Re: Как написать индикатор

Сообщение Рэндом » 26 апр 2016, 04:59

Код: выделить все
{
   int i,limit;
//--- check for bars count and input parameter
   if(rates_total<=InpMomPeriod || InpMomPeriod<=0)
      return(0);
//--- counting from 0 to rates_total
   ArraySetAsSeries(ExtMomBuffer,false);
   ArraySetAsSeries(close,false);
//--- initial zero
   if(prev_calculated<=0)
     {
      for(i=0; i<InpMomPeriod; i++)
         ExtMomBuffer[i]=0.0;
      limit=InpMomPeriod;
     }
   else
      limit=prev_calculated-1;
//--- the main loop of calculations
   for(i=limit; i<rates_total; i++)
      ExtMomBuffer[i]=close[i]*100/close[i-InpMomPeriod];
//--- done
   return(rates_total);
  }

if(rates_total<=InpMomPeriod || InpMomPeriod<=0)
return(0); Если баров на графике меньше или равно периоду индикатора или период индикатора равен или меньше 0, то выход из функции.

ArraySetAsSeries(ExtMomBuffer,false);
ArraySetAsSeries(close,false); Эти функции устанавливают нормальный порядок нумерации массивов как для буфера, так и для ценовых данных. Это сделано потому что ценовые данные передаваемые в функцию имеют нормальную нумерацию, а буфер обратную.

Код: выделить все
if(prev_calculated<=0)
     {
      for(i=0; i<InpMomPeriod; i++)
         ExtMomBuffer[i]=0.0;
      limit=InpMomPeriod;
     }
   else
      limit=prev_calculated-1;

Здесь опредиляется точка старта для расчета индикатора. Если не просчитан ни один бар, то буфер индикатора обнуляется до периода индикатора. Далее задается точка старта расчета равная периоду индикатора. Это делается потому что индикатор берет прошлые ценовые данные на глубину равную периоду индикатора. Поэтому считать значения раньше периода индикатора бессмысленно. Если этого не сделать на этапе выполнения можно получить ошибку.

limit=prev_calculated-1; Это делается для того чтобы всегда просчитывать последний бар.

for(i=limit; i<rates_total; i++)
ExtMomBuffer[i]=close[i]*100/close[i-InpMomPeriod]; Расчет индикатора.

return(rates_total); Возвращаем в терминал количество баров на этой итерации индикатора.

Жду ваших вопросов.
Аватар пользователя
Рэндом
Специалист MQL
 
Сообщений: 13700
Зарегистрирован: 18 июл 2013, 08:05
Средств на руках: 31.45 Доллар
Группа: Администраторы
Благодарил (а): 1131 раз.
Поблагодарили: 3174 раз.
Каждый заблуждается в меру своих возможностей.


Вернуться в MQL – теория и практика

Кто сейчас на форуме?

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 46

Права доступа к форуму

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения