preview
Изучаем индикатор рыночного профиля — Market Profile: Что это и как устроен?

Изучаем индикатор рыночного профиля — Market Profile: Что это и как устроен?

MetaTrader 5Примеры | 28 ноября 2024, 15:55
2 015 2
Artyom Trishkin
Artyom Trishkin

Содержание



Market Profile — что это?

Питер Стайдлмэйер (Peter Steidlmayer) разработал концепцию Профиля рынка в 1980-х с Чикагской товарной биржей. Трейдеры, применяющие этот метод, отмечают его пользу для глубокого понимания рынка и повышения эффективности торговли.

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

Профиль рынка отражает организацию торгов на основе времени, цены и объема. Каждый день формируется диапазон с областью стоимости, представляющей равновесие между покупателями и продавцами. В этой зоне цены колеблются, и Market Profile помогает трейдерам интерпретировать эти изменения как во время, так и уже после завершения торгов. Он строится на кривой нормального распределения, где около 70% стоимости находится в пределах одного стандартного отклонения от среднего значения. Иными словами — это инструмент анализа, который показывает распределение объёмов или времени, проведённого ценой на определённых уровнях. Он помогает трейдерам понимать, где происходили наибольшие сделки, и определять ключевые уровни спроса и предложения.

При использовании Профиля рынка существуют некоторые понятия, обусловленные поведением рынка и прямо или косвенно отображаемые на графике. Рекомендуемый таймфрейм для анализа — M30.

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

Существуют две категории участников рынка:

  1. day-timeframe trader — трейдер на площадке, торгующий внутри дня большими объемами в несколько тиков;
  2. other-timeframe trader — трейдер, торгующий на остальных отрезках времени, обеспечивающий движение цены к новым уровням.

Рассмотрим некоторые базовые понятия, помогающие лучше понять Профиль рынка.

  • Initial balance (первоначальный баланс) — диапазон цен рынка в течение первого часа торговой сессии (два 30-минутных бара после открытия сессии).

  • Range — диапазон, абсолютная высота всего Профиля рынка.

  • Range extension — расширение диапазона при движении цены по отношению к первоначальному балансу.

  • Value area — зона стоимости, диапазон цен, который включает в себя 70 % торговой активности.

  • Point of control (POC — точка контроля) — самая длинная линия профиля, которая в то же время наиболее близка к центру диапазона.

  • Closing range (диапазон закрытия) — располагается вблизи закрытия торговой сессии.

  • Buying/Selling tail (хвост покупки/продажи). Наличие подобных хвостов указывает на резкую активность трейдеров "other-timeframe".
    Небольшая длина хвостов говорит о достаточной агрессивности трейдеров "other-timeframe" как со стороны покупателей, так и со стороны продавцов.

Чтобы читать Market Profile, можно следовать этим основным принципам:

  1. Определение "Цена-объём" (Price-Volume)
    Market Profile отображает, на каких уровнях цен был максимальный объём. Линии или блоки на графике (обычно горизонтальные) показывают, сколько времени цена проводила на определённом уровне, или какой объём был здесь наторгован. Более длинные линии означают больше времени или больший объём — это ключевые уровни поддержки и сопротивления.

  2. Точка контроля (POC — Point of Control)
    Это уровень цены, на котором был максимальный объём торговли за период. Он часто действует, как сильный уровень поддержки или сопротивления, поскольку цена здесь наиболее "приемлема" для участников рынка.

  3. Зоны справедливой и несправедливой цены (Value Area и Non-Value Area)
    • Value Area: обычно составляет около 70% объёма торговли за период. Это диапазон, где цена находила большую часть времени "баланс" между спросом и предложением.
    • Non-Value Area: уровни выше или ниже Value Area считаются зонами с низким интересом. Если цена уходит в эти зоны, она может либо быстро вернуться в Value Area, либо продолжить движение за счёт дисбаланса спроса и предложения.

  4. Обнаружение трендов и флэтов
    • Трендовый день: Market Profile "удлиняется" в одну сторону, когда рынок устойчиво движется вверх или вниз. В этих случаях цена не задерживается на уровнях, а движется, создавая новые зоны интереса. У трендового дня небольшой первоначальный баланс, представляющий менее 25% диапазона всего дня. Фактически это существенное доминирование покупателей или продавцов с сильным расширением диапазона в одном из направлений.
    • Флетовый день: профиль концентрируется вокруг одного уровня, а Value Area остаётся узкой. Это говорит о балансе и отсутствии сильного тренда.
    • Нетрендовый день: характеризуется узким первоначальным балансом, который впоследствии содержит диапазон цен всего дня.
    • Нормальный день: у такого дня присутствует широкий первоначальный баланс, приблизительно равный 80% от всего диапазона. Это самый распространенный тип торгового дня.
    • Нормальное изменение нормального дня: первоначальный баланс такой структуры равняется приблизительно 50% диапазона дня. При этом формируется расширение диапазона либо в одном, либо в другом направлении.
    • Нейтральный день: такой профиль не характеризуется его первоначальным балансом, присутствует расширение диапазона в обоих направлениях, и день завершается около центра диапазона.

  5. Понимание экстремумов и реакции цены
    • Если цена достигает новых уровней с низким объёмом и быстро возвращается к POC или Value Area, это может указывать на отскок.
    • Если цена продолжает торговаться за пределами Value Area и создаёт новую POC, это сигнализирует о возможном начале нового тренда.

  6. Практическое применение Market Profile
    • Для поиска точек входа: определите уровни поддержки и сопротивления, используя POC и границы Value Area, ожидайте откатов от этих уровней или пробоев в сторону тренда.
    • Для установки стоп-лоссов и целей: используйте POC и Value Area High/Low как уровни для размещения защитных стопов и фиксации прибыли.

Классический Market Profile основан на TPOs (временно-ценовых возможностях), которые включают в себя заглавные буквы латинского алфавита.  При этом каждая буква представляет собой 30 минут торговой сессии.



Market Profile в терминале MetaTrader 5

В клиентском терминале MetaTrader 5 в каталоге индикаторов \MQL5\Indicators\Free Indicators\ представлена простая версия Профиля рынка, код которого содержится в файле MarketProfile.mq5. Этот индикатор, в отличие от его классического представления, строит вертикальные объемные профили по сессиям (Азия, Европа, Америка), визуализируя зоны, где цена проводила больше времени. Он анализирует внутридневные бары и разбивает их по трем временным периодам, отражающим разные сессии рынка. Индикатор рассчитывает количество времени, проведённого на каждом ценовом уровне, в течение этих сессий, и визуально отображает их как прямоугольники на графике.

Основные моменты для чтения этого индикатора:

  1. Разделение на торговые сессии:
    • Весь день делится на три сессии: Азиатская, Европейская и Американская.
    • Цветовые зоны на графике показывают время, проведённое на уровне цены в каждой сессии.

  2. Ценовые уровни:
    • Каждый ценовой уровень окрашивается согласно сессии и продолжительности пребывания на нём цены. Уровни, где цена задерживалась дольше, считаются важными уровнями, так как там наблюдалась активная торговля.

  3. POC (Point of Control):
    • Индикатор не рассчитывает POC напрямую, но можно визуально выделить уровни с наибольшей протяженностью цветных зон — это и есть точки контроля, указывающие ключевые уровни спроса и предложения.

  4. Области ценности (Value Areas):
    • Как и в стандартном Market Profile, области с более высокой концентрацией объёма (долгое нахождение цены) можно рассматривать как уровни, к которым цена может стремиться вернуться.

Таким образом, индикатор Market Profile в данном коде помогает понять, на каких уровнях в течение дня была сосредоточена основная активность, и выделить уровни поддержки и сопротивления для каждой торговой сессии.

Давайте разберёмся, как работает этот индикатор.



Устройство и принципы

Во входных параметрах индикатора указываются:

  1. количество дней, для которых необходимо показать профиль,
  2. номер ближайшего дня к текущему:
    • 0 — текущий день,
    • 1 — прошлый,
    • 2 — позапрошлый, и т.д.,
  3. цвета, которыми рисуются сессии (азиатская, европейская и американская),
  4. часы открытия европейской и американской сессий (азиатская сессия открывается на открытии дня)
  5. множитель длины отрезков гистограммы:
//--- 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 */    // час открытия американской сессии

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

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создаём префикс для имён объектов
   string number=StringFormat("%I64d", GetTickCount64());
   ExtPrefixUniq=StringSubstr(number, StringLen(number)-4);
   Print("Indicator \"Market Profile\" started, prefix=", ExtPrefixUniq);

   return(INIT_SUCCEEDED);
  }

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

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- после использования удалим все графические объекты, созданные индикатором
   Print("Indicator \"Market Profile\" stopped, delete all objects with prefix=", ExtPrefixUniq);
   ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_RECTANGLE);
   ChartRedraw(0);
  }

Так как же устроен индикатор? Какова его логика рисования? Давайте представим, что полный размер дневной свечи от её Low до High — это размер таблицы. Тогда строками таблицы будут являться ценовые уровни: каждый пункт цены — это одна строка таблицы. Столбцами таблицы в самом простом варианте является дистанция от одного внутридневного бара до другого. Строка с нулевым индексом — это самая нижняя цена, равная Low бара. Цена с индексом 1 — это Low бара плюс один пункт. Соответственно, строка, определяющая High бара — это строка с индексом Low бара плюс количество пунктов от Low до High бара.

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

Как это определить? Да очень просто. Так как Low дневного бара на открытии дня — это нулевой индекс массива, то можно рассчитать индексы всех остальных внутридневных баров: индекс строки, соответствующей бару, следующему за баром открытия, — это Low этого бара минус Low открытия дня в пунктах. Последним индексом для этого бара будет High этого бара минус Low открытия дня.

На рисунке ниже показаны начальные и конечные индексы в массиве Array для четырёх шестичасовых баров внутри дня: Bar0, Bar1, Bar2 и Bar3:

Если теперь отметить в массиве количество баров, побывавших на ценовых уровнях, и нарисовать прямоугольники профиля рынка, то получим такую картину в массиве и на графике:

Массив Array на каждом новом тике очищается, ему устанавливается новый размер Range дневного бара и его индексы заполняются заново значением количества баров, побывавших на каждом уровне цены. Таким образом, в профиле рынка динамически заполняется и отображается на графике актуальная информация о состоянии рынка и, чем дольше цена находится на каком-либо уровне (больше баров включают в себя уровень), тем длиннее линия, обозначающая этот уровень.

Посмотрим, как это реализовано в коде индикатора:

//+------------------------------------------------------------------+
//| 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(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
   return(rates_total);
  }

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


Разбор основного алгоритма

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

  • в зависимости от периода графика, на котором запущен индикатор, получаем бары, входящие в отображаемый день:
    MqlRates day_rate[];
    if(CopyRates(Symbol(), PERIOD_D1, day, 1, day_rate)==-1)
       return(prev_calculated);
    
    MqlRates bars_in_day[];
    datetime start_time=day_rate[0].time+PeriodSeconds(PERIOD_D1)-1;
    datetime stop_time=day_rate[0].time;
    
    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)
        {
         for(int ind=start_box; ind<stop_box; ind++)
            boxes_america[ind]++;
        }
      else
        {
         if(hour>=InpEuropeStartHour && hour<InpAmericaStartHour)
            for(int ind=start_box; ind<stop_box; ind++)
               boxes_europe[ind]++;
         else
            for(int ind=start_box; ind<stop_box; ind++)
               boxes_asia[ind]++;
         }
      
  • после прохождения в цикле по всем внутридневным барам, в каждой ячейке массивов сессий будет записано число баров, побывавших на этой цене. Все массивы сессий будут иметь одинаковый размер, равный количеству пунктов цены в дневном диапазоне от Low до High дневной свечи, но заполнены массивы будут каждый в соответствии с тем, какие уровни цены были в эту сессию достигнуты.
  • Далее в цикле по всем заполненным массивам сессий на график выводятся графические объекты по рассчитанным координатам:
    for(int ind=0; ind<day_size; ind++)
      {
       if(boxes_asia[ind]>0)
         {
          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)
         {
          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)
         {
          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);
         }
      }
  • В итоге на графике будет нарисован профиль рынка для одного дня.

Функция, рисующая отрезки линий:

//+------------------------------------------------------------------+
//| Draw color box                                                   |
//+------------------------------------------------------------------+
void DrawBox(string bar_prefix, double price, datetime time1, datetime time2, color clr)
  {
   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);
  }

Отрезки рисуются графическими объектами-прямоугольниками (в принципе, можно рисовать и трендовыми линиями, отключив для них свойства "луч влево" и "луч вправо"). В функцию передаются координаты прямоугольника в виде времени его начала и конца, и цены, которая одинакова для обеих сторон прямоугольника. Таким образом, получается обычная линия с указанным во входных параметрах цветом.

Запустив индикатор на графике EURUSD M30 можно увидеть такую картину профиля рынка:

Если от каждого из предыдущих дней, от его точки Point of Control провести линии, то отчётливо можно увидеть уровни "справедливой" цены, которые вполне могут послужить уровнями поддержки и сопротивления, или притяжения (гэп текущего дня вполне вероятно, что может быть закрыт, а это — POC вчерашнего дня).


Заключение

Изучая Market Profile, вы сможете лучше понимать рыночную динамику, выявлять ключевые уровни и находить эффективные точки для входа и выхода из позиций.  Профиль рынка отличается от тикового графика, объединяя цену, объем и время в удобной форме. Он позволяет выявлять ценовые уровни равновесия и анализировать, кто контролирует рынок в данный момент. Это предоставляет преимущество в принятии торговых решений, позволяя адаптироваться к изменению оценки справедливой стоимости трейдерами.

Ознакомившись с индикатором Market Profile, поставляемым с клиентским терминалом MetaTrader 5, теперь мы понимаем всю простоту его работы, понимаем логику его построения, и можем использовать его, как для оценки рыночной ситуации в предоставленном виде, так и создать на его основе нечто более продвинутое и сложное.

Для полноты понимания и сравнения с иными концепциями, можно ознакомиться с другими работами по Профилю рынка, представленными на ресурсе mql5.com:

Полностью прокомментированный код индикатора, рассмотренного в статье, прикреплён внизу страницы — его можно скачать и самостоятельно изучить, опираясь на комментарии в коде.

Прикрепленные файлы |
MarketProfile.mq5 (25.99 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Alexey Viktorov
Alexey Viktorov | 28 нояб. 2024 в 17:00
Странно, что вы не сделали вывод, что это не индикатор, а чушь собачья никчёмная. Ровно как и фракталы ничего умного не показывает.
Intrest1
Intrest1 | 8 апр. 2025 в 13:35
Не могли бы вы сделать так, чтобы тиковый объем уровня делился на количество баров в этом уровне? Цена может долго находится во флете и пустые тики накапливаются. Какой смысл в уровнях в которых нет реальных объемов? Отношение тикового объема к количеству баров в уровне покажет реальный вес уровня
Построение модели для ограничения диапазона сигналов по тренду (Часть 5): Система уведомлений (Часть I) Построение модели для ограничения диапазона сигналов по тренду (Часть 5): Система уведомлений (Часть I)
Мы разобьем основной код MQL5 на отдельные фрагменты, чтобы проиллюстрировать интеграцию Telegram и WhatsApp для получения уведомлений о сигналах от индикатора Trend Constraint, который мы создаем в этой серии статей. Статья будет полезна трейдерам, а также начинающим и опытным разработчикам. Сначала мы рассмотрим настройку уведомлений в MetaTrader 5 и пользу их подключения для пользователя. На основе этого разработчики смогут отметить для себя определенные моменты для дальнейшего применения в своих системах.
Машинное обучение и Data Science (Часть 24): Прогнозирование временных рядов на форексе с помощью обычных ИИ-моделей Машинное обучение и Data Science (Часть 24): Прогнозирование временных рядов на форексе с помощью обычных ИИ-моделей
На валютном рынке сложно предсказать будущие тренды, не имея представления о прошлом. Очень немногие модели машинного обучения способны делать прогнозы на будущее, учитывая прошлые значения. В этой статье мы посмотрим, как можно использовать классические (не временные ряды) модели искусственного интеллекта, чтобы понять рынок.
Прогнозирование временных рядов с использованием нейронных сетей LSTM: Нормализация цены и токенизация времени Прогнозирование временных рядов с использованием нейронных сетей LSTM: Нормализация цены и токенизация времени
В статье описывается простая стратегия нормализации рыночных данных с использованием дневного диапазона и обучения нейронной сети для улучшения рыночных прогнозов. Разработанные модели могут использоваться совместно с существующими системами технического анализа или отдельно для прогнозирования общего направления рынка. Структура, изложенная в этой статье, может быть дополнительно усовершенствована техническим аналитиком для разработки моделей, подходящих как для ручных, так и для автоматизированных торговых стратегий.
Циклы и трейдинг Циклы и трейдинг
Эта статья посвящена использованию циклов в трейдинге. В ней мы постараемся разобраться, как можно построить торговую стратегию, основываясь на циклических моделях.