English 中文 Español Deutsch 日本語 Português
preview
Готовые шаблоны для подключения индикаторов в экспертах (Часть 3): Трендовые индикаторы

Готовые шаблоны для подключения индикаторов в экспертах (Часть 3): Трендовые индикаторы

MetaTrader 5Примеры | 28 сентября 2023, 14:45
1 550 0
Artyom Trishkin
Artyom Trishkin

Содержание


Введение

Статья является продолжением темы готовых шаблонов для использования индикаторов в советниках. Уже были рассмотрены шаблоны подключения к советникам индикаторов-осцилляторов и индикаторов объёма и Билла Вильямса.
Сегодня рассмотрим подключение к советникам и использование трендовых индикаторов. Данные, получаемые от индикаторов, в качестве примера, как и в предыдущих статьях, будем выводить на информационную панель, которую создали в первой статье этого цикла.

Статья по стилю изложения информации ничем не будет отличаться от предыдущих статей — краткий справочный обзор каждого трендового индикатора и лаконичные коды для подключения и использования индикаторов в советниках.

Для каждого индикатора в статье будут представлены готовые шаблоны для использования в своих программах:

  • Переменные, входные и глобальные.
  • Инициализация переменных и создание хэндла индикатора.
  • Деинициализация.
  • Получение данных в советнике от индикатора.
  • Пример вывода полученных данных на информационную панель.

Статья имеет практическую пользу, так как предоставляет возможность просто использовать коды из неё как Copy-Paste в своих разработках.


Adaptive Moving Average

Технический индикатор Адаптивное Скользящее Среднее (Adaptive Moving Average, AMA) используется для построения скользящей средней с малой чувствительностью к шумам в ценовых сериях и характеризуется минимальным запаздыванием для определения тренда. Его разработал и описал Перри Кауфман (Perry Kaufman) в своей книге "Smarter Trading".

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



Параметры

Для создания хэндла индикатора используется функция iAMA():

Возвращает хэндл индикатора Adaptive Moving Average. Всего один буфер.

int  iAMA(
   string              symbol,             // имя символа
   ENUM_TIMEFRAMES     period,             // период
   int                 ama_period,         // период AMA
   int                 fast_ma_period,     // период быстрой скользящей
   int                 slow_ma_period,     // период медленной скользящей
   int                 ama_shift,          // смещение индикатора по горизонтали
   ENUM_APPLIED_PRICE  applied_price       // тип цены или handle
   );

symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

ama_period

[in]  Период вычисления, на котором вычисляется  коэффициент эффективности.

fast_ma_period

[in]  Быстрый период для вычисления коэффициента сглаживания в моменты быстрого рынка.

slow_ma_period

[in]  Медленный период для вычисления коэффициента сглаживания в отсутствии тренда.

ama_shift

[in]  Сдвиг индикатора относительно ценового графика.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                                 TestTrendAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  9;             /* Period         */ // Период расчёта AMA
input uint                 InpPeriodFast  =  2;             /* Fast EMA Period*/ // Период расчёта быстрой EMA
input uint                 InpPeriodSlow  =  30;            /* Slow EMA Period*/ // Период расчёта медленной EMA
input int                  InpShift       =  0;             /* AMA Shift      */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта AMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта AMA
int      period_fast=0;          // Период расчёта быстрой EMA
int      period_slow=0;          // Период расчёта медленной EMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

Перечисление ENUM_LINE_STATE создано для упрощения получения состояния линии индикатора — её формы (фигуры) и расположения относительно линии другого индикатора, либо какого-либо уровня.
Подробнее о перечислении можно прочитать в статье по индикаторам-осцилляторам в разделе параметров индикатора ATR.

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                                 TestTrendAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  9;             /* Period         */ // Период расчёта AMA
input uint                 InpPeriodFast  =  2;             /* Fast EMA Period*/ // Период расчёта быстрой EMA
input uint                 InpPeriodSlow  =  30;            /* Slow EMA Period*/ // Период расчёта медленной EMA
input int                  InpShift       =  0;             /* AMA Shift      */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта AMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта AMA
int      period_fast=0;          // Период расчёта быстрой EMA
int      period_slow=0;          // Период расчёта медленной EMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 9 : InpPeriod);
   period_fast=int(InpPeriodFast<1 ? 2 : InpPeriodFast);
   period_slow=int(InpPeriodSlow<1 ? 30 : InpPeriodSlow);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("AMA(%lu,%lu,%lu)",period,period_fast,period_slow);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iAMA(Symbol(),PERIOD_CURRENT,period,period_fast,period_slow,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 9 : InpPeriod);
   period_fast=int(InpPeriodFast<1 ? 2 : InpPeriodFast);
   period_slow=int(InpPeriodSlow<1 ? 30 : InpPeriodSlow);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("AMA(%lu,%lu,%lu)",period,period_fast,period_slow);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iAMA(Symbol(),PERIOD_CURRENT,period,period_fast,period_slow,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора представлены ниже. Функции были рассмотрены в статье о подключении осцилляторов к советникам. Представленные функции можно использовать "как есть" в своих программах:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные индикатора с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Выводим описание состояния линии индикатора
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:



Файл тестового советника "TestTrendAMA.mq5" можно посмотреть в прикреплённых к статье файлах.


Average Directional Movement Index

Технический индикатор Индекс Среднего Направления Движения (Average Directional Movement Index, ADX) помогает определить наличие ценовой тенденции. Он построен на подходах, описанных в книге "Новые концепции технических торговых систем" Уэллса Уайлдера.

Простейший метод торговли на основе системы направленного движения предполагает сравнение двух индикаторов направленности 14-периодного +DI и 14-периодного -DI. Для этого либо графики индикаторов наносятся один на другой, либо +DI вычитается из -DI. У. Уайлдер предлагает покупать, если +DI поднимается выше -DI, и продавать, когда +DI опускается ниже -DI.

Эти простые торговые правила У. Уайлдер дополняет также "правилом экстремальных точек". Оно служит для устранения ложных сигналов и уменьшения числа заключаемых сделок. Согласно принципу экстремальных точек, в момент пересечения +DI и -DI необходимо отметить "экстремальную точку". Если +DI поднимается выше -DI, этой точкой является максимальная цена дня пересечения. Если +DI опускается ниже -DI, эта точка — минимальная цена дня пересечения.

Экстремальная точка затем используется как уровень вхождения в рынок. Так, после сигнала к покупке (+DI поднялся выше -DI) необходимо дождаться, когда цена поднимется выше экстремальной точки, и лишь после этого покупать. Если же цене не удается преодолеть уровень экстремальной точки, следует сохранять короткую позицию.



Параметры

Для создания хэндла индикатора используется функция iADX():

Возвращает хэндл индикатора Average Directional Movement Index.

int  iADX(
   string           symbol,         // имя символа
   ENUM_TIMEFRAMES  period,         // период
   int              adx_period      // период усреднения
   );

symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

adx_period

[in]  Период для вычисления индекса.

Номера буферов: 0 — MAIN_LINE, 1 — PLUSDI_LINE, 2 — MINUSDI_LINE.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                                 TestTrendADX.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */ // Период расчёта ADX
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта ADX
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                                 TestTrendADX.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */ // Период расчёта ADX
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта ADX
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("ADX(%lu)",period);
   ind_digits=2;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iADX(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("ADX(%lu)",period);
   ind_digits=2;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iADX(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,4,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные линии ADX с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,MAIN_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Выводим на панель данные линии +DI с указанного бара в таблицу 1
   panel.DrawText("+DI", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,PLUSDI_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Выводим на панель данные линии -DI с указанного бара в таблицу 1
   panel.DrawText("-DI", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,MINUSDI_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Выводим описание соотношения линии +DI и -DI
   panel.DrawText("+DI vs -DI", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   ENUM_LINE_STATE state=LineStateRelative(handle,index,1,value_dim,IndicatorValue(handle,index+1,2));
   string state_di_str=LineStateDescription(state);
   color clr=clrNONE;
   if(state==LINE_STATE_ABOVE)
     {
      state_di_str="+DI > -DI";
      clr=clrGreen;
     }
   if(state==LINE_STATE_CROSS_UP)
      clr=clrGreen;
   if(state==LINE_STATE_UNDER)
     {
      state_di_str="+DI < -DI";
      clr=clrRed;
     }
   if(state==LINE_STATE_CROSS_DOWN)
      clr=clrRed;
   panel.DrawText(state_di_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Помимо данных линий индикатора на панель выводится описание соотношения линий +DI и -DI, по сути являющимися сигнальными линиями индикатора.

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendADX.mq5" можно посмотреть в прикреплённых к статье файлах.


Average Directional Movement Index Wilder

Технический индикатор Индекс Среднего Направления Движения Уайлдера (Average Directional Movement Index Wilder, ADX Wilder) помогает определить наличие ценовой тенденции. Данный индикатор построен в строгом соответствии с алгоритмом, описанным Уэллсом Уайлдером в книге "Новые концепции технических торговых систем".

Торговые правила по данному индикатору описаны в разделе "Average Directional Movement Index".



Параметры

Для создания хэндла индикатора используется функция iADXWilder():

Возвращает хэндл индикатора Average Directional Movement Index by Welles Wilder.

int  iADXWilder(
   string           symbol,         // имя символа
   ENUM_TIMEFRAMES  period,         // период
   int              adx_period      // период усреднения
   );

symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

adx_period

[in]  Период для вычисления индекса.

Номера буферов: 0 — MAIN_LINE, 1 — PLUSDI_LINE, 2 — MINUSDI_LINE.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                           TestTrendADXWilder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */ // Период расчёта ADX Wilder
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта ADX Wilder
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                           TestTrendADXWilder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint  InpPeriod   =  14;   /* Period   */ // Период расчёта ADX Wilder
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта ADX Wilder
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("ADX Wilder(%lu)",period);
   ind_digits=2;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iADXWilder(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("ADX Wilder(%lu)",period);
   ind_digits=2;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iADXWilder(Symbol(),PERIOD_CURRENT,period);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,4,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные линии ADX с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,MAIN_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Выводим на панель данные линии +DI с указанного бара в таблицу 1
   panel.DrawText("+DI", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,PLUSDI_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Выводим на панель данные линии -DI с указанного бара в таблицу 1
   panel.DrawText("-DI", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,MINUSDI_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Выводим описание соотношения линии +DI и -DI
   panel.DrawText("+DI vs -DI", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   ENUM_LINE_STATE state=LineStateRelative(handle,index,1,value_dim,IndicatorValue(handle,index+1,2));
   string state_di_str=LineStateDescription(state);
   color clr=clrNONE;
   if(state==LINE_STATE_ABOVE)
     {
      state_di_str="+DI > -DI";
      clr=clrGreen;
     }
   if(state==LINE_STATE_CROSS_UP)
      clr=clrGreen;
   if(state==LINE_STATE_UNDER)
     {
      state_di_str="+DI < -DI";
      clr=clrRed;
     }
   if(state==LINE_STATE_CROSS_DOWN)
      clr=clrRed;
   panel.DrawText(state_di_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

На панель выводятся данные линий индикатора и описание соотношения линий +DI и -DI, которые по сути являются сигнальными линиями индикатора.

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:



Файл тестового советника "TestTrendADXWilder.mq5" можно посмотреть в прикреплённых к статье файлах.


Bollinger Bands®

Полосы Боллинджера (Bollinger Bands, BB) схожи с Envelopes. Различие между ними состоит в том, что границы Торговых Полос (Envelopes) расположены выше и ниже кривой скользящего среднего на фиксированном, выраженном в процентах расстоянии, тогда как границы Bollinger Bands строятся на расстояниях, равных определенному числу стандартных отклонений. Поскольку величина стандартного отклонения зависит от волатильности, полосы сами регулируют свою ширину: она увеличивается, когда рынок неустойчив, и уменьшается в более стабильные периоды.

Bollinger Bands обычно наносятся на ценовой график, но могут наноситься и на график индикатора. Как и в случае с Envelopes, интерпретация Bollinger Bands основана на том, что ценам свойственно оставаться в пределах верхней и нижней границ полосы. Отличительной особенностью Полос Боллинджера является их переменная ширина, обусловленная волатильностью цен. В периоды значительных ценовых изменений (т.е., высокой волатильности) полосы расширяются, давая простор ценам. В периоды застоя (т.е., низкой волатильности) полосы сужаются, удерживая цены в пределах своих границ.

К особенностям этого индикатора можно отнести:

  1. резкие изменения цен, обычно происходящие после сужения полосы, соответствующего снижению волатильности;
  2. если цены выходят за пределы полосы, следует ожидать продолжения текущей тенденции;
  3. если за пиками и впадинами за пределами полосы следуют пики и впадины внутри полосы, возможен разворот тенденции;
  4. движение цен, начавшееся от одной из границ полосы, обычно достигает противоположной границы.

Последнее наблюдение полезно при прогнозировании ценовых ориентиров.



Параметры

Для создания хэндла индикатора используется функция iBands():

Возвращает хэндл индикатора Bollinger Bands®.

int  iBands(
   string              symbol,            // имя символа
   ENUM_TIMEFRAMES     period,            // период
   int                 bands_period,      // период для расчета средней линии
   int                 bands_shift,       // смещение индикатора по горизонтали
   double              deviation,         // кол-во стандартных отклонений
   ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
   );

symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

bands_period

[in]  Период усреднения основной линии индикатора.

bands_shift

[in]  Сдвиг индикатора относительно ценового графика.

deviation

[in]  Отклонение от основной линии.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Номера буферов: 0 — BASE_LINE, 1 — UPPER_BAND, 2 — LOWER_BAND

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                               TestTrendBands.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod   =  20;            /* Period         */ // Период расчёта Bollinger Bands
input double               InpDeviation=  2.0;           /* Deviation      */ // Отклонения
input int                  InpShift    =  0;             /* Shift          */ // Сдвиг
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Bollinger Bands
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта Bollinger Bands
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                               TestTrendBands.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod   =  20;            /* Period         */ // Период расчёта Bollinger Bands
input double               InpDeviation=  2.0;           /* Deviation      */ // Отклонения
input int                  InpShift    =  0;             /* Shift          */ // Сдвиг
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Bollinger Bands
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта Bollinger Bands
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 20 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Bands(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iBands(Symbol(),PERIOD_CURRENT,period,InpShift,InpDeviation,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 20 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Bands(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iBands(Symbol(),PERIOD_CURRENT,period,InpShift,InpDeviation,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,225,243);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,110);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,4,2,18,110);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные линии Bands Upper с указанного бара в таблицу 1
   panel.DrawText(ind_title+" Upper", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,UPPER_BAND);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Выводим на панель данные линии Bands Lower с указанного бара в таблицу 1
   panel.DrawText(ind_title+" Lower", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,LOWER_BAND);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Выводим на панель данные линии Bands Middle с указанного бара в таблицу 1
   panel.DrawText(ind_title+" Middle", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   double value_dim=IndicatorValue(handle,index,BASE_LINE);
   string value_dim_str=(value_dim!=EMPTY_VALUE ? DoubleToString(value_dim,ind_digits) : "");
   panel.DrawText(value_dim_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendBands.mq5" можно посмотреть в прикреплённых к статье файлах.


Double Exponential Moving Average

Технический индикатор Двойное Экспоненциальное Скользящее Среднее (Double Exponential Moving Average, DEMA) был разработан Патриком Маллоем (Patrick Mulloy) и опубликован в феврале 1994 в журнале "Technical Analysis of Stocks & Commodities". Он предназначен для сглаживания ценовых серий и накладывается прямо на ценовой график финансового инструмента. Кроме того, он может быть использован для сглаживания значений других индикаторов.

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



Параметры

Для создания хэндла индикатора используется функция iDEMA():

Возвращает хэндл индикатора Double Exponential Moving Average. Всего один буфер.

int  iDEMA(
   string              symbol,            // имя символа
   ENUM_TIMEFRAMES     period,            // период
   int                 ma_period,         // период усреднения
   int                 ma_shift,          // смещение индикатора по горизонтали
   ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
   );

symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

ma_period

[in]  Период(количество баров) для вычисления индикатора.

ma_shift

[in]  Сдвиг индикатора относительно ценового графика.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                                TestTrendDEMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */ // Период расчёта DEMA
input int                  InpShift       =  0;             /* DEMA Shift     */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта DEMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта DEMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                                TestTrendDEMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */ // Период расчёта DEMA
input int                  InpShift       =  0;             /* DEMA Shift     */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта DEMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта DEMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("DEMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iDEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("DEMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iDEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные индикатора с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Выводим описание состояния линии индикатора
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendDEMA.mq5" можно посмотреть в прикреплённых к статье файлах.


Envelopes

Технический индикатор Огибающие Линии (Конверты, Envelopes) образуется двумя скользящими средними, одна из которых смещена вверх, а другая — вниз. Выбор оптимальной относительной величины смещения границ полосы определяется волатильностью рынка: чем она выше — тем больше смещение.

Envelopes определяют верхние и нижние границы нормального диапазона колебаний цен бумаги. Сигнал к продаже возникает тогда, когда цена достигает верхней границы полосы, а сигнал к покупке — при достижении ею нижней границы.

Применение технического индикатора Envelopes основано на естественной логике поведения рынка: когда под давлением особо рьяных покупателей или продавцов цены достигают экстремальных значений (т.е. верхней или нижней границы полосы), они часто стабилизируются, возвращаясь к более реалистичным уровням. Такой же принцип используется при интерпретации Полос Боллинджера (Bollinger Bands®, BB).



Параметры

Для создания хэндла индикатора используется функция iEnvelopes():

Возвращает хэндл индикатора Envelopes.

int  iEnvelopes(
   string              symbol,            // имя символа
   ENUM_TIMEFRAMES     period,            // период
   int                 ma_period,         // период для расчета средней линии
   int                 ma_shift,          // смещение индикатора по горизонтали
   ENUM_MA_METHOD      ma_method,         // тип сглаживания
   ENUM_APPLIED_PRICE  applied_price,     // тип цены или handle
   double              deviation          // отклонение границ от средней линии
   );
symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

ma_period

[in]  Период усреднения основной линии индикатора.

ma_shift

[in]  Сдвиг индикатора относительно ценового графика.

ma_method

[in]  Метод усреднения. Может быть любым из значений значений перечисления ENUM_MA_METHOD.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

deviation

[in]  Отклонение от основной линии в процентах.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Номера буферов: 0 — UPPER_LINE, 1 — LOWER_LINE.


Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                           TestTrendEnvelopes.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod   =  14;            /* Period         */ // Период расчёта Envelopes
input double               InpDeviation=  0.1;           /* Deviation      */ // Отклонения
input int                  InpShift    =  0;             /* Shift          */ // Сдвиг
input ENUM_MA_METHOD       InpMethod   =  MODE_SMA;      /* Method         */ // Метод расчёта Envelopes
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Envelopes
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта Envelopes
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                           TestTrendEnvelopes.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod   =  14;            /* Period         */ // Период расчёта Envelopes
input double               InpDeviation=  0.1;           /* Deviation      */ // Отклонения
input int                  InpShift    =  0;             /* Shift          */ // Сдвиг
input ENUM_MA_METHOD       InpMethod   =  MODE_SMA;      /* Method         */ // Метод расчёта Envelopes
input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Envelopes
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта Envelopes
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Envelopes(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iEnvelopes(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice,InpDeviation);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Envelopes(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iEnvelopes(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice,InpDeviation);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,257,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,126);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,126);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные линии Envelopes Upper с указанного бара в таблицу 1
   panel.DrawText(ind_title+" Upper", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,UPPER_LINE);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);

//--- Выводим на панель данные линии Envelopes Lower с указанного бара в таблицу 1
   panel.DrawText(ind_title+" Lower", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   double value_dip=IndicatorValue(handle,index,LOWER_LINE);
   string value_dip_str=(value_dip!=EMPTY_VALUE ? DoubleToString(value_dip,ind_digits) : "");
   panel.DrawText(value_dip_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,100);

//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendEnvelopes.mq5" можно посмотреть в прикреплённых к статье файлах.


Fractal Adaptive Moving Average

Технический индикатор Фрактальная Адаптивная Скользящая Средняя (Fractal Adaptive Moving Average, FRAMA) был разработан Джоном Эйлерсом (John Ehlers). Данный индикатор строится на основании алгоритма экспоненциальной скользящей средней, в которой фактор сглаживания вычисляется на основании текущей фрактальной размерности ценового ряда. Достоинством индикатора FRAMA является способность следовать за сильными трендовыми движениями и очень сильно замедляться в моменты ценовых консолидаций.

К этому индикатору применимы все виды анализа, которые используются для скользящих средних.



Параметры

Для создания хэндла индикатора используется функция iFrAMA():

Возвращает хэндл индикатора Fractal Adaptive Moving Average. Всего один буфер.

int  iFrAMA(
   string              symbol,            // имя символа
   ENUM_TIMEFRAMES     period,            // период
   int                 ma_period,         // период усреднения
   int                 ma_shift,          // смещение индикатора по горизонтали
   ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
   );
symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

ma_period

[in]  Период(количество баров) для вычисления индикатора.

ma_shift

[in]  Сдвиг индикатора относительно ценового графика.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                               TestTrendFRAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */ // Период расчёта FRAMA
input int                  InpShift       =  0;             /* FRAMA Shift    */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта FRAMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта FRAMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                               TestTrendFRAMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod      =  14;            /* Period         */ // Период расчёта FRAMA
input int                  InpShift       =  0;             /* FRAMA Shift    */ // Сдвиг линии индикатора
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта FRAMA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта FRAMA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("FRAMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iFrAMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 14 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("FRAMA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iFrAMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные индикатора с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Выводим описание состояния линии индикатора
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendFRAMA.mq5" можно посмотреть в прикреплённых к статье файлах.


Ichimoku Kinko Hyo

Технический индикатор Ишимоку Кинко Хайо (Ichimoku Kinko Hyo) предназначен для определения рыночного тренда, уровней поддержки и сопротивления и для генерации сигналов покупки и продажи. Лучше всего индикатор работает на недельных и дневных графиках.

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

  • Tenkan-sen показывает среднее значение цены за первый промежуток времени, определяемый как сумма максимума и минимума за это время, деленная на два;
  • Kijun-sen показывает среднее значение цены за второй промежуток времени;
  • Senkou Span A показывает середину расстояния между предыдущими двумя линиями, сдвинутую вперед на величину второго временного интервала;
  • Senkou Span B показывает среднее значение цены за третий временной интервал, сдвинутое вперед на величину второго временного интервала.

Chikou Span показывает цену закрытия текущей свечи, сдвинутую назад на величину второго временного интервала. Расстояние между линиями Senkou штрихуется на графике другим цветом и называется "облаком". Если цена находится между этими линиями, рынок считается нетрендовым и тогда края облака образуют уровни поддержки и сопротивления.

  • Если цена находится над облаком, то верхняя его линия образует первый уровень поддержки, а вторая — второй уровень поддержки;
  • Если цена находится под облаком, то нижняя линия образует первый уровень сопротивления, а верхняя — второй;
  • Если линия Chikou Span пересекает график цены снизу вверх, это является сигналом к покупке. Если сверху вниз — сигналом к продаже.

Kijun-sen используется как показатель движения рынка. Если цена выше нее, цены, вероятно, будут продолжать расти. Когда цена пересекает эту линию вероятно дальнейшее изменения тренда. Другим вариантом использования Киджун-сен является подача сигналов. Сигнал к покупке генерируется, когда линия Tenkan-sen пересекает Kijun-sen снизу вверх. Сверху вниз — сигнал к продаже. Tenkan-sen используется как индикатор рыночного тренда. Если эта линия растет или падает — тренд существует. Когда она идет горизонтально — рынок вошел в канал.



Параметры

Для создания хэндла индикатора используется функция iIchimoku():

Возвращает хэндл индикатора Ichimoku Kinko Hyo.

int  iIchimoku(
   string           symbol,            // имя символа
   ENUM_TIMEFRAMES  period,            // период
   int              tenkan_sen,        // период Tenkan-sen
   int              kijun_sen,         // период Kijun-sen
   int              senkou_span_b      // период Senkou Span B
   );
symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

tenkan_sen

[in]  Период усреднения Tenkan Sen.

kijun_sen

[in]  Период усреднения Kijun Sen.

senkou_span_b

[in]  Период усреднения Senkou Span B.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Номера буферов: 0 — TENKANSEN_LINE, 1 — KIJUNSEN_LINE, 2 — SENKOUSPANA_LINE, 3 — SENKOUSPANB_LINE, 4 — CHIKOUSPAN_LINE.


Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                            TestTrendIchimoku.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriodTenkan=  9;    /* Tenkan-sen     */ // Период расчёта линии "Tenkan-sen"
input uint                 InpPeriodKijun =  26;   /* Kijun-sen      */ // Период расчёта линии "Kijun-sen"
input uint                 InpPeriodSpanB =  52;   /* Senkou Span B  */ // Период расчёта линии "Senkou Span B"
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period_tenkan=0;        // Период расчёта линии "Tenkan-sen"
int      period_kijun=0;         // Период расчёта линии "Kijun-sen"
int      period_spanb=0;         // Период расчёта линии "Senkou Span B"
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                            TestTrendIchimoku.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriodTenkan=  9;    /* Tenkan-sen     */ // Период расчёта линии "Tenkan-sen"
input uint                 InpPeriodKijun =  26;   /* Kijun-sen      */ // Период расчёта линии "Kijun-sen"
input uint                 InpPeriodSpanB =  52;   /* Senkou Span B  */ // Период расчёта линии "Senkou Span B"
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period_tenkan=0;        // Период расчёта линии "Tenkan-sen"
int      period_kijun=0;         // Период расчёта линии "Kijun-sen"
int      period_spanb=0;         // Период расчёта линии "Senkou Span B"
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта
   period_tenkan=int(InpPeriodTenkan<1 ? 9 : InpPeriodTenkan);
   period_kijun=int(InpPeriodKijun<1 ? 26 : InpPeriodKijun);
   period_spanb=int(InpPeriodSpanB<1 ? 52 : InpPeriodSpanB);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Ichimoku Kinko Hyo (%lu,%lu,%lu)",period_tenkan,period_kijun,period_spanb);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iIchimoku(Symbol(),PERIOD_CURRENT,period_tenkan,period_kijun,period_spanb);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта
   period_tenkan=int(InpPeriodTenkan<1 ? 9 : InpPeriodTenkan);
   period_kijun=int(InpPeriodKijun<1 ? 26 : InpPeriodKijun);
   period_spanb=int(InpPeriodSpanB<1 ? 52 : InpPeriodSpanB);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("Ichimoku Kinko Hyo (%lu,%lu,%lu)",period_tenkan,period_kijun,period_spanb);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iIchimoku(Symbol(),PERIOD_CURRENT,period_tenkan,period_kijun,period_spanb);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаёи панель
   panel=new CDashboard(1,20,20,229,261);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,112);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,5,2,18,112);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Получаем данные буферов индикатора
   double tenkan=IndicatorValue(handle,index,TENKANSEN_LINE);     // Линия Tenkan Sen
   double kijun =IndicatorValue(handle,index,KIJUNSEN_LINE);      // Линия Kijun Sen
   double spana =IndicatorValue(handle,index,SENKOUSPANA_LINE);   // Линия Senkou Span A
   double spanb =IndicatorValue(handle,index,SENKOUSPANB_LINE);   // Линия Senkou Span B
   double chikou=IndicatorValue(handle,index,CHIKOUSPAN_LINE);    // Линия Chikou Span
   color clr=clrNONE;

//--- Выводим на панель данные линии Tenkan Sen с указанного бара в таблицу 1
   string tenkan_str=StringFormat("Tenkan-sen(%lu)",period_tenkan);
   panel.DrawText(tenkan_str, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   string value_str=(tenkan!=EMPTY_VALUE ? DoubleToString(tenkan,ind_digits) : " ");
   ENUM_LINE_STATE state_tenkan=LineStateRelative(handle,index,TENKANSEN_LINE,kijun,IndicatorValue(handle,index+1,KIJUNSEN_LINE));
   clr=(state_tenkan==LINE_STATE_CROSS_UP ? clrBlue : state_tenkan==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clr,100);
   
//--- Выводим на панель данные линии Kijun Sen с указанного бара в таблицу 1
   string kijun_str=StringFormat("Kijun-sen(%lu)",period_kijun);
   panel.DrawText(kijun_str, panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   value_str=(kijun!=EMPTY_VALUE ? DoubleToString(kijun,ind_digits) : " ");
   ENUM_LINE_STATE state_kijun=LineState(handle,index,KIJUNSEN_LINE);
   clr=
     (
      state_kijun==LINE_STATE_UP || state_kijun==LINE_STATE_TURN_UP ? clrBlue : 
      state_kijun==LINE_STATE_DOWN || state_kijun==LINE_STATE_TURN_DOWN ? clrRed : clrNONE
     );
   panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);

//--- Выводим на панель данные линии Senkou Span A с указанного бара в таблицу 1
   panel.DrawText("Senkou Span A", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
   value_str=(spana!=EMPTY_VALUE ? DoubleToString(spana,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,100);
   
//--- Выводим на панель данные линии Senkou Span B с указанного бара в таблицу 1
   string spanb_str=StringFormat("Senkou Span B(%lu)",period_spanb);
   panel.DrawText(spanb_str, panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
   value_str=(spanb!=EMPTY_VALUE ? DoubleToString(spanb,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clrNONE,100);
   
//--- Выводим на панель данные линии Chikou Span с указанного бара в таблицу 1
   panel.DrawText("Chikou Span", panel.CellX(1,4,0)+2, panel.CellY(1,4,0)+2);
   value_str=(chikou!=EMPTY_VALUE ? DoubleToString(chikou,ind_digits) : " ");
   panel.DrawText(value_str,panel.CellX(1,4,1)+2,panel.CellY(1,4,1)+2,clrNONE,100);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Цвет значений линий Tenkan-sen и Kijun-sen зависит от их взаимного расположения и направления.

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendIchimoku.mq5" можно посмотреть в прикреплённых к статье файлах.


Moving Average

Технический индикатор Скользящее Среднее (Moving Average, MA) показывает среднее значение цены инструмента за некоторый период времени. При расчете Moving Average производится математическое усреднение цены инструмента за данный период. По мере изменения цены ее среднее значение либо растет, либо падает.

Существует несколько типов скользящих средних: простое (его также называют арифметическим), экспоненциальное, сглаженное и взвешенное. Moving Average можно рассчитывать для любого последовательного набора данных, включая цены открытия и закрытия, максимальную и минимальную цены, объем торгов или значения других индикаторов. Нередко используются и скользящие средние самих скользящих средних.

Единственное, чем Moving Average разных типов существенно отличаются друг от друга, — это разные весовые коэффициенты, которые присваиваются последним данным. В случае Простого Скользящего Среднего (Simple Moving Average) все цены рассматриваемого периода имеют равный вес. Экспоненциальная и взвешенная скользящие средние (Exponential Moving Average и Linear Weighted Moving Average) делают более весомыми последние цены.

Самый распространенный метод интерпретации скользящего среднего цены состоит в сопоставлении его динамики с динамикой самой цены. Когда цена инструмента поднимается выше значения Moving Average, возникает сигнал к покупке, при ее падении ниже линии индикатора — сигнал к продаже.

Данная система торговли с помощью Moving Average вовсе не предназначена обеспечивать вхождение в рынок строго в его низшей точке, а выход — строго на вершине. Она позволяет действовать в соответствии с текущей тенденцией: покупать вскоре после того, как цены достигли основания, и продавать вскоре после образования вершины.

Скользящие Средние могут применяться также и к индикаторам. При этом интерпретация скользящих средних индикаторов аналогична интерпретации ценовых скользящих средних: если индикатор поднимается выше своего Moving Average, значит, восходящее движение индикатора продолжится, а если индикатор опускается ниже Moving Average, это означает продолжение его нисходящего движения.

Варианты скользящих средних:

  • Simple Moving Average (SMA) — простое скользящее среднее
  • Exponential Moving Average (EMA) — экспоненциальное скользящее среднее
  • Smoothed Moving Average (SMMA) — сглаженное скользящее среднее
  • Linear Weighted Moving Average (LWMA) — линейно-взвешенное скользящее среднее



Параметры

Для создания хэндла индикатора используется функция iMA():

Возвращает хэндл индикатора скользящего среднего. Всего один буфер.

int  iMA(
   string               symbol,            // имя символа
   ENUM_TIMEFRAMES      period,            // период
   int                  ma_period,         // период усреднения
   int                  ma_shift,          // смещение индикатора по горизонтали
   ENUM_MA_METHOD       ma_method,         // тип сглаживания
   ENUM_APPLIED_PRICE   applied_price      // тип цены или handle
   );
symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

ma_period

[in]  Период усреднения для вычисления скользящего среднего.

ma_shift

[in]  Сдвиг индикатора относительно ценового графика.

ma_method

[in]  Метод усреднения. Может быть любым из значений ENUM_MA_METHOD.

applied_price

[in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.


Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                                  TestTrendMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod=  10;            /* Period         */ // Период расчёта MA
input int                  InpShift =  0;             /* MA Shift       */ // Сдвиг линии индикатора
input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ // Метод расчёта MA
input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта MA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта MA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                                  TestTrendMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input uint                 InpPeriod=  10;            /* Period         */ // Период расчёта MA
input int                  InpShift =  0;             /* MA Shift       */ // Сдвиг линии индикатора
input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ // Метод расчёта MA
input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта MA
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта MA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 10 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("MA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем и корректируем при необходимости период расчёта и уровни
   period=int(InpPeriod<1 ? 10 : InpPeriod);
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("MA(%lu)",period);
   ind_digits=Digits()+1;
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные индикатора с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Выводим описание состояния линии индикатора
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

На панели отображаются значения индикатора и состояние его линии.

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendMA.mq5" можно посмотреть в прикреплённых к статье файлах.


Parabolic SAR

Технический индикатор Параболическая Система SAR (Parabolic SAR) был разработан для анализа трендовых рынков. Индикатор строится на ценовом графике. По своему смыслу данный индикатор аналогичен скользящей средней, с той лишь разницей, что Parabolic SAR движется с большим ускорением и может менять положение относительно цены. На "бычьем тренде" (Up Trend) индикатор располагается ниже цен, на "медвежьем тренде" (Down Trend) — выше.

Если цена пересекает линию Parabolic SAR, то происходит разворот индикатора, а следующие его значения располагаются по другую сторону от цены. При этом "перевороте" индикатора, точкой отсчета будет служить максимальная или минимальная цена за предыдущий период. Переворот индикатора — это сигнал либо об окончании (переходе в коррекцию или флэт) тренда, либо об его развороте.

Parabolic SAR превосходно определяет точки выхода из рынка. Длинные позиции следует закрывать, когда цена опускается ниже линии технического индикатора, а короткие — когда цена поднимается выше линии Parabolic SAR. То есть необходимо отслеживать направление движения Parabolic SAR и держать открытыми на рынке позиции только в направлении этого движения. Часто данный индикатор используют в качестве линии скользящего стопа (trailing stop).

Если открыта длинная позиция (то есть цена выше линии Parabolic SAR), то линия индикатора будет перемещаться вверх независимо от того, в каком направлении движутся цены. Величина перемещения линии Parabolic SAR зависит от величины ценового движения.



Параметры

Для создания хэндла индикатора используется функция iSAR():

Возвращает хэндл индикатора Parabolic Stop and Reverse system. Всего один буфер.

int  iSAR(
   string           symbol,      // имя символа
   ENUM_TIMEFRAMES  period,      // период
   double           step,        // шаг изменения цены - коэффициент ускорения
   double           maximum      // максимальный шаг
   );
symbol

[in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

period

[in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

step

[in]  Шаг изменения цены, обычно 0.02.

maximum

[in]  Максимальный шаг, обычно 0.2.

Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

Для создания индикатора в советнике объявим входные и глобальные переменные:

//+------------------------------------------------------------------+
//|                                                 TestTrendSAR.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input double   InpStep  =  0.02; /* Step     */ // Шаг Parabolic SAR
input double   InpMax   =  0.2;  /* Maximum  */ // Максимум Parabolic SAR
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта MA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора

При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

//+------------------------------------------------------------------+
//|                                                 TestTrendSAR.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Неопределённое состояние
   LINE_STATE_UP,          // Направление вверх
   LINE_STATE_DOWN,        // Направление вниз
   LINE_STATE_TURN_UP,     // Разворот вверх
   LINE_STATE_TURN_DOWN,   // Разворот вниз
   LINE_STATE_STOP_UP,     // Остановка направления вверх
   LINE_STATE_STOP_DOWN,   // Остановка направления вниз
   LINE_STATE_ABOVE,       // Над значением
   LINE_STATE_UNDER,       // Под значением
   LINE_STATE_CROSS_UP,    // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW, // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
   LINE_STATE_EQUALS,      // Равно значению
  };
//--- input parameters
input double   InpStep  =  0.02; /* Step     */ // Шаг Parabolic SAR
input double   InpMax   =  0.2;  /* Maximum  */ // Максимум Parabolic SAR
//--- global variables
int      handle=INVALID_HANDLE;  // Хэндл индикатора
int      period=0;               // Период расчёта MA
int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
string   ind_title;              // Описание индикатора
//--- переменные для панели
int      mouse_bar_index;        // Индекс бара, с которого берутся данные
CDashboard *panel=NULL;          // Указатель на объект панели


Инициализация

Установка значений глобальных переменных для индикатора и создание его хэндла:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("SAR(%.2f,%.2f)",InpStep,InpMax);
   ind_digits=Digits();
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iSAR(Symbol(),PERIOD_CURRENT,InpStep,InpMax);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

При использовании в советнике информационной панели создаём панель:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Индикатор
//--- Устанавливаем наименование индикатора и количество знаков после запятой
   ind_title=StringFormat("SAR(%.2f,%.2f)",InpStep,InpMax);
   ind_digits=Digits();
//--- Создаём хэндл индикатора
   ResetLastError();
   handle=iSAR(Symbol(),PERIOD_CURRENT,InpStep,InpMax);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,197,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,96);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,3,2,18,96);
   
//--- Выводим в журнал табличные данные
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }


Деинициализация

В обработчике OnDeinit() советника освобождаем хэндл индикатора:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
  }

При использовании информационной панели удаляем созданный объект панели:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Освобождаем хэндл индикатора
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Очищаем все комментарии на графике
   Comment("");
   
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
  }


Получение данных

Общие функции получения данных по хэндлу индикатора:

//+------------------------------------------------------------------+
//| Возвращает данные индикатора на указанном баре                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора                            |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

При использовании информационной панели данные выводятся на панель при помощи функции:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Если текущие цены получить не удалось - уходим
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим на панель данные индикатора с указанного бара в таблицу 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
   
//--- Выводим заголовок описания состояния линии индикатора
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   
//--- Сохраняем в переменную значение Close на баре index
   double close0=rates[0].close;
//--- Если данные бара по индекс+1 получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index+1,1,rates)!=1)
      return;
//--- Сохраняем в переменную значение Close на баре index+1 и получаем значение индикатора на баре index+1
   double close1=rates[0].close;
   double value1=IndicatorValue(handle,index+1,0);
//--- Получаем и корректируем состояние линии индикатора
   ENUM_LINE_STATE state=LineState(handle,index,0);
   if(value<close0 && value1>close1)
      state=LINE_STATE_TURN_UP;
   if(state==LINE_STATE_STOP_DOWN)
      state=LINE_STATE_UP;
   if(value>close0 && value1<close1)
      state=LINE_STATE_TURN_DOWN;
   if(state==LINE_STATE_STOP_UP)
      state=LINE_STATE_DOWN;
//--- Выводим описание состояния линии индикатора
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


Файл тестового советника "TestTrendSAR.mq5" можно посмотреть в прикреплённых к статье файлах.


Standard Deviation

Технический индикатор Стандартное отклонение (Standard Deviation, StdDev) измеряет волатильность рынка. Этот индикатор характеризует размер колебаний цены относительно скользящего среднего. Так, если значение индикатора велико, то рынок является волатильным и цены баров достаточно разбросаны относительно скользящего среднего. Если значение индикатора невелико, рынок характеризуется низкой волатильностью и цены баров достаточно близки к скользящему среднему.

Обычно этот индикатор используется как составная часть других индикаторов. Так, при расчете Bollinger Bands® значение стандартного отклонения инструмента прибавляется к его скользящему среднему.

Динамика рынка состоит в последовательном чередовании периодов покоя и всплесков активности, поэтому подход к данному индикатору прост:

  • если значение индикатора слишком мало, то есть рынок в полном покое, то имеет смысл ожидать скорого всплеска активности;
  • напротив, если индикатор экстремально велик, значит, скорее всего, эта активность скоро пойдет на убыль.


    Параметры

    Для создания хэндла индикатора используется функция iStdDev():

    Возвращает хэндл индикатора Standard Deviation. Всего один буфер.

    int  iStdDev(
       string              symbol,            // имя символа
       ENUM_TIMEFRAMES     period,            // период
       int                 ma_period,         // период усреднения
       int                 ma_shift,          // смещение индикатора по горизонтали
       ENUM_MA_METHOD      ma_method,         // тип сглаживания
       ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
       );
    symbol

    [in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

    period

    [in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

    ma_period

    [in]  Период усреднения для вычисления индикатора.

    ma_shift

    [in]  Сдвиг индикатора относительно ценового графика.

    ma_method

    [in]  Метод усреднения. Может быть любым из значений ENUM_MA_METHOD.

    applied_price

    [in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

    Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

    Для создания индикатора в советнике объявим входные и глобальные переменные:

    //+------------------------------------------------------------------+
    //|                                              TestTrendStdDev.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriod=  20;            /* Period         */ // Период расчёта Standard Deviation
    input int                  InpShift =  0;             /* StdDev Shift   */ // Сдвиг линии индикатора
    input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ // Метод расчёта Standard Deviation
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Standard Deviation
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period=0;               // Период расчёта Standard Deviation
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    

    При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

    //+------------------------------------------------------------------+
    //|                                              TestTrendStdDev.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriod=  20;            /* Period         */ // Период расчёта Standard Deviation
    input int                  InpShift =  0;             /* StdDev Shift   */ // Сдвиг линии индикатора
    input ENUM_MA_METHOD       InpMethod=  MODE_SMA;      /* Method         */ // Метод расчёта Standard Deviation
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Standard Deviation
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period=0;               // Период расчёта Standard Deviation
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    //--- переменные для панели
    int      mouse_bar_index;        // Индекс бара, с которого берутся данные
    CDashboard *panel=NULL;          // Указатель на объект панели
    


    Инициализация

    Установка значений глобальных переменных для индикатора и создание его хэндла:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period=int(InpPeriod<1 ? 20 : InpPeriod<2 ? 2 : InpPeriod);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("StdDev(%lu)",period);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iStdDev(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    

    При использовании в советнике информационной панели создаём панель:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period=int(InpPeriod<1 ? 20 : InpPeriod<2 ? 2 : InpPeriod);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("StdDev(%lu)",period);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iStdDev(Symbol(),PERIOD_CURRENT,period,InpShift,InpMethod,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Панель
    //--- Создаём панель
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Устанавливаем параметры шрифта
       panel.SetFontParams("Calibri",9);
    //--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
       panel.CreateNewTable(0);
    //--- Рисуем таблицу с идентификатором 0 на фоне панели
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
       panel.CreateNewTable(1);
    //--- Получаем координату Y2 таблицы с идентификатором 0 и
    //--- устанавливаем координату Y1 для таблицы с идентификатором 1
       int y1=panel.TableY2(0)+22;
    //--- Рисуем таблицу с идентификатором 1 на фоне панели
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Выводим в журнал табличные данные
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Инициализируем переменную с индексом бара указателя мышки
       mouse_bar_index=0;
    //--- Выводим на панель данные текущего бара
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    


    Деинициализация

    В обработчике OnDeinit() советника освобождаем хэндл индикатора:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
      }
    

    При использовании информационной панели удаляем созданный объект панели:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
       
    //--- Если объект панели существует - удаляем его
       if(panel!=NULL)
          delete panel;
      }
    


    Получение данных

    Общие функции получения данных по хэндлу индикатора:

    //+------------------------------------------------------------------+
    //| Возвращает данные индикатора на указанном баре                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии индикатора                            |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Разворот линии вверх (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Направление линии вверх (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Остановка направления линии вверх (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Разворот линии вниз (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Направление линии вниз (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Остановка направления линии вниз (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии относительно указанного уровня        |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Определяем второй сравниваемый уровень
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- Линия находится под уровнем (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- Линия находится над уровнем (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия равна значению уровня (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание состояния линии индикатора                   |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    

    При использовании информационной панели данные выводятся на панель при помощи функции:

    //+------------------------------------------------------------------+
    //| Выводит данные с указанного индекса таймсерии на панель          |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Объявляем переменные для получения в них данных
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Если текущие цены получить не удалось - уходим
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Если данные бара по указанному индексу получить не удалось - уходим
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Устанавливаем параметры шрифта для данных бара и индикатора
       panel.SetFontParams(name,9);
    
    //--- Выводим на панель данные указанного бара в таблицу 0
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Выводим на панель данные индикатора с указанного бара в таблицу 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Выводим описание состояния линии индикатора
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Перерисовываем график для немедленного отображения всех изменений на панели
       ChartRedraw(ChartID());
      }
    

    На панель выводится значение линии индикатора и её состояние.

    Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Работа с панелью
    //--- Вызываем обработчик событий панели
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- Если курсор перемещается или щелчок по графику
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Объявляем переменные для записи в них координат времени и цены
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- Если координаты курсора преобразованы в дату и время
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- записываем индекс бара, где расположен курсор в глобальную переменную
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Выводим данные бара под курсором на панель
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- Если получили пользовательское событие - выводим об этом сообщение в журнал
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    

    После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


    Файл тестового советника "TestTrendStdDev.mq5" можно посмотреть в прикреплённых к статье файлах.


    Triple Exponential Moving Average

    Технический индикатор Triple Exponential Moving Average (TEMA) был разработан Патриком Маллоем и опубликован в журнале Technical Analysis of Stocks & Commodities. Принцип его расчета такой же, как и индикатора DEMA (Double Exponential Moving Average). Название тройное экспоненциальное сглаживание не совсем корректно отображает заложенный в нем алгоритм. Это — уникальная смесь однократного, двойного и тройного экспоненциального скользящего среднего, обеспечивающая меньшую задержку, нежели каждый из них по-отдельности.

    Индикатор TEMA может быть использован вместо традиционных скользящих средних. Он может быть использован для сглаживания ценовых данных и для сглаживания других индикаторов.



    Параметры

    Для создания хэндла индикатора используется функция iTEMA():

    Возвращает хэндл индикатора Triple Exponential Moving Average. Всего один буфер.

    int  iTEMA(
       string              symbol,            // имя символа
       ENUM_TIMEFRAMES     period,            // период
       int                 ma_period,         // период усреднения
       int                 ma_shift,          // смещение индикатора по горизонтали
       ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
       );

    symbol

    [in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

    period

    [in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

    ma_period

    [in]  Период(количество баров) для вычисления индикатора.

    ma_shift

    [in]  Сдвиг индикатора относительно ценового графика.

    applied_price

    [in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

    Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

    Для создания индикатора в советнике объявим входные и глобальные переменные:

    //+------------------------------------------------------------------+
    //|                                                TestTrendTEMA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriod=  14;            /* Period         */ // Период расчёта Triple Exponential Moving Average
    input int                  InpShift =  0;             /* TEMA Shift     */ // Сдвиг линии индикатора
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Triple Exponential Moving Average
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period=0;               // Период расчёта Triple Exponential Moving Average
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    

    При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

    //+------------------------------------------------------------------+
    //|                                                TestTrendTEMA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriod=  14;            /* Period         */ // Период расчёта Triple Exponential Moving Average
    input int                  InpShift =  0;             /* StdDev Shift   */ // Сдвиг линии индикатора
    input ENUM_APPLIED_PRICE   InpPrice =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Triple Exponential Moving Average
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period=0;               // Период расчёта Triple Exponential Moving Average
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    //--- переменные для панели
    int      mouse_bar_index;        // Индекс бара, с которого берутся данные
    CDashboard *panel=NULL;          // Указатель на объект панели
    


    Инициализация

    Установка значений глобальных переменных для индикатора и создание его хэндла:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period=int(InpPeriod<1 ? 14 : InpPeriod);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("TEMA(%lu)",period);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iTEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    

    При использовании в советнике информационной панели создаём панель:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period=int(InpPeriod<1 ? 14 : InpPeriod);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("TEMA(%lu)",period);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iTEMA(Symbol(),PERIOD_CURRENT,period,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Панель
    //--- Создаём панель
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Устанавливаем параметры шрифта
       panel.SetFontParams("Calibri",9);
    //--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
       panel.CreateNewTable(0);
    //--- Рисуем таблицу с идентификатором 0 на фоне панели
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
       panel.CreateNewTable(1);
    //--- Получаем координату Y2 таблицы с идентификатором 0 и
    //--- устанавливаем координату Y1 для таблицы с идентификатором 1
       int y1=panel.TableY2(0)+22;
    //--- Рисуем таблицу с идентификатором 1 на фоне панели
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Выводим в журнал табличные данные
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Инициализируем переменную с индексом бара указателя мышки
       mouse_bar_index=0;
    //--- Выводим на панель данные текущего бара
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    


    Деинициализация

    В обработчике OnDeinit() советника освобождаем хэндл индикатора:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
      }
    

    При использовании информационной панели удаляем созданный объект панели:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
       
    //--- Если объект панели существует - удаляем его
       if(panel!=NULL)
          delete panel;
      }
    


    Получение данных

    Общие функции получения данных по хэндлу индикатора:

    //+------------------------------------------------------------------+
    //| Возвращает данные индикатора на указанном баре                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии индикатора                            |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Разворот линии вверх (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Направление линии вверх (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Остановка направления линии вверх (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Разворот линии вниз (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Направление линии вниз (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Остановка направления линии вниз (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии относительно указанного уровня        |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Определяем второй сравниваемый уровень
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- Линия находится под уровнем (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- Линия находится над уровнем (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия равна значению уровня (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание состояния линии индикатора                   |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    

    При использовании информационной панели данные выводятся на панель при помощи функции:

    //+------------------------------------------------------------------+
    //| Выводит данные с указанного индекса таймсерии на панель          |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Объявляем переменные для получения в них данных
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Если текущие цены получить не удалось - уходим
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Если данные бара по указанному индексу получить не удалось - уходим
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Устанавливаем параметры шрифта для данных бара и индикатора
       panel.SetFontParams(name,9);
    
    //--- Выводим на панель данные указанного бара в таблицу 0
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Выводим на панель данные индикатора с указанного бара в таблицу 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Выводим описание состояния линии индикатора
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Перерисовываем график для немедленного отображения всех изменений на панели
       ChartRedraw(ChartID());
      }
    

    На панель выводятся значения линии индикатора и её состояние.

    Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Работа с панелью
    //--- Вызываем обработчик событий панели
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- Если курсор перемещается или щелчок по графику
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Объявляем переменные для записи в них координат времени и цены
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- Если координаты курсора преобразованы в дату и время
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- записываем индекс бара, где расположен курсор в глобальную переменную
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Выводим данные бара под курсором на панель
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- Если получили пользовательское событие - выводим об этом сообщение в журнал
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    

    После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


    Файл тестового советника "TestTrendTEMA.mq5" можно посмотреть в прикреплённых к статье файлах.


    Variable Index Dynamic Average

    Технический индикатор Скользящая Средняя с Динамическим Периодом Усреднения (Variable Index Dynamic Average, VIDYA) был разработан Тушаром Чендом (Tushar  Chande) и представляет собой оригинальную методику расчета экспоненциальной скользящей средней (EMA) с динамически меняющимся периодом усреднения. Период усреднения зависит от волатильности рынка, в качестве меры волатильности был выбран осциллятор Chande Momentum Oscillator (CMO). Данный осциллятор измеряет отношение между суммой положительных приращений и суммой отрицательных приращений за определенный период (период CMO). Значение CMO используется в качестве коэффициента к сглаживающему фактору EMA. Таким образом, индикатор VIDYA имеет два параметра настройки: период осциллятора CMO и период сглаживания экспоненциальной скользящей средней (период EMA).



    Параметры

    Для создания хэндла индикатора используется функция iVIDyA():

    Возвращает хэндл индикатора Variable Index Dynamic Average. Всего один буфер.

    int  iVIDyA(
       string              symbol,            // имя символа
       ENUM_TIMEFRAMES     period,            // период
       int                 cmo_period,        // период Chande Momentum
       int                 ema_period,        // период фактора сглаживания
       int                 ma_shift,          // смещение индикатора по горизонтали
       ENUM_APPLIED_PRICE  applied_price      // тип цены или handle
       );

    symbol

    [in]  Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

    period

    [in]  Значение периода может быть одним из значений перечисления ENUM_TIMEFRAMES, 0 означает текущий таймфрейм.

    cmo_period

    [in]  Период(количество баров) для вычисления Chande Momentum Oscillator.

    ema_period

    [in]  Период(количество баров) EMA для вычисления фактора сглаживания.

    ma_shift

    [in]  Сдвиг индикатора относительно ценового графика.

    applied_price

    [in]  Используемая цена. Может быть любой из ценовых констант ENUM_APPLIED_PRICE или хендлом другого индикатора.

    Возвращает хэндл указанного технического индикатора, в случае неудачи возвращает INVALID_HANDLE. Для освобождения памяти компьютера от неиспользуемого больше индикатора служит функция IndicatorRelease(), которой передается хэндл этого индикатора.

    Для создания индикатора в советнике объявим входные и глобальные переменные:

    //+------------------------------------------------------------------+
    //|                                               TestTrendVIDYA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriodCMO=  9;             /* CMO Period     */ // Период расчёта CMO
    input int                  InpShift    =  0;             /* VIDYA Shift    */ // Сдвиг линии индикатора
    input uint                 InpPeriodEMA=  12;            /* EMA Period     */ // Период расчёта EMA
    input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Variable Index Dynamic Average
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period_cmo=0;           // Период расчёта CMO
    int      period_ema=0;           // Период расчёта EMA
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    

    При использовании информационной панели в советнике для неё тоже необходимо объявить глобальные переменные и подключить файл классов панели:

    //+------------------------------------------------------------------+
    //|                                               TestTrendVIDYA.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Неопределённое состояние
       LINE_STATE_UP,          // Направление вверх
       LINE_STATE_DOWN,        // Направление вниз
       LINE_STATE_TURN_UP,     // Разворот вверх
       LINE_STATE_TURN_DOWN,   // Разворот вниз
       LINE_STATE_STOP_UP,     // Остановка направления вверх
       LINE_STATE_STOP_DOWN,   // Остановка направления вниз
       LINE_STATE_ABOVE,       // Над значением
       LINE_STATE_UNDER,       // Под значением
       LINE_STATE_CROSS_UP,    // Пересечение значения вверх
       LINE_STATE_CROSS_DOWN,  // Пересечение значения вниз
       LINE_STATE_TOUCH_BELOW, // Касание значения снизу
       LINE_STATE_TOUCH_ABOVE, // Касание значения сверху
       LINE_STATE_EQUALS,      // Равно значению
      };
    //--- input parameters
    input uint                 InpPeriodCMO=  9;             /* CMO Period     */ // Период расчёта CMO
    input int                  InpShift    =  0;             /* VIDYA Shift    */ // Сдвиг линии индикатора
    input uint                 InpPeriodEMA=  12;            /* EMA Period     */ // Период расчёта EMA
    input ENUM_APPLIED_PRICE   InpPrice    =  PRICE_CLOSE;   /* Applied Price  */ // Цена расчёта Variable Index Dynamic Average
    //--- global variables
    int      handle=INVALID_HANDLE;  // Хэндл индикатора
    int      period_cmo=0;           // Период расчёта CMO
    int      period_ema=0;           // Период расчёта EMA
    int      ind_digits=0;           // Количество знаков после запятой в значении индикатора
    string   ind_title;              // Описание индикатора
    //--- переменные для панели
    int      mouse_bar_index;        // Индекс бара, с которого берутся данные
    CDashboard *panel=NULL;          // Указатель на объект панели
    


    Инициализация

    Установка значений глобальных переменных для индикатора и создание его хэндла:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period_cmo=int(InpPeriodCMO<1 ? 9 : InpPeriodCMO);
       period_ema=int(InpPeriodEMA<1 ? 12 : InpPeriodEMA);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("VIDYA(%lu,%lu)",period_cmo,period_ema);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iVIDyA(Symbol(),PERIOD_CURRENT,period_cmo,period_ema,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    

    При использовании в советнике информационной панели создаём панель:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Индикатор
    //--- Устанавливаем и корректируем при необходимости период расчёта и уровни
       period_cmo=int(InpPeriodCMO<1 ? 9 : InpPeriodCMO);
       period_ema=int(InpPeriodEMA<1 ? 12 : InpPeriodEMA);
    //--- Устанавливаем наименование индикатора и количество знаков после запятой
       ind_title=StringFormat("VIDYA(%lu,%lu)",period_cmo,period_ema);
       ind_digits=Digits()+1;
    //--- Создаём хэндл индикатора
       ResetLastError();
       handle=iVIDyA(Symbol(),PERIOD_CURRENT,period_cmo,period_ema,InpShift,InpPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Панель
    //--- Создаём панель
       panel=new CDashboard(1,20,20,197,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Устанавливаем параметры шрифта
       panel.SetFontParams("Calibri",9);
    //--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
       panel.CreateNewTable(0);
    //--- Рисуем таблицу с идентификатором 0 на фоне панели
       panel.DrawGrid(0,2,20,6,2,18,96);
    
    //--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора
       panel.CreateNewTable(1);
    //--- Получаем координату Y2 таблицы с идентификатором 0 и
    //--- устанавливаем координату Y1 для таблицы с идентификатором 1
       int y1=panel.TableY2(0)+22;
    //--- Рисуем таблицу с идентификатором 1 на фоне панели
       panel.DrawGrid(1,2,y1,3,2,18,96);
       
    //--- Выводим в журнал табличные данные
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Инициализируем переменную с индексом бара указателя мышки
       mouse_bar_index=0;
    //--- Выводим на панель данные текущего бара
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Успешная инициализация
       return(INIT_SUCCEEDED);
      }
    


    Деинициализация

    В обработчике OnDeinit() советника освобождаем хэндл индикатора:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
      }
    

    При использовании информационной панели удаляем созданный объект панели:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Освобождаем хэндл индикатора
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Очищаем все комментарии на графике
       Comment("");
       
    //--- Если объект панели существует - удаляем его
       if(panel!=NULL)
          delete panel;
      }
    


    Получение данных

    Общие функции получения данных по хэндлу индикатора:

    //+------------------------------------------------------------------+
    //| Возвращает данные индикатора на указанном баре                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии индикатора                            |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Разворот линии вверх (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Направление линии вверх (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Остановка направления линии вверх (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Разворот линии вниз (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Направление линии вниз (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Остановка направления линии вниз (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает состояние линии относительно указанного уровня        |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Определяем второй сравниваемый уровень
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- Линия находится под уровнем (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- Линия находится над уровнем (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Линия равна значению уровня (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Неопределённое состояние
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание состояния линии индикатора                   |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    

    При использовании информационной панели данные выводятся на панель при помощи функции:

    //+------------------------------------------------------------------+
    //| Выводит данные с указанного индекса таймсерии на панель          |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Объявляем переменные для получения в них данных
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Если текущие цены получить не удалось - уходим
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Если данные бара по указанному индексу получить не удалось - уходим
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Устанавливаем параметры шрифта для данных бара и индикатора
       panel.SetFontParams(name,9);
    
    //--- Выводим на панель данные указанного бара в таблицу 0
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Выводим на панель данные индикатора с указанного бара в таблицу 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Выводим описание состояния линии индикатора
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Перерисовываем график для немедленного отображения всех изменений на панели
       ChartRedraw(ChartID());
      }
    

    На панель выводятся значения линии индикатора и её состояние.

    Кроме того, при использовании информационной панели в обработчике событий OnChartEvent() советника вызывается обработчик событий панели и обрабатываются события для получения индекса бара под курсором:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Работа с панелью
    //--- Вызываем обработчик событий панели
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- Если курсор перемещается или щелчок по графику
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Объявляем переменные для записи в них координат времени и цены
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- Если координаты курсора преобразованы в дату и время
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- записываем индекс бара, где расположен курсор в глобальную переменную
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Выводим данные бара под курсором на панель
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- Если получили пользовательское событие - выводим об этом сообщение в журнал
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    

    После компиляции и запуска советника на графике мы можем контролировать значение и состояние линии индикатора на информационной панели:


    Файл тестового советника "TestTrendVIDYA.mq5" можно посмотреть в прикреплённых к статье файлах.


    Заключение

    Мы рассмотрели все типы технических индикаторов, имеющихся в терминале MetaTrader 5, их подключение к индикаторам и советникам и получение данных. Остаётся открытым вопрос использования в советниках и индикаторах мультисимвольных мультипериодных индикаторов. Следующая статья будет посвящена созданию инструментария для подключения к советникам и индикаторам мультисимвольных мультипериодных индикаторов, получение от них данных и сигналов.

    Все представленные выше коды можно просто скопировать к себе "как есть" прямо из статьи и использовать в своих разработках. Со всеми представленными тестовыми советниками можно ознакомиться в прикреплённых к статье файлах.


    Прикрепленные файлы |
    TestTrendADX.mq5 (33.77 KB)
    TestTrendAMA.mq5 (32.96 KB)
    TestTrendBands.mq5 (33.26 KB)
    TestTrendDEMA.mq5 (31.94 KB)
    TestTrendFRAMA.mq5 (31.95 KB)
    TestTrendMA.mq5 (32.09 KB)
    TestTrendSAR.mq5 (32.97 KB)
    TestTrendStdDev.mq5 (32.26 KB)
    TestTrendTEMA.mq5 (32.07 KB)
    TestTrendVIDYA.mq5 (32.47 KB)
    Dashboard.mqh (218.88 KB)
    Разработка системы репликации - Моделирование рынка (Часть 14): Появление СИМУЛЯТОРА (IV) Разработка системы репликации - Моделирование рынка (Часть 14): Появление СИМУЛЯТОРА (IV)
    В этой статье мы продолжим этап разработки симулятора. Однако сейчас мы увидим, как эффективно создать движение типа «СЛУЧАЙНОЕ БЛУЖДАНИЕ». Этот тип движения весьма интригующий, поскольку служит основой всего, что происходит на рынке капитала. Кроме того, мы начнем понимать некоторые концепции, основополагающие для тех, кто проводит анализ рынка.
    Разработка системы репликации - Моделирование рынка (Часть 13): Появление СИМУЛЯТОРА (III) Разработка системы репликации - Моделирование рынка (Часть 13): Появление СИМУЛЯТОРА (III)
    Здесь мы немного упростим несколько элементов, связанных с работой в следующей статье. Я также объясню, как можно визуализировать то, что генерирует симулятор с точки зрения случайности.
    Разработка пользовательского индикатора Heiken Ashi с помощью MQL5 Разработка пользовательского индикатора Heiken Ashi с помощью MQL5
    В этой статье мы узнаем, как создать собственный индикатор с использованием MQL5 на основе наших предпочтений, который будет использоваться в MetaTrader 5 для интерпретации графиков или применяться в составе советников.
    Разработка системы репликации - Моделирование рынка (Часть 12): Появление СИМУЛЯТОРА (II) Разработка системы репликации - Моделирование рынка (Часть 12): Появление СИМУЛЯТОРА (II)
    Разработка симулятора может оказаться гораздо интереснее, чем кажется. Сегодня мы сделаем еще несколько шагов в этом направлении, потому что всё становится интереснее.