English Русский 中文 Deutsch 日本語 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 |
299 4
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 - current, 1 - previous, etc.
input uint  InpShowDays        =3;           /* number of days to display */        // starting with and including the day in 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()
  {
//--- create a prefix for object names
   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)
  {
//--- after use, delete all graphical objects created by the indicator
   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[])
  {
//--- opening time of the current daily bar
   datetime static open_time=0;

//--- number of the last day for calculations
//--- (if InpStartDate = 0 and InpShowDays = 3, lastday = 3)
//--- (if InpStartDate = 1 and InpShowDays = 3, lastday = 4) etc ...
   uint lastday=InpStartDate+InpShowDays;

//--- if the first calculation has already been made
   if(prev_calculated!=0)
     {
      //--- get the opening time of the current daily bar
      datetime current_open=iTime(Symbol(), PERIOD_D1, 0);
      
      //--- if we do not calculate the current day
      if(InpStartDate!=0)
        {
         //--- if the opening time was not received, leave
         if(current_open==open_time)
            return(rates_total);
        }
      //--- update opening time
      open_time=current_open;
      //--- we will only calculate one day from now on, since all other days have already been calculated during the first run
      lastday=InpStartDate+1;
     }

//--- in a loop for the specified number of days (either InpStartDate+InpShowDays on first run, or InpStartDate+1 on each tick)
   for(uint day=InpStartDate; day<lastday; day++)
     {
      //--- get the data of the day with index day into the structure
      MqlRates day_rate[];
      //--- if the indicator is launched on weekends or holidays when there are no ticks, you should first open the daily chart of the symbol
      //--- if we have not received bar data for the day index of the daily period, we leave until the next call to OnCalculate()
      if(CopyRates(Symbol(), PERIOD_D1, day, 1, day_rate)==-1)
         return(prev_calculated);

      //--- get daily range (Range) in points
      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);

      //--- prepare arrays for storing price level rectangles
      int boxes_asia[], boxes_europe[], boxes_america[];
      //--- array sizes equal to the number of points in the day range
      ArrayResize(boxes_asia, day_size);
      ArrayResize(boxes_europe, day_size);
      ArrayResize(boxes_america, day_size);
      //--- zero out the arrays
      ZeroMemory(boxes_asia);
      ZeroMemory(boxes_europe);
      ZeroMemory(boxes_america);

      //--- get all intraday bars of the current day
      MqlRates bars_in_day[];
      datetime start_time=day_rate[0].time+PeriodSeconds(PERIOD_D1)-1;
      datetime stop_time=day_rate[0].time;
      //--- if the indicator is launched on weekends or holidays when there are no ticks, you should first open the daily chart of the symbol
      //--- if it was not possible to get the bars of the current timeframe for the specified day, leave until the next call of OnCalculate()
      if(CopyRates(Symbol(), PERIOD_CURRENT, start_time, stop_time, bars_in_day)==-1)
         return(prev_calculated);

      //--- we go through all the bars of the current day in a loop and mark the price cells that the bars fall into
      int size=ArraySize(bars_in_day);
      for(int i=0; i<size; i++)
        {
         //--- calculate the range of price level indices in the daily range
         int         start_box=(int)((bars_in_day[i].low-low_day)/point);  // index of the first cell of the price array corresponding to the Low price of the current i bar
         int         stop_box =(int)((bars_in_day[i].high-low_day)/point); // index of the last cell of the price array corresponding to the High price of the current i bar

         //--- get the bar hour by the loop index
         MqlDateTime bar_time;
         TimeToStruct(bars_in_day[i].time, bar_time);
         uint        hour=bar_time.hour;

         //--- determine which session the bar belongs to by the bar hour
         //--- American session
         if(hour>=InpAmericaStartHour)
           {
            //--- in the American session array, in cells from start_box to stop_box, increase the bar counters
            for(int ind=start_box; ind<stop_box; ind++)
               boxes_america[ind]++;
           }
         //--- Europe or Asia
         else
           {
            //--- European session
            if(hour>=InpEuropeStartHour && hour<InpAmericaStartHour)
               //--- in the European session array, in cells from start_box to stop_box, increase the bar counters
               for(int ind=start_box; ind<stop_box; ind++)
                  boxes_europe[ind]++;
            //--- Asian session
            else
               //--- in the Asian session array, in cells from start_box to stop_box, increase the bar counters
               for(int ind=start_box; ind<stop_box; ind++)
                  boxes_asia[ind]++;
           }
        }

      //--- draw a market profile based on the created arrays of price levels
      //---  market profile on the chart is drawn with segments of colored lines using the color specified in the settings for each trading session
      //--- segments are drawn as rectangle objects with the height of the rectangle equal to one price point and the width equal to the distance to the next bar to the right
      
      //--- define the day for the name of the graphical object and the width of the rectangle
      string day_prefix=TimeToString(day_rate[0].time, TIME_DATE);
      int    box_length=PeriodSeconds(PERIOD_CURRENT);

      //--- Asian session
      //--- in a loop by the number of points of the daily bar
      for(int ind=0; ind<day_size; ind++)
        {
         //--- if the Asian session array is full
         if(boxes_asia[ind]>0)
           {
            //--- get the next price by adding the number of 'ind' points to the Low price of the daily bar
            //--- get the start time of the segment (opening time of the daily bar)
            //--- and the end time of the segment (opening time of the daily bar + the number of bars stored in the 'ind' cell of the boxes_asia[] array)
            double   price=low_day+ind*point;
            datetime time1=day_rate[0].time;
            datetime time2=time1+boxes_asia[ind]*box_length*InpMultiplier;
            //--- create a prefix for the name of the Asian session graphical object
            string   prefix=ExtPrefixUniq+"_"+day_prefix+"_Asia_"+StringFormat("%.5f", price);

            //--- draw a rectangle (line segment) at the calculated coordinates with the color for the Asian session
            DrawBox(prefix, price, time1, time2, InpAsiaSession);
           }
        }

      //--- European session
      for(int ind=0; ind<day_size; ind++)
        {
         //--- if the European session array is full
         if(boxes_europe[ind]>0)
           {
            //--- get the next price by adding the number of 'ind' points to the Low price of the daily bar
            //--- get the start time of the segment (opening time of the daily bar + time of the right edge of the Asian session profile)
            //--- and the end time of the segment (start time of the European session segment + the number of bars stored in the 'ind' cell of the boxes_europe[] array)
            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;
            //--- create a prefix for the name of the graphical object of the European session
            string   prefix=ExtPrefixUniq+"_"+day_prefix+"_Europe_"+StringFormat("%.5f", price);

            //--- draw a rectangle (line segment) at the calculated coordinates with the color for the European session
            DrawBox(prefix, price, time1, time2, InpEuropeSession);
           }
        }

      //--- American session
      for(int ind=0; ind<day_size; ind++)
        {
         //--- if the American session array is full
         if(boxes_america[ind]>0)
           {
            //--- get the next price by adding the number of 'ind' points to the Low price of the daily bar
            //--- get the start time of the segment (opening time of the daily bar + time of the right edge of the Asian session profile + time of the right edge of the European session profile)
            //--- and the end time of the segment (start time of the American session segment + the number of bars stored in the 'ind' cell of the boxes_america[] array) 
            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;
            //--- create a prefix for the name of the American session graphical object
            string   prefix=ExtPrefixUniq+"_"+day_prefix+"_America_"+StringFormat("%.5f", price);

            //--- draw a rectangle (line segment) at the calculated coordinates with the color for the American session
            DrawBox(prefix, price, time1, time2, InpAmericaSession);
           }
        }
     }

//--- when the loop is complete, redraw the chart
   ChartRedraw(0);

//--- return the number of bars for the next OnCalculate call
   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);  // index of the first cell of the price array corresponding to the Low price of the current i bar
      int         stop_box =(int)((bars_in_day[i].high-low_day)/point); // index of the last cell of the price array corresponding to the High price of the current i bar
      
      
    • 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.87 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
Silk Road Trading LLC
Ryan L Johnson | 14 jul 2025 en 13:50

Código base

Volumen Perfil + Gama v6.0

Olexiy Polyakov, 2016.06.30 12:46

Volume Profile + Range v6.0 (antiguo TPO). Distribución de operaciones por niveles de precios en un intervalo de tiempo determinado. Se muestra como un histograma.

Silk Road Trading LLC
Ryan L Johnson | 15 jul 2025 en 12:55

Artículos

Indicador de perfil de mercado

Artyom Trishkin, 2025.07.14 11:49

En este artículo, vamos a considerar el indicador Market Profile. Vamos a averiguar lo que se esconde detrás de este nombre, tratar de entender sus principios de funcionamiento y echar un vistazo a su versión terminal (MarketProfile).

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).