2026-03-21 20:33:14 +07:00
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:28:34 +07:00
|
|
|
//| Article-14105-MQL5-Multi-Symbol-Multi-Period-Draw-Arrow.mq5 |
|
|
|
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
#property indicator_chart_window
|
|
|
|
|
#property indicator_buffers 4
|
|
|
|
|
#property indicator_plots 4
|
|
|
|
|
//--- enums
|
|
|
|
|
|
|
|
|
|
//--- plot FractalsUp1
|
|
|
|
|
#property indicator_label1 "FractalsUp1"
|
|
|
|
|
#property indicator_type1 DRAW_ARROW
|
|
|
|
|
#property indicator_color1 clrGray
|
|
|
|
|
#property indicator_width1 1
|
|
|
|
|
|
|
|
|
|
//--- plot FractalsDown1
|
|
|
|
|
#property indicator_label2 "FractalsDown1"
|
|
|
|
|
#property indicator_type2 DRAW_ARROW
|
|
|
|
|
#property indicator_color2 clrGray
|
|
|
|
|
#property indicator_width2 1
|
|
|
|
|
|
|
|
|
|
//--- plot FractalsUp2
|
|
|
|
|
#property indicator_label3 "FractalsUp2"
|
|
|
|
|
#property indicator_type3 DRAW_ARROW
|
|
|
|
|
#property indicator_color3 clrDodgerBlue
|
|
|
|
|
#property indicator_width3 1
|
|
|
|
|
|
|
|
|
|
//--- plot FractalsDown2
|
|
|
|
|
#property indicator_label4 "FractalsDown2"
|
|
|
|
|
#property indicator_type4 DRAW_ARROW
|
|
|
|
|
#property indicator_color4 clrDodgerBlue
|
|
|
|
|
#property indicator_width4 1
|
|
|
|
|
|
|
|
|
|
//--- includes
|
2026-03-21 20:38:13 +07:00
|
|
|
#include "IndMSTF.mqh"
|
|
|
|
|
#include "Dashboard.mqh"
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- input parameters
|
|
|
|
|
input string InpSymbol = NULL; /* Symbol */ // Символ скользящей средней
|
|
|
|
|
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; /* Timeframe */ // Таймфрейм скользящей средней
|
|
|
|
|
input uchar InpArrowShift1 = 10; /* Senior period Arrow VShift */ // Смещение стрелок по вертикали для старшего периода
|
|
|
|
|
input uchar InpArrowShift2 = 10; /* Junior period Arrow VShift */ // Смещение стрелок по вертикали для младшего периода
|
|
|
|
|
input bool InpAsSeries = true; /* As Series flag */ // Флаг серийности массивов буферов индикатора
|
|
|
|
|
|
|
|
|
|
//--- indicator buffers
|
|
|
|
|
double BufferFractalsUp1[];
|
|
|
|
|
double BufferFractalsDn1[];
|
|
|
|
|
double BufferFractalsUp2[];
|
|
|
|
|
double BufferFractalsDn2[];
|
|
|
|
|
//--- global variables
|
|
|
|
|
int handle_fractals1;
|
|
|
|
|
int handle_fractals2;
|
|
|
|
|
CMSTFIndicators indicators; // Экземпляр объекта коллекции индикаторов
|
|
|
|
|
//--- переменные для панели
|
|
|
|
|
CDashboard *panel=NULL; // Указатель на объект панели
|
|
|
|
|
int mouse_bar_index; // Индекс бара, с которого берутся данные
|
2026-03-21 20:28:34 +07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Custom indicator initialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
|
|
|
|
{
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- Устанавливаем таймер с периодичностью в 1 секунду
|
|
|
|
|
EventSetTimer(1);
|
|
|
|
|
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferFractalsUp1 и BufferFractalsDn1 соответственно
|
|
|
|
|
SetIndexBuffer(0,BufferFractalsUp1,INDICATOR_DATA);
|
|
|
|
|
SetIndexBuffer(1,BufferFractalsDn1,INDICATOR_DATA);
|
|
|
|
|
//--- Назначаем рисуемым буферам 2 и 3 массивы BufferFractalsUp2 и BufferFractalsDn2 соответственно
|
|
|
|
|
SetIndexBuffer(2,BufferFractalsUp2,INDICATOR_DATA);
|
|
|
|
|
SetIndexBuffer(3,BufferFractalsDn2,INDICATOR_DATA);
|
|
|
|
|
//--- Зададём код символа из шрифта Wingdings для отрисовки в PLOT_ARROW
|
|
|
|
|
PlotIndexSetInteger(0,PLOT_ARROW,217);
|
|
|
|
|
PlotIndexSetInteger(1,PLOT_ARROW,218);
|
|
|
|
|
PlotIndexSetInteger(2,PLOT_ARROW,217);
|
|
|
|
|
PlotIndexSetInteger(3,PLOT_ARROW,218);
|
|
|
|
|
//--- Устанавливаем смещение стрелок по вертикали
|
|
|
|
|
PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,-InpArrowShift1);
|
|
|
|
|
PlotIndexSetInteger(1,PLOT_ARROW_SHIFT, InpArrowShift1);
|
|
|
|
|
PlotIndexSetInteger(2,PLOT_ARROW_SHIFT,-InpArrowShift2);
|
|
|
|
|
PlotIndexSetInteger(3,PLOT_ARROW_SHIFT, InpArrowShift2);
|
|
|
|
|
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
|
|
|
|
|
ArraySetAsSeries(BufferFractalsUp1,InpAsSeries);
|
|
|
|
|
ArraySetAsSeries(BufferFractalsDn1,InpAsSeries);
|
|
|
|
|
ArraySetAsSeries(BufferFractalsUp2,InpAsSeries);
|
|
|
|
|
ArraySetAsSeries(BufferFractalsDn2,InpAsSeries);
|
|
|
|
|
|
|
|
|
|
//--- Создаём два индикатора одного типа
|
|
|
|
|
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
|
|
|
|
|
handle_fractals1=indicators.AddNewFractals(NULL,PERIOD_CURRENT);
|
|
|
|
|
handle_fractals2=indicators.AddNewFractals(InpSymbol,InpTimeframe);
|
|
|
|
|
|
|
|
|
|
//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
|
|
|
|
|
if(handle_fractals1==INVALID_HANDLE || handle_fractals2==INVALID_HANDLE)
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
|
|
|
|
|
indicators.SetPlotLabelFromBuffer(0,handle_fractals1,0);
|
|
|
|
|
indicators.SetPlotLabelFromBuffer(1,handle_fractals1,1);
|
|
|
|
|
indicators.SetPlotLabelFromBuffer(2,handle_fractals2,0);
|
|
|
|
|
indicators.SetPlotLabelFromBuffer(3,handle_fractals2,1);
|
|
|
|
|
|
|
|
|
|
//--- Панель
|
|
|
|
|
//--- Создаём панель
|
|
|
|
|
int width=311;
|
|
|
|
|
panel=new CDashboard(1,20,20,width,264);
|
|
|
|
|
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,width/2-2);
|
|
|
|
|
|
|
|
|
|
//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
|
|
|
|
|
panel.CreateNewTable(1);
|
|
|
|
|
//--- Получаем координату Y2 таблицы с идентификатором 0 и
|
|
|
|
|
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
|
|
|
|
|
int y1=panel.TableY2(0)+22;
|
|
|
|
|
//--- Рисуем таблицу с идентификатором 1 на фоне панели
|
|
|
|
|
panel.DrawGrid(1,2,y1,2,2,18,width/2-2);
|
|
|
|
|
|
|
|
|
|
//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
|
|
|
|
|
panel.CreateNewTable(2);
|
|
|
|
|
//--- Получаем координату Y2 таблицы с идентификатором 1 и
|
|
|
|
|
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
|
|
|
|
|
int y2=panel.TableY2(1)+3;
|
|
|
|
|
//--- Рисуем таблицу с идентификатором 2 на фоне панели
|
|
|
|
|
panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
|
2026-03-21 20:28:34 +07:00
|
|
|
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- Инициализируем переменную с индексом бара указателя мышки
|
|
|
|
|
mouse_bar_index=0;
|
|
|
|
|
//--- Выводим на панель данные текущего бара
|
|
|
|
|
DrawData(mouse_bar_index,TimeCurrent());
|
|
|
|
|
|
|
|
|
|
//--- Успешная инициализация
|
2026-03-21 20:28:34 +07:00
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
//| Custom indicator deinitialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnDeinit(const int reason)
|
|
|
|
|
{
|
|
|
|
|
//--- Уничтожаем таймер
|
|
|
|
|
EventKillTimer();
|
|
|
|
|
//--- Если объект панели существует - удаляем его
|
|
|
|
|
if(panel!=NULL)
|
|
|
|
|
delete panel;
|
|
|
|
|
//--- Стираем все комментарии
|
|
|
|
|
Comment("");
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:28:34 +07:00
|
|
|
//| Custom indicator iteration function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
int OnCalculate(const int rates_total,
|
|
|
|
|
const int prev_calculated,
|
2026-03-21 20:28:34 +07:00
|
|
|
const datetime &time[],
|
|
|
|
|
const double &open[],
|
|
|
|
|
const double &high[],
|
|
|
|
|
const double &low[],
|
|
|
|
|
const double &close[],
|
|
|
|
|
const long &tick_volume[],
|
|
|
|
|
const long &volume[],
|
2026-03-21 20:33:14 +07:00
|
|
|
const int &spread[])
|
2026-03-21 20:28:34 +07:00
|
|
|
{
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- Количество баров для расчёта
|
|
|
|
|
int limit=rates_total-prev_calculated;
|
|
|
|
|
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
|
|
|
|
|
if(limit>1)
|
|
|
|
|
{
|
|
|
|
|
//--- указываем для просчёта всю доступную историю
|
|
|
|
|
limit=rates_total-1;
|
|
|
|
|
/*
|
|
|
|
|
// Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
|
|
|
|
|
// то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
|
|
|
|
|
if(!indicators.Calculate())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
|
|
|
|
|
DrawData(mouse_bar_index,time[mouse_bar_index]);
|
|
|
|
|
|
|
|
|
|
//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
|
|
|
|
|
if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_fractals1,0,0,limit,BufferFractalsUp1))
|
|
|
|
|
return 0;
|
|
|
|
|
if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_fractals1,1,0,limit,BufferFractalsDn1))
|
|
|
|
|
return 0;
|
|
|
|
|
if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_fractals2,0,0,limit,BufferFractalsUp2))
|
|
|
|
|
return 0;
|
|
|
|
|
if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_fractals2,1,0,limit,BufferFractalsDn2))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2026-03-21 20:28:34 +07:00
|
|
|
//--- return value of prev_calculated for next call
|
|
|
|
|
return(rates_total);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
//| Timer function |
|
2026-03-21 20:28:34 +07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTimer()
|
|
|
|
|
{
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- Вызываем таймер коллекции индикаторов
|
|
|
|
|
indicators.OnTimer();
|
2026-03-21 20:28:34 +07:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| ChartEvent function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
void OnChartEvent(const int id,
|
2026-03-21 20:28:34 +07:00
|
|
|
const long &lparam,
|
|
|
|
|
const double &dparam,
|
|
|
|
|
const string &sparam)
|
|
|
|
|
{
|
2026-03-21 20:33:14 +07:00
|
|
|
//--- Работа с панелью
|
|
|
|
|
//--- Вызываем обработчик событий панели
|
|
|
|
|
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);
|
|
|
|
|
}
|
2026-03-21 20:28:34 +07:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-21 20:33:14 +07:00
|
|
|
//| Выводит данные с указанного индекса таймсерии на панель |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void DrawData(const int index,const datetime time)
|
|
|
|
|
{
|
|
|
|
|
//--- Объявляем переменные для получения в них данных
|
|
|
|
|
MqlRates rates[1];
|
|
|
|
|
|
|
|
|
|
//--- Если данные бара по указанному индексу получить не удалось - уходим
|
|
|
|
|
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("Indicators 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 данные буфера 0 индикатора 1 с указанного бара в таблицу 1
|
|
|
|
|
panel.DrawText(indicators.Title(handle_fractals1)+" Up", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
|
|
|
|
|
double value10=indicators.GetData(handle_fractals1,0,0,index);
|
|
|
|
|
string value_str10=(value10!=EMPTY_VALUE ? DoubleToString(value10,indicators.Digits(handle_fractals1)) : " ");
|
|
|
|
|
panel.DrawText(value_str10,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
|
|
|
|
|
|
|
|
|
|
//--- Выводим в таблицу 1 данные буфера 1 индикатора 1 с указанного бара в таблицу 1
|
|
|
|
|
panel.DrawText(indicators.Title(handle_fractals1)+" Down", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
|
|
|
|
|
double value11=indicators.GetData(handle_fractals1,1,0,index);
|
|
|
|
|
string value_str11=(value11!=EMPTY_VALUE ? DoubleToString(value11,indicators.Digits(handle_fractals1)) : " ");
|
|
|
|
|
panel.DrawText(value_str11,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
|
|
|
|
|
|
|
|
|
|
//--- Выводим в таблицу 2 данные буфера 0 индикатора 2 с указанного бара в таблицу 2
|
|
|
|
|
panel.DrawText(indicators.Title(handle_fractals2)+" Up", panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
|
|
|
|
|
double value20=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_fractals2,0,0,index);
|
|
|
|
|
string value_str20=(value20!=EMPTY_VALUE ? DoubleToString(value20,indicators.Digits(handle_fractals2)) : " ");
|
|
|
|
|
panel.DrawText(value_str20,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
|
|
|
|
|
|
|
|
|
|
//--- Выводим в таблицу 2 данные буфера 1 индикатора 2 с указанного бара в таблицу 2
|
|
|
|
|
panel.DrawText(indicators.Title(handle_fractals2)+" Down", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
|
|
|
|
|
double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_fractals2,1,0,index);
|
|
|
|
|
string value_str21=(value21!=EMPTY_VALUE ? DoubleToString(value21,indicators.Digits(handle_fractals2)) : " ");
|
|
|
|
|
panel.DrawText(value_str21,panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
|
|
|
|
|
|
|
|
|
|
//--- Перерисовываем график для немедленного отображения всех изменений на панели
|
|
|
|
|
ChartRedraw(ChartID());
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|