Article-16461-MQL5-Market-P.../Article-16461-MQL5-Market-Profile-Introduction.mq5

247 lines
16 KiB
MQL5
Raw Permalink Normal View History

2026-03-23 20:55:01 +07:00
//+------------------------------------------------------------------+
2026-03-23 20:51:20 +07:00
//| Article-16461-MQL5-Market-Profile-Introduction.mq5 |
//| Copyright 2026, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
#property indicator_chart_window
#property indicator_plots 0
//--- input parameters
input uint InpStartDate =0; /* day number to start calculation */ // номер дня, с которого начнём расчёт (0 - текущий, 1 - предыдущий, и т.д.)
input uint InpShowDays =3; /* number of days to display */ // количество отображаемых дней, начиная и включая день в InpStartDate
input int InpMultiplier =1; /* histogram length multiplier */ // множитель длины гистограммы
input color InpAsiaSession =clrGold; /* Asian session */ // цвет гистограммы азиатской сессии
input color InpEuropeSession =clrBlue; /* European session */ // цвет гистограммы европейской сессии
input color InpAmericaSession =clrViolet; /* American session */ // цвет гистограммы американской сессии
input uint InpEuropeStartHour =8; /* European session opening hour */ // час открытия европейской сессии
input uint InpAmericaStartHour=14; /* American session opening hour */ // час открытия американской сессии
//--- уникальный префикс для идентификации графических объектов, принадлежащих индикатору
string ExtPrefixUniq;
2026-03-23 20:51:20 +07:00
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
2026-03-23 20:55:01 +07:00
//--- создаём префикс для имён объектов
string number=StringFormat("%I64d", GetTickCount64());
ExtPrefixUniq=StringSubstr(number, StringLen(number)-4);
Print("Indicator \"Market Profile\" started, prefix=", ExtPrefixUniq);
2026-03-23 20:51:20 +07:00
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
int OnCalculate(const int rates_total,
const int prev_calculated,
2026-03-23 20:51:20 +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-23 20:55:01 +07:00
const int &spread[])
2026-03-23 20:51:20 +07:00
{
2026-03-23 20:55:01 +07:00
//--- время открытия текущего дневного бара
datetime static open_time=0;
//--- номер последнего дня для расчетов
//--- (при InpStartDate = 0 и InpShowDays = 3, lastday = 3)
//--- (при InpStartDate = 1 и InpShowDays = 3, lastday = 4) etc ...
uint lastday=InpStartDate+InpShowDays;
//--- если первый расчет уже был
if(prev_calculated!=0)
{
//--- получаем время открытия текущего дневного бара
datetime current_open=iTime(Symbol(), PERIOD_D1, 0);
//--- если текущий день не рассчитываем
if(InpStartDate!=0)
{
//--- если время открытия не было получено - уходим
if(current_open==open_time)
return(rates_total);
}
//--- обновляем время открытия
open_time=current_open;
//--- далее будем рассчитывать только один день, так как все остальные дни уже посчитаны при первом запуске
lastday=InpStartDate+1;
}
//--- в цикле по указанному количеству дней (либо InpStartDate+InpShowDays при первом запуске, либо InpStartDate+1 на каждом тике)
for(uint day=InpStartDate; day<lastday; day++)
{
//--- получаем в структуру данные дня с индексом day
MqlRates day_rate[];
//--- если индикатор запускается в выходные или праздничные дни, когда нет тиков, сначала нужно открыть дневной график символа
//--- если не получили данные бара по индексу day дневного периода - уходим до следующего вызова OnCalculate()
if(CopyRates(Symbol(), PERIOD_D1, day, 1, day_rate)==-1)
return(prev_calculated);
//--- получаем дневной диапазон (Range) в пунктах
double high_day=day_rate[0].high;
double low_day=day_rate[0].low;
double point=SymbolInfoDouble(Symbol(), SYMBOL_POINT);
int day_size=(int)((high_day-low_day)/point);
//--- подготавливаем массивы для хранения прямоугольников уровней цены
int boxes_asia[], boxes_europe[], boxes_america[];
//--- размеры массивов равны количеству пунктов в диапазоне дня
ArrayResize(boxes_asia, day_size);
ArrayResize(boxes_europe, day_size);
ArrayResize(boxes_america, day_size);
//--- обнуляем массивы
ZeroMemory(boxes_asia);
ZeroMemory(boxes_europe);
ZeroMemory(boxes_america);
//--- получаем все внутредневные бары текущего дня
MqlRates bars_in_day[];
datetime start_time=day_rate[0].time+PeriodSeconds(PERIOD_D1)-1;
datetime stop_time=day_rate[0].time;
//--- если индикатор запускается в выходные или праздничные дни, когда нет тиков, сначала нужно открыть дневной график символа
//--- если для указанного дня не удалось получить бары текущего таймфрейма - уходим до следующего вызова OnCalculate()
if(CopyRates(Symbol(), PERIOD_CURRENT, start_time, stop_time, bars_in_day)==-1)
return(prev_calculated);
//--- перебираем в цикле все бары текущего дня и отмечаем ячейки цены, в которые попадают бары
int size=ArraySize(bars_in_day);
for(int i=0; i<size; i++)
{
//--- рассчитываем диапазон индексов уровней цены в дневном диапазоне
int start_box=(int)((bars_in_day[i].low-low_day)/point); // индекс первой ячейки массива цен, соответствующей цене Low текущего бара i
int stop_box =(int)((bars_in_day[i].high-low_day)/point); // индекс последней ячейки массива цен, соответствующей цене High текущего бара i
//--- получаем час бара по индексу цикла
MqlDateTime bar_time;
TimeToStruct(bars_in_day[i].time, bar_time);
uint hour=bar_time.hour;
//--- по часу бара определяем к какой сессии принадлежит бар
//--- американская сессия
if(hour>=InpAmericaStartHour)
{
//--- в массиве американской сессии, в ячейках от start_box до stop_box увеличиваем счётчики баров
for(int ind=start_box; ind<stop_box; ind++)
boxes_america[ind]++;
}
//--- Европа или Азия
else
{
//--- европейская сессия
if(hour>=InpEuropeStartHour && hour<InpAmericaStartHour)
//--- в массиве европейской сессии, в ячейках от start_box до stop_box увеличиваем счётчики баров
for(int ind=start_box; ind<stop_box; ind++)
boxes_europe[ind]++;
//--- азиатская сессия
else
//--- в массиве азиатской сессии, в ячейках от start_box до stop_box увеличиваем счётчики баров
for(int ind=start_box; ind<stop_box; ind++)
boxes_asia[ind]++;
}
}
//--- на основании созданных массивов уровней цен рисуем профиль рынка
//--- профиль рынка на графике рисуется отрезками цветных линий цветом, заданным в настройках для каждой торговой сессии
//--- отрезки рисуются объектами-прямоугольниками с высотой прямоугольника, равной одному пункту цены, и шириной, равной дистанции до следующего бара справа
//--- определяем день для наименования графического объекта и ширину прямоугольника
string day_prefix=TimeToString(day_rate[0].time, TIME_DATE);
int box_length=PeriodSeconds(PERIOD_CURRENT);
//--- азиатская сессия
//--- в цикле по количеству пунктов дневного бара
for(int ind=0; ind<day_size; ind++)
{
//--- если массив азиатской сессии заполнен
if(boxes_asia[ind]>0)
{
//--- получаем очередную цену методом прибавления количества пунктов ind к цене Low дневного бара
//--- получаем время начала отрезка (время открытия дневного бара)
//--- и время конца отрезка (время открытия дневного бара + количество баров, хранящееся в ячейке ind массива boxes_asia[])
double price=low_day+ind*point;
datetime time1=day_rate[0].time;
datetime time2=time1+boxes_asia[ind]*box_length*InpMultiplier;
//--- создаём префикс имени графического объекта азиатской сессии
string prefix=ExtPrefixUniq+"_"+day_prefix+"_Asia_"+StringFormat("%.5f", price);
//--- рисуем прямоугольник (отрезок линии) на рассчитанных координатах с цветом для азиатской сессии
DrawBox(prefix, price, time1, time2, InpAsiaSession);
}
}
//--- европейская сессия
//--- в цикле по количеству пунктов дневного бара
for(int ind=0; ind<day_size; ind++)
{
//--- если массив европейской сессии заполнен
if(boxes_europe[ind]>0)
{
//--- получаем очередную цену методом прибавления количества пунктов ind к цене Low дневного бара
//--- получаем время начала отрезка (время открытия дневного бара + время правого края профиля азиатской сессии)
//--- и время конца отрезка (время начала отрезка европейской сессии + количество баров, хранящееся в ячейке ind массива boxes_europe[])
double price=low_day+ind*point;
datetime time1=day_rate[0].time+boxes_asia[ind]*box_length*InpMultiplier;
datetime time2=time1+boxes_europe[ind]*box_length*InpMultiplier;
//--- создаём префикс имени графического объекта европейской сессии
string prefix=ExtPrefixUniq+"_"+day_prefix+"_Europe_"+StringFormat("%.5f", price);
//--- рисуем прямоугольник (отрезок линии) на рассчитанных координатах с цветом для европейской сессии
DrawBox(prefix, price, time1, time2, InpEuropeSession);
}
}
//--- американская сессия
//--- в цикле по количеству пунктов дневного бара
for(int ind=0; ind<day_size; ind++)
{
//--- если массив американской сессии заполнен
if(boxes_america[ind]>0)
{
//--- получаем очередную цену методом прибавления количества пунктов ind к цене Low дневного бара
//--- получаем время начала отрезка (время открытия дневного бара + время правого края профиля азиатской сессии + время правого края профиля европейской сессии)
//--- и время конца отрезка (время начала отрезка американской сессии + количество баров, хранящееся в ячейке ind массива boxes_america[])
double price=low_day+ind*point;
datetime time1=day_rate[0].time+(boxes_asia[ind]+boxes_europe[ind])*box_length*InpMultiplier;
datetime time2=time1+boxes_america[ind]*box_length*InpMultiplier;
//--- создаём префикс имени графического объекта американской сессии
string prefix=ExtPrefixUniq+"_"+day_prefix+"_America_"+StringFormat("%.5f", price);
//--- рисуем прямоугольник (отрезок линии) на рассчитанных координатах с цветом для американской сессии
DrawBox(prefix, price, time1, time2, InpAmericaSession);
}
}
}
//--- по завершении цикла перерисуем график
ChartRedraw(0);
//--- возвращаем количество баров для следующего вызова OnCalculate
2026-03-23 20:51:20 +07:00
return(rates_total);
}
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
//| Custom indicator deinitialization function |
2026-03-23 20:51:20 +07:00
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
void OnDeinit(const int reason)
2026-03-23 20:51:20 +07:00
{
2026-03-23 20:55:01 +07:00
//--- после использования удалим все графические объекты, созданные индикатором
Print("Indicator \"Market Profile\" stopped, delete all objects with prefix=", ExtPrefixUniq);
ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_RECTANGLE);
ChartRedraw(0);
2026-03-23 20:51:20 +07:00
}
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
//| Draw color box |
2026-03-23 20:51:20 +07:00
//+------------------------------------------------------------------+
2026-03-23 20:55:01 +07:00
void DrawBox(string bar_prefix, double price, datetime time1, datetime time2, color clr)
2026-03-23 20:51:20 +07:00
{
2026-03-23 20:55:01 +07:00
ObjectCreate(0, bar_prefix, OBJ_RECTANGLE, 0, time1, price, time2, price);
ObjectSetInteger(0, bar_prefix, OBJPROP_COLOR, clr);
ObjectSetInteger(0, bar_prefix, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, bar_prefix, OBJPROP_WIDTH, 1);
ObjectSetString(0, bar_prefix, OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(0, bar_prefix, OBJPROP_BACK, true);
2026-03-23 20:51:20 +07:00
}
//+------------------------------------------------------------------+