Русский Português
preview
Estudiamos el indicador de perfil de mercado Market Profile: ¿Qué es y cómo se estructura?

Estudiamos el indicador de perfil de mercado Market Profile: ¿Qué es y cómo se estructura?

MetaTrader 5Ejemplos |
228 2
Artyom Trishkin
Artyom Trishkin

Contenido



Market Profile: ¿qué es?

Peter Steidlmayer desarrolló el concepto de perfil de mercado en la década de 1980 con la Bolsa Mercantil de Chicago. Los tráders que utilizan este método destacan su utilidad para comprender en profundidad el mercado y aumentar la eficacia de las transacciones.

El perfil de mercado no es un indicador técnico tradicional y no proporciona señales comerciales directas, pero sirve como complemento del sistema comercial, ya que permite organizar los datos y determinar quién controla el mercado, cuál es el valor justo y qué factores influyen en los movimientos de los precios.

El perfil de mercado refleja la organización de las operaciones según el tiempo, el precio y el volumen. Cada día se forma un rango con una zona de valores que representa el equilibrio entre compradores y vendedores. Los precios fluctúan en esta zona, y Market Profile ayuda a los tráders a interpretar estos cambios tanto durante una transacción, como después del cierre de la misma. Se basa en una curva de distribución normal en la que aproximadamente el 70% del valor se encuentra dentro de una desviación estándar de la media. En otras palabras, es una herramienta de análisis que muestra la distribución de los volúmenes o el tiempo empleado por el precio en determinados niveles, ayudando a los tráders a comprender dónde se han producido las mayores operaciones y a identificar los niveles clave de oferta y demanda.

A la hora de utilizar el Perfil de mercado, existen algunos conceptos que dependen del comportamiento del mercado y que se muestran directa o indirectamente en el gráfico. El marco temporal recomendado para el análisis es M30.

La primera hora de negociación constituye la pauta principal de la jornada bursátil y es la básica para analizar la actividad de los participantes en el mercado durante toda la sesión. Este periodo es importante para determinar lo que hacen los tráders en el parqué para encontrar un precio al que el comprador y el vendedor acepten realizar la transacción.

Existen dos categorías de participantes en el mercado:

  1. day-timeframe trader — tráder que opera intradía con grandes volúmenes de varios ticks;
  2. other-timeframe trader — tráder que opera en otros marcos temporales, posibilitando el movimiento de los precios hacia nuevos niveles.

Veamos algunos conceptos básicos que le ayudarán a comprender mejor el Perfil de mercado.

  • Initial balance (balance inicial) — rango de precios de mercado durante la primera hora de la sesión bursátil (dos barras de 30 minutos tras la apertura de la sesión).

  • Range — rango, la altura absoluta de todo el Perfil de mercado.

  • Range extension — extensión del rango cuando el precio se mueve en relación con el balance inicial.

  • Value area — área de valor, rango de precios que incluye el 70% de la actividad comercial.

  • Point of control (POC — punto de control) — línea más larga del perfil, que al mismo tiempo es la más cercana al centro del rango.

  • Closing range (rango de cierre) — se ubica cerca del cierre de la sesión bursátil.

  • Buying/Selling tail (cola de compra/venta). La presencia de estas colas indica una fuerte actividad de los tráders "other-timeframe".
    Una longitud escasa en las colas sugiere una agresividad suficiente de los tráders "other-timeframe", tanto por parte de los compradores como de los vendedores.

Para leer el Perfil de mercado, puede seguir estos principios básicos:

  1. Definición de "Precio-volumen" (Price-Volume)
    El perfil de mercado muestra en qué niveles de precios se ha dado un volumen máximo. Las líneas o bloques del gráfico (normalmente horizontales) muestran cuánto tiempo ha permanecido el precio en un determinado nivel o cuánto volumen se ha negociado en él. Las líneas más largas indican más tiempo o más volumen: son niveles clave de apoyo y resistencia.

  2. Punto de Control (POC - Point of Control)
    Es el nivel de precios en el que se ha registrado el máximo volumen comercial durante un periodo. A menudo actúa como un fuerte nivel de apoyo o resistencia porque el precio aquí es el más "aceptable" para los participantes del mercado.

  3. Zonas de precio justo e injusto (Value Area y Non-Value Area)
    • Value Area: suele suponer alrededor del 70% del volumen de negociación del periodo. Se trata de la franja en la que el precio encuentra la mayor parte del tiempo el "equilibrio" entre la oferta y la demanda.
    • Non-Value Area: los niveles por encima o por debajo de la zona de valor se consideran zonas de bajo interés. Si el precio entra en estas zonas, puede regresar rápidamente a la zona de valor o seguir moviéndose debido a los desequilibrios entre la oferta y la demanda.

  4. Detección de tendencias y consolidaciones
    • Día de tendencia: El perfil de mercado se "alarga" hacia un lado cuando el mercado se desplaza de forma constante al alza o a la baja. En estos casos, el precio no se detiene en los niveles, sino que se desplaza, creando nuevas zonas de interés. Un día de tendencia tiene un balance inicial pequeño, que representa menos del 25% del rango del día completo. De hecho, supone un dominio significativo de compradores o vendedores con una fuerte extensión del rango en una dirección.
    • Día de consolidación: el perfil se concentra en torno a un nivel y el área de valor sigue siendo estrecha. Esto indica equilibrio y ausencia de una tendencia fuerte.
    • Día sin tendencia: se caracteriza por un balance inicial estrecho, que posteriormente contiene el rango de precios de todo el día.
    • Día normal: este día tiene un balance inicial amplio, aproximadamente igual al 80% de todo el rango. Este es el tipo más común de día comercial.
    • Cambio normal de un día normal: el balance inicial de una estructura de este tipo equivale aproximadamente al 50% del rango del día. En este caso, además, se forma una ampliación del rango en una u otra dirección.
    • Día neutro: este perfil no se caracteriza por su equilibrio inicial, hay una ampliación del rango en ambas direcciones y el día termina cerca del centro del rango.

  5. Comprensión de los extremos y la reacción de los precios
    • Si el precio alcanza nuevos niveles con poco volumen y vuelve rápidamente al POC o a la zona de valor, esto podría indicar un retroceso.
    • Si el precio continúa comerciándose fuera del Área de Valor y crea un nuevo POC, esto señalará un posible inicio de una nueva tendencia.

  6. Aplicación práctica de Market Profile
    • Para encontrar puntos de entrada: identificamos los niveles de apoyo y resistencia utilizando los límites del POC y del área de valor, esperamos retrocesos desde estos niveles o rupturas en la dirección de la tendencia.
    • Para fijar stop loss y objetivos: utilizamos POC y Value Area High/Low como niveles para colocar stops de protección y fijación de beneficios.

El Perfil de mercado clásico se basa en TPOs (oportunidades tiempo-precio) que incluyen letras mayúsculas del alfabeto latino.  En este caso, cada letra representa 30 minutos de la sesión comercial.



Market Profile en el terminal MetaTrader 5

En el terminal de cliente MetaTrader 5, en el directorio de indicadores \MQL5\Indicators\Free Indicators\ se encuentra una versión sencilla del Perfil de mercado, cuyo código se halla en el archivo MarketProfile.mq5. Este indicador, a diferencia de su representación clásica, construye perfiles verticales de volumen por sesiones (Asia, Europa, América), representando las zonas donde el precio ha pasado más tiempo. Asimismo, analiza las barras intradía y las desglosa en tres periodos de tiempo que representan las distintas sesiones del mercado. El indicador calcula la cantidad de tiempo transcurrido en cada nivel de precios durante estas sesiones y las muestra visualmente como rectángulos en el gráfico.

Aspectos destacados de la lectura del presente indicador:

  1. División en sesiones comerciales:
    • La jornada se divide en tres sesiones: Asiática, Europea y Americana.
    • Las zonas de color del gráfico muestran el tiempo transcurrido en el nivel de precios en cada sesión.

  2. Niveles de precios:
    • Cada nivel de precios está coloreado según la sesión y la duración de la cotización. Los niveles en los que el precio se ha mantenido durante más tiempo se consideran niveles importantes porque en ellos se ha observado una negociación activa.

  3. POC (Punto de Control):
    • El indicador no calcula directamente un POC, pero puede destacar visualmente los niveles con mayor extensión de zonas coloreadas: estos son los puntos de control que indican los niveles clave de oferta y demanda.

  4. Áreas de valor (Value Areas):
    • Al igual que en el Perfil de mercado estándar, las zonas de mayor concentración de volumen (presencia larga del precio) pueden considerarse niveles a los que el precio puede tender a regresar.

Así, el indicador Market Profile de este código ayuda a comprender en qué niveles del día se ha concentrado la actividad principal y a destacar los niveles de apoyo y resistencia de cada sesión bursátil.

Veamos cómo funciona este indicador.



Diseño y principios

En los parámetros de entrada del indicador se especifican:

  1. el número de días durante los que debe mostrarse el perfil,
  2. el número del día más próximo al día actual:
    • 0 — día actual,
    • 1 — día pasado,
    • 2 — día anterior al día pasado, etc,
  3. los colores utilizados para dibujar las sesiones (asiática, europea y americana),
  4. los horarios de apertura de las sesiones europea y americana (la sesión asiática se abre al inicio del día)
  5. el multiplicador de la longitud de los segmentos del histograma:
//--- 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 */    // час открытия американской сессии

Al inicializar el indicador, creamos un identificador único de nombres de objetos a partir de los cuatro últimos dígitos del número de milisegundos transcurridos desde el inicio del sistema:

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

Además, este número se usará al crear los nombres de los objetos gráficos del indicador, y con el mismo prefijo se eliminarán los objetos durante la desinicialización:

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

¿Cómo funciona el indicador? ¿Cuál es su lógica de dibujado? Supongamos que el tamaño completo de una vela diaria desde su mínimo hasta su máximo es el tamaño de la tabla. Entonces, las filas de la tabla serán los niveles de precios: cada punto de precio será una fila de la tabla. Las columnas de la tabla en la variante más sencilla son la distancia de una barra intradía a otra. La fila con índice cero es el precio más bajo igual al Low de la barra. El precio con el índice 1 es el Low de la barra más un punto. Como consecuencia, la línea que define el High de la barra será la línea con el índice del Low de la barra más el número de puntos desde el Low hasta el High de la barra.

Si ahora creamos un array simple que se corresponda con las filas de esta tabla, su índice cero almacenará el número de barras que han conseguido visitar el precio Low de la barra durante el movimiento del precio del día. Y del mismo modo, todas las demás celdas de este array almacenarán el número de veces que el precio ha estado en estos niveles.

¿Cómo se determina eso? Pues muy fácilmente. Como el Low de la barra diaria en la apertura del día es el índice cero del array, podemos calcular los índices de todas las demás barras intradía: el índice de la fila correspondiente a la barra siguiente a la barra de apertura es el Low de esta barra menos el Low de la apertura del día en puntos. El último índice de esta barra será el High de esta barra menos el Low de la apertura del día.

La siguiente figura muestra los índices de inicio y fin en el array Array para cuatro barras intradía de seis horas: Bar0, Bar1, Bar2 y Bar3:

Si ahora marcamos en el array el número de barras que han estado en los niveles de precios y dibujamos los rectángulos del perfil de mercado, obtendremos dicha imagen en el array y en el gráfico:

El array Array se limpia en cada nuevo tick, se le asigna un nuevo tamaño Range de la barra diaria y sus índices se rellenan nuevamente con el valor del número de barras que han visitado cada nivel de precios. Así, el perfil de mercado rellena y muestra dinámicamente en el gráfico la información real sobre el estado del mercado y, cuanto más tiempo esté el precio en un determinado nivel (más barras incluyan el nivel), más larga será la línea que marque dicho nivel.

Veamos cómo se implementa esto en el código del indicador:

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

El código se comenta con bastante detalle. El indicador puede dibujar el perfil de varios días al mismo tiempo. Todos ellos se dibujan en un ciclo según el número de días visualizados.


Análisis del algoritmo básico

Vamos a analizar el algoritmo del código anterior con más detalle. Cada día individual se dibuja de esta manera:

  • dependiendo del periodo del gráfico en el que se ejecute el indicador, obtendremos las barras incluidas en el día representado:
    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);
    
  • en un ciclo por el número de barras intradía
    int size=ArraySize(bars_in_day);
    for(int i=0; i<size; i++)
    
    • para la barra actual seleccionada en el ciclo, calculamos los índices del principio y el final del rango de precios, en el que la barra seleccionada se encuentra dentro de la barra diaria
      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
      
    • definimos la sesión en la que se encuentra la barra seleccionada en el ciclo y, en el array correspondiente, aumentemos los contadores de barras en todas las celdas situadas en el rango de precios de la barra intradía
      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]++;
         }
      
  • después de pasar por todas las barras intradía en el ciclo, cada celda de los arrays de las sesiones contendrá el número de barras que han estado a este precio. Todos los arrays de las sesiones tendrán el mismo tamaño, igual al número de puntos de precio en el rango diario de Low a High de la vela diaria, pero los arrays se llenarán cada uno según los niveles de precio que se hayan alcanzado en esa sesión.
  • A continuación, en un ciclo a través de todos los arrays llenos de las sesiones, en el gráfico se muestran los objetos gráficos según las coordenadas calculadas:
    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);
         }
      }
  • Como resultado, en el gráfico se dibujará un perfil de mercado para un día.

Función que dibuja los segmentos de las líneas:

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

Los segmentos se dibujan mediante objetos-rectángulo gráficos (en principio, también se pueden dibujar líneas de tendencia desactivando para ellas las propiedades "rayo izquierda" y "rayo derecha"). La función recibe las coordenadas del rectángulo en forma de hora inicial y final, y un precio que es el mismo para ambos lados del rectángulo. De esta forma, obtenemos una línea normal con el color especificado en los parámetros de entrada.

Tras ejecutar el indicador en el gráfico EURUSD M30, podemos ver la siguiente imagen del perfil del mercado:

Si se trazamos líneas desde cada uno de los días anteriores, desde su POC, podemos ver claramente los niveles de precio "justo", que bien pueden servir como niveles de apoyo y resistencia, o de atracción (el gap del día actual bien puede estar cerrado, y este será el POC de ayer).


Conclusión

Estudiando Market Profile, podemos comprender mejor la dinámica del mercado, identificar los niveles clave y encontrar puntos de entrada y salida eficaces.  El perfil de mercado se diferencia de un gráfico de ticks en que combina de forma práctica precio, volumen y tiempo. Asimismo, permite identificar los niveles de precios de equilibrio y analizar quién controla el mercado en un momento dado. Esto ofrece ciertas ventajas al adoptar decisiones comerciales, pues nos permite adaptarnos a los cambios en las estimaciones del valor justo de los tráders.

Tras familiarizarnos con el indicador Market Profile suministrado con el terminal cliente MetaTrader 5, ahora entendemos lo simple que es su funcionamiento, comprendemos la lógica de su diseño, y podemos utilizarlo tanto para evaluar la situación del mercado tal como es, como para crear algo más avanzado y complejo sobre su base.

Para una plena comprensión y comparación con otros conceptos, puede leer otros artículos sobre el Perfil de mercado, presentados en el recurso mql5.com:

Al final de la página adjuntamos el código completamente comentado del indicador considerado en el artículo: podrá descargarlo y estudiarlo usted mismo, basándose en los comentarios del código.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/16461

Archivos adjuntos |
MarketProfile.mq5 (25.99 KB)
Alexey Viktorov
Alexey Viktorov | 28 nov 2024 en 17:00
Es extraño que no hayas llegado a la conclusión de que no es un indicador, sino una chorrada sin valor. Igual que los fractales no muestran nada inteligente.
Intrest1
Intrest1 | 8 abr 2025 en 13:35
¿Podría hacer que el volumen de ticks de un nivel se dividiera por el número de barras de ese nivel? El precio puede estar plano durante mucho tiempo y los ticks vacíos se acumulan. ¿Qué sentido tienen los niveles sin volumen real? La relación entre el volumen de ticks y el número de barras del nivel mostrará el peso real del nivel
Características del Wizard MQL5 que debe conocer (Parte 44): Indicador técnico Average True Range (ATR) Características del Wizard MQL5 que debe conocer (Parte 44): Indicador técnico Average True Range (ATR)
El oscilador ATR es un indicador muy popular que actúa como proxy de volatilidad, especialmente en los mercados de divisas, donde los datos de volumen son escasos. Examinamos esto, basándonos en patrones, como hemos hecho con indicadores anteriores, y compartimos estrategias e informes de pruebas gracias a las clases y el ensamblaje de la biblioteca del asistente MQL5.
Redes neuronales en el trading: Transformer parámetro-eficiente con atención segmentada (Final) Redes neuronales en el trading: Transformer parámetro-eficiente con atención segmentada (Final)
En artículos anteriores, revisamos los aspectos teóricos del framework PSformer, que incluye dos importantes innovaciones en la arquitectura del Transformer clásico: el mecanismo de compartición de parámetros (PS) y la atención a los segmentos espaciotemporales (SegAtt). En este artículo, continuaremos el trabajo sobre la implementación de los enfoques propuestos mediante MQL5.
Redes neuronales en el trading: Modelo adaptativo multiagente (MASA) Redes neuronales en el trading: Modelo adaptativo multiagente (MASA)
Hoy les propongo familiarizarse con el MASA, un framework adaptativo multiagente que combina el aprendizaje por refuerzo y las estrategias adaptativas para ofrecer un equilibrio armonioso entre la rentabilidad y la gestión del riesgo en condiciones de mercado turbulentas.
Redes neuronales en el trading: Transformer parámetro-eficiente con atención segmentada (PSformer) Redes neuronales en el trading: Transformer parámetro-eficiente con atención segmentada (PSformer)
Hoy proponemos al lector un primer contacto con el nuevo framework PSformer, que adapta la arquitectura del Transformer vainilla para resolver problemas de previsión de series temporales multidimensionales. El framework se basa en dos innovaciones clave: el mecanismo de compartición de parámetros (PS) y la atención a los segmentos espaciotemporales (SegAtt).