//+------------------------------------------------------------------+ //| Article-16579-MQL5-Market-Profile-Canvas-Optimization.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_plots 0 #include #include //--- input parameters input uint InpStartDate =0; /* day number to start calculation */ // номер дня, с которого начнём расчёт (0 - текущий, 1 - предыдущий, и т.д.) input uint InpShowDays =7; /* 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 uchar InpTransparency =150; /* Transparency, 0 = invisible */ // прозрачность профиля рынка, 0 = полностью прозрачный input uint InpEuropeStartHour =8; /* European session opening hour */ // час открытия европейской сессии input uint InpAmericaStartHour=14; /* American session opening hour */ // час открытия американской сессии //--- уникальный префикс для идентификации графических объектов, принадлежащих индикатору string ExtPrefixUniq; //--- декларируем класс CMarketProfile class CMarketProfile; //--- объявляем список указателей на объекты класса CMarketProfile CArrayList mp_list; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- создаём префикс для имён объектов string number=StringFormat("%I64d", GetTickCount64()); ExtPrefixUniq=StringSubstr(number, StringLen(number)-4); Print("Indicator \"Market Profile Canvas\" started, prefix=", ExtPrefixUniq); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- время открытия текущего дневного бара 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(open_time==current_open) return(rates_total); } //--- обновляем время открытия open_time=current_open; //--- далее будем рассчитывать только один день, так как все остальные дни уже посчитаны при первом запуске lastday=InpStartDate+1; } //--- в цикле по указанному количеству дней (либо InpStartDate+InpShowDays при первом запуске, либо InpStartDate+1 на каждом тике) for(uint day=InpStartDate; day0) { //--- найдём объект Профиля рынка (класса CMarketProfile) в списке по времени открытия дня с индексом day market_profile=GetMarketProfileByDate(ExtPrefixUniq, start_time); //--- если объект не найден возвращаем ноль для полного перерасчёта индикатора if(market_profile==NULL) { PrintFormat("Market Profile not found for %s. Indicator will be recalculated for all specified days", TimeToString(start_time, TIME_DATE)); return(0); } //--- объект CMarketProfile найден в списке; устанавливаем в него значения High и Low дня и передаём массив внутридневных баров //--- при этом объект смещается на новую координату, соответствующую High дневной свечи, и все массивы (векторы) переинициализируются market_profile.SetHiLoBars(day_rate[0].high, day_rate[0].low, bars_in_day); } //--- если это первый расчёт else { //--- создаём новый объект класса CMarketProfile для хранения Профиля рынка дня с индексом day market_profile = new CMarketProfile(ExtPrefixUniq, start_time, stop_time, day_rate[0].high, day_rate[0].low, bars_in_day); //--- добавляем указатель на созданный объект CMarketProfile в список mp_list.Add(market_profile); } //--- устанавливаем размеры холста и параметры рисования линий market_profile.UpdateSizes(); //--- рассчитываем профили для каждой торговой сесии market_profile.CalculateSessions(); //--- рисуем Профиль рынка market_profile.Draw(InpMultiplier); } //--- по завершении цикла после создания и обновления всех объектов, перерисуем график ChartRedraw(0); //--- возвращаем количество баров для следующего вызова OnCalculate return(rates_total); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- удаляем все графические объекты Market Profile после использования Print("Indicator \"Market Profile Canvas\" stopped, delete all objects CMarketProfile with prefix=", ExtPrefixUniq); //--- в цикле по количеству объектов CMarketProfile в списке int size=mp_list.Count(); for(int i=0; i=CHARTEVENT_CUSTOM) return; //--- если есть изменение чарта, обновляем размеры всех объектов класса CMarketProfile с перерисовкой графика if(CHARTEVENT_CHART_CHANGE==id) { //--- в цикле по количеству объектов CMarketProfile в списке int size=mp_list.Count(); for(int i=0; im_high) { m_high=high; if(!ObjectSetDouble(0, m_name, OBJPROP_PRICE, m_high)) PrintFormat("Failed to update canvas for %s, error %d", TimeToString(m_time1, TIME_DATE), GetLastError()); } ArrayCopy(m_bars, bars); m_high=high; m_low=low; //--- дневной диапазон в пунктах m_day_size_pt=(int)((m_high-m_low)/SymbolInfoDouble(Symbol(), SYMBOL_POINT)); //--- переустанавливаем размеры векторов для торговых сессий m_asia=vector::Zeros(m_day_size_pt); m_europe=vector::Zeros(m_day_size_pt); m_america=vector::Zeros(m_day_size_pt); } //+------------------------------------------------------------------+ //| Sets drawing parameters | //+------------------------------------------------------------------+ void CMarketProfile::UpdateSizes(void) { //--- преобразуем время/цену в координаты x/y int x1, y1, x2, y2; ChartTimePriceToXY(0, 0, m_time1, m_high, x1, y1); ChartTimePriceToXY(0, 0, m_time2, m_low, x2, y2); //--- рассчитываем размеры холста m_height=y2-y1; m_width =x2-x1; //--- рассчитываем коэффициенты для преобразования вертикальных уровней цен //--- и горизонтальных счетчиков баров в пиксели графика m_vert_scale=double(m_height)/(m_day_size_pt); m_hor_scale =double(m_width*PeriodSeconds(PERIOD_CURRENT))/PeriodSeconds(PERIOD_D1); //--- изменяем размер холста m_canvas.Resize(m_width, m_height); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ void CMarketProfile::~CMarketProfile(void) { //--- удаляем все графические объекты после использования ObjectsDeleteAll(0, m_prefix, 0, OBJ_BITMAP); ChartRedraw(); } //+------------------------------------------------------------------+ //| Checks that the profile is in the visible part of the chart | //+------------------------------------------------------------------+ bool CMarketProfile::isVisibleOnChart(void) { long last_bar=ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR); // последний видимый бар на графике слева long first_bar=last_bar+-ChartGetInteger(0, CHART_VISIBLE_BARS); // первый видимый бар на графике справа first_bar=first_bar>0?first_bar:0; datetime left =iTime(Symbol(), Period(), (int)last_bar); // время левого видимого бара на графике datetime right=iTime(Symbol(), Period(), (int)first_bar); // время правого видимого бара на графике //--- возвращаем флаг того, что холст расположен внутри левого и правого видимых баров графика return((m_time1>= left && m_time1 <=right) || (m_time2>= left && m_time2 <=right)); } //+------------------------------------------------------------------+ //| Prepares profile arrays by sessions | //+------------------------------------------------------------------+ bool CMarketProfile::CalculateSessions(void) { double point=SymbolInfoDouble(Symbol(), SYMBOL_POINT); // значение одного пункта //--- если массив внутридневных баров не заполнен - уходим if(ArraySize(m_bars)==0) return(false); //---- перебираем все бары текущего дня и отмечаем ячейки массивов (векторов), в которые попадают перебираемые в цикле бары int size=ArraySize(m_bars); for(int i=0; i=InpAmericaStartHour) { //--- в цикле от начала до конца ценовых уровней заполняем счётчики баров, где была цена на этом уровне for(int ind=start_box; ind=InpEuropeStartHour && hour