English Русский 中文 Deutsch 日本語
preview
Introducción a MQL5 (Parte 15): Guía para principiantes sobre cómo crear indicadores personalizados (IV)

Introducción a MQL5 (Parte 15): Guía para principiantes sobre cómo crear indicadores personalizados (IV)

MetaTrader 5Sistemas comerciales |
67 0
Israel Pelumi Abioye
Israel Pelumi Abioye

Introducción

¡Bienvenido de nuevo a la serie Introducción a MQL5! Descubrirás que este artículo se basa directamente en las ideas y técnicas que ya hemos tratado en artículos anteriores. Dado que utilizaremos gran parte de lo que hemos aprendido hasta ahora, esta parte parecerá más una continuación que un nuevo comienzo. A estas alturas, ya deberías tener una sólida comprensión de los fundamentos de MQL5, y en este artículo daremos un paso más allá combinando esos conocimientos para desarrollar indicadores personalizados más interesantes.

Tu valía depende de los proyectos en los que hayas trabajado en MQL5, por lo que esta serie siempre adopta un enfoque basado en proyectos. Es el método más útil para aprender y desarrollarse personalmente. En esta parte de la serie crearemos un indicador capaz de reconocer tendencias, utilizar rupturas de estructura y generar señales de compra y venta. El punto de entrada, el stop loss y varios niveles de take profit están incluidos en estas señales, lo que le proporciona una estrategia completa que puede probar y desarrollar. En este artículo, aprenderás a diseñar indicadores personalizados en MQL5 utilizando conceptos de acción del precio. Para crear una estrategia que siga las tendencias, aprenderás a reconocer estructuras importantes del mercado, como máximos más altos, mínimos más altos y máximos más bajos, mínimos más bajos.

En este artículo aprenderás:

  • Cómo crear un indicador de acción del precio.
  • Reconocer puntos importantes como mínimo (L), máximo (H), mínimo más alto (HL), máximo más alto (HH), mínimo más bajo (LL) y máximo más bajo (LH) para comprender la estructura de una tendencia alcista y bajista.
  • Dibujar la zona de prima y descuento basándose en puntos clave de la tendencia y marcar el nivel de retroceso del 50 %.
  • Cómo aplicar la relación riesgo-recompensa al calcular los objetivos de ganancia potenciales en una configuración de tendencia alcista.
  • Calcular y marcar el punto de entrada, el stop loss (SL) y los múltiples niveles de take profit (TP) basándose en la estructura de la tendencia.


1. Configuración del proyecto

1.1. Cómo funciona el indicador

El indicador identificará un mínimo, un máximo, un mínimo más alto y un máximo más alto para indicar una tendencia alcista para las señales de compra. A continuación, se determinará el nivel de retroceso del 50 % entre el mínimo más alto y el máximo más alto. Una ruptura en la estructura por encima del máximo más alto iniciará la entrada, y el nivel de retroceso del 50 % servirá como stop loss. Una relación riesgo-recompensa de 1:1 será el objetivo de Take Profit 1, y una relación de 1:2 será el objetivo de Take Profit 2.

Figura 1. Tendencia alcista

Para identificar una tendencia bajista para las señales de venta, el indicador identificará primero un máximo, un mínimo, un máximo más bajo y un mínimo más bajo. A continuación, calculará el retroceso del 50 % entre el máximo más bajo y el mínimo más bajo. TP1 será 1:1, TP2 será 1:2, el stop loss estará en el nivel del 50 % y la entrada se producirá cuando se rompa por debajo del mínimo más bajo.

Figura 2. Tendencia bajista


2. Construcción del indicador de acción del precio

Todas las estrategias de trading pueden transformarse en un indicador, solo que aún no se han visualizado. Cualquier elemento que se ajuste a un conjunto de directrices puede codificarse y mostrarse en el gráfico, ya sea la oferta y la demanda, la evolución de los precios o el soporte y la resistencia. Aquí es donde entra en juego MQL5. Para los operadores algorítmicos, es uno de los lenguajes de programación más potentes y sencillos, que permite transformar cualquier lógica de negociación en una herramienta útil y visualmente atractiva. En esta sección comenzaremos a desarrollar un indicador que analiza el movimiento de los precios, reconoce la estructura del mercado, como máximos, mínimos, máximos más altos y mínimos más bajos, y luego utiliza esos datos para generar señales de compra y venta reveladoras que incluyen niveles de entrada, stop loss y take profit.

En el capítulo uno, describí el objetivo del proyecto y cómo el indicador detectará tendencias, identificará rupturas estructurales y generará señales comerciales completas que incluyen entrada, stop loss y take profit. Ahora comenzaremos a poner en práctica todo lo aprendido en MQL5 en este capítulo. Tomaremos la lógica que discutimos y comenzaremos, paso a paso, a implementarla en el código.

2.1. Identificar los máximos y mínimos

Encontrar los máximos y mínimos oscilantes es la primera etapa en la creación de nuestro indicador de acción del precio. Estos momentos significativos de inflexión en el mercado ayudan a identificar la estructura de la tendencia. Al comparar el máximo o mínimo de la vela actual con el de las velas anteriores y posteriores, podemos identificarlas en MQL5. La detección de máximos más altos, mínimos más altos, máximos más bajos y mínimos más bajos, todos ellos cruciales para identificar patrones y rupturas estructurales, se basará en esto.

Ejemplos:
//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOWS                                          |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(low[index] > low[index - i] || low[index] > low[index + i])
         return false; 
     }
   return true; 
  }


//+------------------------------------------------------------------+
//| FUNCTION FOR SWING HIGHS                                         |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(high[index] < high[index - i] || high[index] < high[index + i])
         return false; 
     }
   return true; 
  }

Las dos funciones se utilizaron en el artículo anterior y ayudan a identificar tanto los máximos como los mínimos de oscilación.

2.2. Tendencia alcista

Utilizando la estructura del mercado, este indicador debe verificar primero si existe una tendencia alcista antes de indicar una señal de compra. Un mínimo, un máximo, un mínimo más alto y un máximo más alto son los principales puntos de precio que deben identificarse para lograrlo. Este patrón denota una tendencia alcista, lo que significa que los compradores tienen el control y que es probable que el mercado siga subiendo. Tras confirmarse este patrón, el indicador estará listo para generar una señal de compra legítima.

Figura 3. Tendencia alcista

Ejemplo:

// CHART ID
long chart_id = ChartID();

// Input parameters
input int  LookbackBars = 10;   // Number of bars to look back/forward for swing points
input int  bars_check   = 1000; // Number of bars to check for swing points
input bool show_bullish = true; //Show Buy Signals

// Variables for Bullish Market Structure

double L;              // Low: the starting low point in the up trend
datetime L_time;       // Time of the low
string L_letter;       // Label for the low point (e.g., "L")

double H;              // High: the first high after the low
datetime H_time;       // Time of the high
string H_letter;       // Label for the high point (e.g., "H")

double HL;             // Higher Low: the next low that is higher than the first low
datetime HL_time;      // Time of the higher low
string HL_letter;      // Label for the higher low point (e.g., "HL")

double HH;             // Higher High: the next high that is higher than the first high
datetime HH_time;      // Time of the higher high
string HH_letter;      // Label for the higher high point (e.g., "HH")

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//---
   if(show_bullish == true) // Check if the bullish trend is to be displayed
     {
      if(rates_total >= bars_check) // Ensure enough bars are available for analysis
        {
         // Loop through the price data starting from a certain point based on bars_check and LookbackBars
         for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
           {
            // Check if the current bar is a swing low
            if(IsSwingLow(low, i, LookbackBars))
              {
               // Store the values for the swing low
               L = low[i];
               L_time = time[i];
               L_letter = StringFormat("Low%d", i);

               // Loop through further to find a swing high after the low
               for(int j = i; j < rates_total - LookbackBars; j++)
                 {
                  // Check if the current bar is a swing high and occurs after the identified swing low
                  if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time)
                    {
                     // Store the values for the swing high
                     H = high[j];
                     H_time = time[j];
                     H_letter = StringFormat("High%d", j);

                     // Loop further to find a higher low after the swing high
                     for(int k = j; k < rates_total - LookbackBars; k++)
                       {
                        // Check if the current bar is a swing low and occurs after the swing high
                        if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
                          {
                           // Store the values for the higher low
                           HL = low[k];
                           HL_time = time[k];
                           HL_letter = StringFormat("Higher Low%d", j);

                           // Loop further to find a higher high after the higher low
                           for(int l = j ; l < rates_total - LookbackBars; l++)
                             {
                              // Check if the current bar is a swing high and occurs after the higher low
                              if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                                {
                                 // Store the values for the higher high
                                 HH = high[l];
                                 HH_time = time[l];
                                 HH_letter = StringFormat("Higher High%d", l);

                                 // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
                                 if(L < H && HL < H && HL > L && HH > H)
                                   {
                                    // Create and display text objects for Low, High, Higher Low, and Higher High on the chart
                                    ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L);
                                    ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
                                    ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H);
                                    ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
                                    ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL);
                                    ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
                                    ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH);
                                    ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH");
                                    ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15);
                                   }

                                 break; // Exit the loop once the pattern is found
                                }
                             }

                           break; // Exit the loop once the higher low is found
                          }
                       }

                     break; // Exit the loop once the higher high is found
                    }
                 }
              }
           }
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Salida:

Figura 4. Oscilaciones alcistas

Explicación:

Los parámetros de entrada que se establecen al principio del código se utilizan para examinar la estructura del mercado. Para determinar los máximos y mínimos oscilantes y garantizar la relevancia en el mercado actual, LookbackBars especifica el número de barras que se evalúan antes y después de la barra actual. Mientras tanto, bars_check controla el número total de barras de datos de precios que se examinarán, lo que permite al script buscar hasta 1000 barras en busca de posibles patrones alcistas.

Aunque también requerirá más potencia de cálculo, los valores más altos para bars_check indican que el algoritmo considerará un rango más amplio de datos al buscar estas ubicaciones. Una entrada booleana llamada show_bullish controla si deben mostrarse las señales de compra o las señales alcistas. La aplicación procederá a analizar el movimiento de los precios y determinará la estructura alcista del mercado basándose en los puntos de oscilación si show_bullish se establece en verdadero. Incluso si se cumplen los requisitos, el script no trazará ni resaltará ninguna estructura alcista si está configurado en falso.

La comprobación de show_bullish == true es la primera acción que se lleva a cabo en términos de la lógica del indicador. Esto garantiza que solo cuando desee ver señales de compra se identificará la estructura alcista. A continuación, el programa comprueba si hay suficientes datos de precios disponibles para el análisis, si se cumplen los criterios. La condición if (rates_total >= bars_check) se utiliza para verificar esto. Para evitar errores debidos a datos inadecuados, el análisis se omite si hay menos barras disponibles de las necesarias. A continuación, el script recorre los datos de precios para encontrar puntos de oscilación si se cumple el criterio.

El bucle externo comienza con i, que busca mínimos oscilantes legítimos escaneando los datos de precios hacia atrás desde la barra más reciente. La función IsSwingLow() determina si la barra actual es la más baja del rango especificado por LookbackBars, identificando así un mínimo oscilante. El precio y el momento del mínimo se registran en las variables L y L_time tan pronto como se identifica un mínimo oscilante. Esto sienta las bases para la fase posterior del proceso de descubrimiento del patrón alcista. Tras identificar el mínimo oscilante, el programa busca otro máximo oscilante. Utilizando IsSwingHigh(), el segundo bucle, indexado por j, busca un swing alto en cada barra sucesiva. Los valores de cualquier máximo oscilante que se descubra que sigue al mínimo se registran en H y H_time. Esto crea el segmento inicial de la estructura alcista del mercado, que consiste en un mínimo y un máximo.

Después del máximo del swing, el tercer bucle, que está indexado por k, busca un mínimo más alto. El script utiliza IsSwingLow() una vez más para encontrar un mínimo más alto, que se define como un mínimo mayor que el mínimo inicial L. Cuando se descubre un mínimo más alto, HL y HL_time se actualizan con su valor y tiempo. Tras identificar este mínimo más alto, el programa sigue buscando el máximo más alto posterior. El swing alto que sigue al mínimo más alto se comprueba en el cuarto bucle, que está indexado por l. Sus valores se guardan en HH y HH_time si se descubre este máximo superior. El código determina si los cuatro puntos críticos (mínimo, máximo, mínimo más alto y máximo más alto) siguen un patrón alcista legítimo después de haber sido detectados. El primer mínimo debe ser inferior al primer máximo, el mínimo superior debe ser inferior al primer máximo, el mínimo superior debe ser superior al primer mínimo y el máximo superior debe ser superior al primer máximo. Estos criterios se comprueban mediante la condición if (L < H && HL < H && HL > L && HH > H). Esto confirma una tendencia alcista al garantizar que el patrón sigue la secuencia prevista de máximos y mínimos cada vez más altos.

A continuación, el programa crea y muestra objetos de texto en el gráfico para resaltar los puntos que se han identificado si se cumplen todos estos requisitos. El gráfico muestra los puntos como etiquetas en los períodos y precios correspondientes: Mínimo (L) Máximo (H), Mínimo más alto (HL) y Máximo más alto (HH). ObjectCreate() se utiliza para crear los objetos de texto, mientras que ObjectSetInteger() y ObjectSetString() se utilizan para establecer sus características, incluyendo el tamaño y el color de la fuente. Con los puntos indicados de forma destacada como referencia, el usuario ahora puede reconocer fácilmente la estructura alcista en el gráfico. En resumen, el objetivo del programa es encontrar un patrón de máximos y mínimos cada vez más altos en los datos de precios para evaluar si se trata de una estructura de mercado alcista. Para ello, analiza los puntos de oscilación dentro de un rango predeterminado de barras, registra la información relevante y determina si la estructura muestra el patrón adecuado. Si se verifica el patrón, el usuario puede verlo visualmente en el gráfico. Los parámetros de entrada, que permiten modificaciones según las preferencias del usuario, rigen todo el procedimiento.

2.2.1. Mapeo de los niveles premium y de descuento desde el mínimo más alto al máximo más alto

Una vez identificados los puntos de inflexión de la estructura alcista del mercado —el mínimo (L), el máximo (H), el mínimo superior (HL) y el máximo superior (HH)—, el siguiente paso es dibujar un rectángulo desde el mínimo superior hasta el máximo superior. El recuadro muestra visualmente el rango de movimiento de precios entre estos dos puntos de oscilación significativos. A continuación, se calcula este rango y el nivel de retroceso del 50 %, un límite crítico entre dos zonas de precios, se divide por la mitad.

La división ayuda a definir los términos «zona premium» y «zona de descuento». Por debajo del 50 %, la zona de descuento denota el rango de precios que se consideran más ventajosos o «más baratos» para posibles compras. La zona premium, por otro lado, se encuentra por encima del 50 % y denota tarifas comparativamente «caras». Para optimizar la relación riesgo-recompensa, los operadores suelen optar por comprar desde la zona de descuento en muchos métodos de negociación; sin embargo, para este indicador específico, adoptamos una estrategia ligeramente diferente.

Figura 5. Premium y descuento

En este caso, solo nos interesa comprar cuando el mercado cotiza por encima de la zona premium o rompe la estructura de máximos superiores. Este patrón indica que la estructura alcista sigue vigente y que es probable que el precio siga subiendo. Para seguir la tendencia y reducir la posibilidad de comprar en un retroceso o reversión, se recomienda esperar a que se supere el máximo más alto o la zona premium.

Ejemplo:

// CHART ID
long chart_id = ChartID();

// Input parameters
input int  LookbackBars = 10;   // Number of bars to look back/forward for swing points
input int  bars_check   = 1000; // Number of bars to check for swing points
input bool show_bullish = true; //Show Buy Signals

// Variables for Bullish Market Structure

double L;              // Low: the starting low point in the up trend
datetime L_time;       // Time of the low
string L_letter;       // Label for the low point (e.g., "L")

double H;              // High: the first high after the low
datetime H_time;       // Time of the high
string H_letter;       // Label for the high point (e.g., "H")

double HL;             // Higher Low: the next low that is higher than the first low
datetime HL_time;      // Time of the higher low
string HL_letter;      // Label for the higher low point (e.g., "HL")

double HH;             // Higher High: the next high that is higher than the first high
datetime HH_time;      // Time of the higher high
string HH_letter;      // Label for the higher high point (e.g., "HH")

// Variables for Premium and Discount
string pre_dis_box;     // Name/ID for the premium-discount zone box (rectangle object on chart)
double lvl_50;          // The price level representing the 50% retracement between Higher Low and Higher High
string lvl_50_line;     // Name/ID for the horizontal line marking the 50% level


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//---
   if(show_bullish == true) // Check if the bullish trend is to be displayed
     {
      if(rates_total >= bars_check) // Ensure enough bars are available for analysis
        {
         // Loop through the price data starting from a certain point based on bars_check and LookbackBars
         for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
           {
            // Check if the current bar is a swing low
            if(IsSwingLow(low, i, LookbackBars))
              {
               // Store the values for the swing low
               L = low[i];
               L_time = time[i];
               L_letter = StringFormat("Low%d", i);

               // Loop through further to find a swing high after the low
               for(int j = i; j < rates_total - LookbackBars; j++)
                 {
                  // Check if the current bar is a swing high and occurs after the identified swing low
                  if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time)
                    {
                     // Store the values for the swing high
                     H = high[j];
                     H_time = time[j];
                     H_letter = StringFormat("High%d", j);

                     // Loop further to find a higher low after the swing high
                     for(int k = j; k < rates_total - LookbackBars; k++)
                       {
                        // Check if the current bar is a swing low and occurs after the swing high
                        if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
                          {
                           // Store the values for the higher low
                           HL = low[k];
                           HL_time = time[k];
                           HL_letter = StringFormat("Higher Low%d", j);

                           // Loop further to find a higher high after the higher low
                           for(int l = j ; l < rates_total - LookbackBars; l++)
                             {
                              // Check if the current bar is a swing high and occurs after the higher low
                              if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                                {
                                 // Store the values for the higher high
                                 HH = high[l];
                                 HH_time = time[l];
                                 HH_letter = StringFormat("Higher High%d", l);

                                 // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
                                 if(L < H && HL < H && HL > L && HH > H)
                                   {
                                    // Create and display text objects for Low, High, Higher Low, and Higher High on the chart
                                    ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L);
                                    ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
                                    ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H);
                                    ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
                                    ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL);
                                    ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
                                    ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15);

                                    ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH);
                                    ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH");
                                    ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen);
                                    ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15);


                                    // Calculate the 50% retracement level between the Higher Low and Higher High
                                    lvl_50 = HL + ((HH - HL)/2);

                                    // Generate unique names for the premium-discount box and the 50% level line using the current loop index
                                    pre_dis_box = StringFormat("Premium and Discount Box%d", i);
                                    lvl_50_line = StringFormat("Level 50 Line%d", i);

                                    // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High
                                    ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH);

                                    // Create a trend line (horizontal line) marking the 50% retracement level
                                    ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);

                                    // Set the color of the premium-discount box to dark green
                                    ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen);

                                    // Set the color of the 50% level line to dark green
                                    ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen);

                                    // Set the width of the premium-discount box for better visibility
                                    ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2);

                                    // Set the width of the 50% level line for better visibility
                                    ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2);


                                   }

                                 break; // Exit the loop once the pattern is found
                                }
                             }

                           break; // Exit the loop once the higher low is found
                          }
                       }

                     break; // Exit the loop once the higher high is found
                    }
                 }
              }
           }
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Salida:


Figura 6. Premiums y descuentos en el gráfico

Explicación:

El código determina el nivel de retroceso del 50 %, que es la mitad entre el mínimo más alto (HL) y el máximo más alto (HH). Las zona premium (arriba) y de descuento (abajo) están separadas por este nivel. El punto medio entre HL y HH se determina utilizando la fórmula lvl_50 = HL + ((HH - HL)/2);. A continuación, el índice de bucle i se incluye en los nombres de dos variables de cadena, pre_dis_box y lvl_50_line. Estos sirven como identificadores distintivos para los elementos visuales que dibujaremos en el gráfico. Cada dibujo será único y no sustituirá a los anteriores si se incluye el índice de bucle.

Se crea un rectángulo en el gráfico mediante la línea ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); que representa gráficamente la transición desde el mínimo más alto al máximo más alto. Los operadores pueden determinar rápidamente el rango de la oscilación alcista más reciente con el uso de este cuadro. El extremo del rectángulo se fija en una barra futura (l + LookbackBars) utilizando el precio HH, mientras que su inicio se fija en el tiempo y el precio HL. Esto mantiene la caja visible al extenderla ligeramente hacia el futuro.

A continuación, se traza una línea horizontal en el gráfico en el nivel del 50 % mediante la línea ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);. Este umbral es importante porque, según la lógica de este indicador, solo buscamos posibles señales de compra cuando el mercado cotiza por encima de la zona de prima, o por encima del nivel del 50 % de la última subida. Los colores del cuadro y de la línea se establecen en clrDarkGreen, y se utiliza la función ObjectSetInteger para aumentar su grosor (o anchura) a 2 para que ambos sean claramente visibles. El mercado debe superar completamente el HH para que una señal de compra se considere válida; en otras palabras, el precio debe cerrar fuera y por encima de toda la zona de prima. En otras palabras, solo queremos comprar cuando la estructura del mercado es claramente alcista y ha superado el máximo anterior (HH). 

2.2.2. Indicar el punto de entrada, el stop loss y el take profit

Una vez marcadas correctamente las zonas premium y de descuento entre el máximo más alto (HH) y el mínimo más alto (HL), el siguiente paso es encontrar posibles señales de compra. Esperar a que una barra alcista rompa por encima del máximo más alto (HH), lo que indica que el mercado sigue moviéndose con fuerza al alza, es la clave para una configuración de compra legítima. 

Pero cruzar solo el HH no es suficiente. La barra alcista debe cerrar por encima del HH para que la entrada se verifique, ya que debemos asegurarnos de que la ruptura es real. El hecho de que el precio haya cerrado por encima del HH sugiere que existe una demanda de compra continua y que probablemente seguirá subiendo. El punto de entrada se establece al cierre de la barra alcista que rompe por encima del HH después de que se haya verificado la ruptura. Estamos seguros de que el mercado ha demostrado suficiente fortaleza actualmente como para que podamos entrar en el mercado.

Establecemos el Stop Loss (SL) en el nivel del 50 % (lvl_50), que es el punto medio entre el HL y el HH, para protegernos contra una posible reversión. Para evitar quedar atrapados en una posible caída en la zona de descuento (por debajo del nivel del 50 %), lo que puede indicar un cambio en el estado de ánimo del mercado, hemos colocado el SL aquí. Nuestro método se basa en la relación riesgo-recompensa (R:R) para los niveles de Take Profit (TP). El objetivo de ganancia para el primer nivel de Take Profit, TP1, es igual a la distancia de riesgo entre el punto de entrada y el SL, ya que se establece en una relación R:R de 1:1. El objetivo de beneficio para el segundo nivel de Take Profit, o TP2, es el doble de la distancia entre el punto de entrada y el SL, con una configuración R:R de 1:2. Para los operadores que prefieren asegurar beneficios parciales en el TP1, estos dos niveles de take profit ofrecen flexibilidad, permitiéndoles dejar una parte de la operación abierta para beneficiarse de ganancias adicionales en caso de que el mercado continúe su tendencia positiva.

Ejemplo:

// Variables for Entry, Stop Loss, and Take Profit
string entry_line;        // Line object to represent the entry point on the chart
string entry_txt;         // Text object for displaying "BUY" at the entry point
double lvl_SL;            // Stop Loss level (set at the 50% retracement level)
string lvl_sl_line;       // Line object for representing the Stop Loss level
string lvl_sl_txt;        // Text object for labeling the Stop Loss level
double TP1;               // Take Profit 1 level (1:1 risk-reward ratio)
double TP2;               // Take Profit 2 level (1:2 risk-reward ratio)
string lvl_tp_line;       // Line object for representing the Take Profit 1 level
string lvl_tp2_line;      // Line object for representing the Take Profit 2 level
string lvl_tp_txt;        // Text object for labeling the Take Profit 1 level
string lvl_tp2_txt;       // Text object for labeling the Take Profit 2 level
string buy_object;        // Arrow object to indicate the Buy signal on the chart
if(show_bullish == true) // Check if the bullish trend is to be displayed
  {
   if(rates_total >= bars_check) // Ensure enough bars are available for analysis
     {
      // Loop through the price data starting from a certain point based on bars_check and LookbackBars
      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         // Check if the current bar is a swing low
         if(IsSwingLow(low, i, LookbackBars))
           {
            // Store the values for the swing low
            L = low[i];
            L_time = time[i];
            L_letter = StringFormat("Low%d", i);

            // Loop through further to find a swing high after the low
            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               // Check if the current bar is a swing high and occurs after the identified swing low
               if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time)
                 {
                  // Store the values for the swing high
                  H = high[j];
                  H_time = time[j];
                  H_letter = StringFormat("High%d", j);

                  // Loop further to find a higher low after the swing high
                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     // Check if the current bar is a swing low and occurs after the swing high
                     if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
                       {
                        // Store the values for the higher low
                        HL = low[k];
                        HL_time = time[k];
                        HL_letter = StringFormat("Higher Low%d", j);

                        // Loop further to find a higher high after the higher low
                        for(int l = j ; l < rates_total - LookbackBars; l++)
                          {
                           // Check if the current bar is a swing high and occurs after the higher low
                           if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                             {
                              // Store the values for the higher high
                              HH = high[l];
                              HH_time = time[l];
                              HH_letter = StringFormat("Higher High%d", l);

                              // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
                              if(L < H && HL < H && HL > L && HH > H)
                                {
                                 // Create and display text objects for Low, High, Higher Low, and Higher High on the chart
                                 ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L);
                                 ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
                                 ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
                                 ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15);

                                 ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H);
                                 ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
                                 ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
                                 ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15);

                                 ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL);
                                 ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
                                 ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
                                 ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15);

                                 ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH);
                                 ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH");
                                 ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen);
                                 ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15);


                                 // Calculate the 50% retracement level between the Higher Low and Higher High
                                 lvl_50 = HL + ((HH - HL)/2);

                                 // Generate unique names for the premium-discount box and the 50% level line using the current loop index
                                 pre_dis_box = StringFormat("Premium and Discount Box%d", i);
                                 lvl_50_line = StringFormat("Level 50 Line%d", i);

                                 // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High
                                 ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH);

                                 // Create a trend line (horizontal line) marking the 50% retracement level
                                 ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);

                                 // Set the color of the premium-discount box to dark green
                                 ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen);

                                 // Set the color of the 50% level line to dark green
                                 ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen);

                                 // Set the width of the premium-discount box for better visibility
                                 ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2);

                                 // Set the width of the 50% level line for better visibility
                                 ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2);


                                 for(int m = l; m < rates_total-1; m++)
                                   {

                                    if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars])
                                      {

                                       TP1 = close[m] + (close[m] - lvl_50);
                                       TP2 = TP1 + (close[m] - lvl_50);

                                       entry_line = StringFormat("Entry%d", m);
                                       lvl_sl_line = StringFormat("SL%d", m);
                                       lvl_tp_line =  StringFormat("TP%d", m);
                                       lvl_tp2_line =  StringFormat("TP 2%d", m);

                                       ObjectCreate(chart_id,entry_line,OBJ_TREND,0,HL_time,close[m],time[m],close[m]);
                                       ObjectCreate(chart_id,lvl_sl_line,OBJ_TREND,0,HL_time,lvl_50,time[m],lvl_50);
                                       ObjectCreate(chart_id,lvl_tp_line,OBJ_TREND,0,HL_time,TP1,time[m],TP1);
                                       ObjectCreate(chart_id,lvl_tp2_line,OBJ_TREND,0,HL_time,TP2,time[m],TP2);

                                       ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2);

                                       ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_COLOR,clrDarkGreen);

                                       entry_txt = StringFormat("Entry Text%d", m);
                                       lvl_sl_txt = StringFormat("SL Text%d", m);
                                       lvl_tp_txt = StringFormat("TP 1 Text%d", m);
                                       lvl_tp2_txt = StringFormat("TP 2 Text%d", m);

                                       ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50);
                                       ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL");
                                       ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]);
                                       ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY");
                                       ObjectSetInteger(chart_id,entry_txt,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1);
                                       ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1");
                                       ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2);
                                       ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2");
                                       ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_COLOR,clrDarkGreen);
                                       ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15);

                                       buy_object = StringFormat("Buy Object%d", m);
                                       ObjectCreate(chart_id,buy_object,OBJ_ARROW_BUY,0,time[m],close[m]);

                                       break;
                                      }
                                   }


                                }

                              break; // Exit the loop once the pattern is found
                             }
                          }

                        break; // Exit the loop once the higher low is found
                       }
                    }

                  break; // Exit the loop once the higher high is found
                 }
              }
           }
        }
     }
  }
Salida:

Figura 7. Señal de compra

Se puede observar que las zonas premium y de descuento, así como los marcadores de señales de compra, no están dibujados con precisión en la imagen anterior. Esto se debe a que se pasaron por alto ciertas condiciones antes de dibujar los objetos en el gráfico. Para mejorar el enfoque, debemos incluir más verificaciones para asegurarnos de que la señal de compra sea legítima. Antes de dibujar cualquier objeto en el gráfico, primero debemos asegurarnos de que una vela realmente haya rompido el máximo más alto (HH). Una ruptura del HH significa una continuación de la tendencia alcista, lo cual es necesario para que la señal de compra se considere legítima, por lo que este es un criterio fundamental. No debemos comenzar los cálculos de entrada y gestión de riesgos hasta que se haya cumplido este requisito.

A continuación, se debe contar el número de barras desde el mínimo más alto (HL) hasta el final del cuadro premium y descuento. Esto garantiza que la evolución del precio se mantenga dentro de un rango aceptable y nos ayuda a comprender hasta qué punto se ha movido el mercado. Una vez finalizado este recuento, debemos confirmar que el precio de cierre de la barra alcista, que rompió el máximo más alto (HH), se encuentra cerca del cuadro premium y descuento. Esto garantiza que la señal de compra no se aleje demasiado de la estructura de mercado prevista y se produzca dentro de un rango de precios razonable.

Ejemplo:

// Declare variables to count bars
int n_bars;     // Number of bars from Higher Low to the end of the Premium/Discount box
int n_bars_2;   // Number of bars from the end of the Premium/Discount box to the bullish bar that broke HH
 if(show_bullish == true) // Check if the bullish trend is to be displayed
     {
      if(rates_total >= bars_check) // Ensure enough bars are available for analysis
        {
         // Loop through the price data starting from a certain point based on bars_check and LookbackBars
         for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
           {
            // Check if the current bar is a swing low
            if(IsSwingLow(low, i, LookbackBars))
              {
               // Store the values for the swing low
               L = low[i];
               L_time = time[i];
               L_letter = StringFormat("Low%d", i);

               // Loop through further to find a swing high after the low
               for(int j = i; j < rates_total - LookbackBars; j++)
                 {
                  // Check if the current bar is a swing high and occurs after the identified swing low
                  if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time)
                    {
                     // Store the values for the swing high
                     H = high[j];
                     H_time = time[j];
                     H_letter = StringFormat("High%d", j);

                     // Loop further to find a higher low after the swing high
                     for(int k = j; k < rates_total - LookbackBars; k++)
                       {
                        // Check if the current bar is a swing low and occurs after the swing high
                        if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time)
                          {
                           // Store the values for the higher low
                           HL = low[k];
                           HL_time = time[k];
                           HL_letter = StringFormat("Higher Low%d", j);

                           // Loop further to find a higher high after the higher low
                           for(int l = j ; l < rates_total - LookbackBars; l++)
                             {
                              // Check if the current bar is a swing high and occurs after the higher low
                              if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time)
                                {
                                 // Store the values for the higher high
                                 HH = high[l];
                                 HH_time = time[l];
                                 HH_letter = StringFormat("Higher High%d", l);


                                 // Loop through the bars to check for the conditions for entry
                                 for(int m = l; m < rates_total-1; m++)
                                   {
                                    // Check if the current bar is a bullish bar and if the price has broken the higher high (HH)
                                    if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars])
                                      {
                                       // Count the bars between HL_time and the end of the Premium/Discount box
                                       n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]);

                                       // Count the bars between the end of the Premium/Discount box and the candle that broke HH
                                       n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]);

                                       // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High
                                       if(L < H && HL < H && HL > L && HH > H && open[l+LookbackBars] <= HH && n_bars_2 < n_bars)
                                         {
                                          // Create and display text objects for Low, High, Higher Low, and Higher High on the chart
                                          ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L);
                                          ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
                                          ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H);
                                          ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
                                          ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL);
                                          ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL");
                                          ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH);
                                          ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH");
                                          ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15);


                                          // Calculate the 50% retracement level between the Higher Low and Higher High
                                          lvl_50 = HL + ((HH - HL)/2);

                                          // Generate unique names for the premium-discount box and the 50% level line using the current loop index
                                          pre_dis_box = StringFormat("Premium and Discount Box%d", i);
                                          lvl_50_line = StringFormat("Level 50 Line%d", i);

                                          // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High
                                          ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH);

                                          // Create a trend line (horizontal line) marking the 50% retracement level
                                          ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);

                                          // Set the color of the premium-discount box to dark green
                                          ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen);

                                          // Set the color of the 50% level line to dark green
                                          ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen);

                                          // Set the width of the premium-discount box for better visibility
                                          ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2);

                                          // Set the width of the 50% level line for better visibility
                                          ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2);



                                          // Calculate Take Profit levels based on the 50% retracement
                                          TP1 = close[m] + (close[m] - lvl_50);    // TP1 at 1:1 risk-reward ratio
                                          TP2 = TP1 + (close[m] - lvl_50);          // TP2 at 1:2 risk-reward ratio

                                          // Create unique object names for Entry, Stop Loss, and Take Profit lines and text
                                          entry_line = StringFormat("Entry%d", m);
                                          lvl_sl_line = StringFormat("SL%d", m);
                                          lvl_tp_line =  StringFormat("TP%d", m);
                                          lvl_tp2_line =  StringFormat("TP 2%d", m);

                                          // Create the lines on the chart for Entry, Stop Loss, and Take Profit levels
                                          ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, HL_time, close[m], time[m], close[m]);
                                          ObjectCreate(chart_id, lvl_sl_line, OBJ_TREND, 0, HL_time, lvl_50, time[m], lvl_50);
                                          ObjectCreate(chart_id, lvl_tp_line, OBJ_TREND, 0, HL_time, TP1, time[m], TP1);
                                          ObjectCreate(chart_id, lvl_tp2_line, OBJ_TREND, 0, HL_time, TP2, time[m], TP2);

                                          // Set the properties for the lines (width, color, etc.)
                                          ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 2);
                                          ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_WIDTH, 2);
                                          ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_WIDTH, 2);
                                          ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_WIDTH, 2);

                                          ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_COLOR, clrDarkGreen);

                                          // Create the text labels for Entry, Stop Loss, and Take Profit levels
                                          entry_txt = StringFormat("Entry Text%d", m);
                                          lvl_sl_txt = StringFormat("SL Text%d", m);
                                          lvl_tp_txt = StringFormat("TP 1 Text%d", m);
                                          lvl_tp2_txt = StringFormat("TP 2 Text%d", m);

                                          // Create the text objects for the Entry, Stop Loss, and Take Profit labels
                                          ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0, time[m], lvl_50);
                                          ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL");
                                          ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0, time[m], close[m]);
                                          ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY");
                                          ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, entry_txt, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0, time[m], TP1);
                                          ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1");
                                          ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_FONTSIZE, 15);

                                          ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0, time[m], TP2);
                                          ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2");
                                          ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_COLOR, clrDarkGreen);
                                          ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_FONTSIZE, 15);

                                          // Create a Buy arrow object to indicate the Buy signal on the chart
                                          buy_object = StringFormat("Buy Object%d", m);
                                          ObjectCreate(chart_id, buy_object, OBJ_ARROW_BUY, 0, time[m], close[m]);

                                          break; // Exit the loop once a Buy signal is found
                                         }
                                      }

                                   }

                                 break; // Exit the loop once the pattern is found
                                }
                             }

                           break; // Exit the loop once the higher low is found
                          }
                       }

                     break; // Exit the loop once the higher high is found
                    }
                 }
              }
           }
        }
     }

Salida:

Figura 8. Señal de compra

Explicación:

Se declararon dos variables enteras, n_bars y n_bars_2, en el espacio global del código. El número de velas (barras) entre puntos importantes en el patrón de estructura alcista del mercado viene determinado por estas variables. En concreto, n_bars es el número de barras que separan el final del cuadro Premium/Descuento (time[l + LookbackBars]) del máximo más alto (HL). Sin embargo, n_bars_2 cuenta el número de barras que separan la vela alcista que rompió el máximo más alto (HH) del final del cuadro premium/descuento. Este recuento se utiliza para evaluar si la acción del precio se ha desviado demasiado de la zona óptima de negociación o si la señal de compra sigue siendo válida.

Estas variables se emplean más adelante en el código como parte de una condición de validación adicional que refuerza el argumento a favor de una estructura de mercado alcista. Se comprueba una condición adicional n_bars_2 < n_bars después de determinar los valores mínimo, máximo, mínimo superior y máximo superior (asegurándose de que se ajustan a la estructura L < H, HL < H, HL > L y HH > H) y de verificar que el precio de apertura de la vela que cerró el cuadro premiun/descuento no es superior al máximo superior. Esto garantiza que la vela alcista de ruptura (la que rompe por encima del HH) no aparezca demasiado lejos después de la formación, lo que podría sugerir una configuración débil o inválida, y que aparezca bastante cerca del patrón.

Todas las rutinas ObjectCreate() y ObjectSet*() que se utilizaban anteriormente para dibujar los marcadores de mínimo, máximo, mínimo superior, máximo superior, cuadro premium/descuento, línea del 50 % y entrada/SL/TP del gráfico se transfirieron dentro de esta instrucción if para imponer esta inspección más estricta. Esto implica que solo cuando se cumplan todos los requisitos de estructura alcista y tiempo se producirán y mostrarán estos componentes visuales. De esta manera, el gráfico se mantiene claro y no se sobrecarga con elementos erróneos o prematuros debido a señales engañosas.

2.3. Tendencia bajista

Este indicador debe utilizar primero la estructura del mercado para validar una caída antes de poder indicar una venta. Esto se logra encontrando una serie de puntos de precios significativos: un máximo, un mínimo, un máximo más bajo y un mínimo más bajo. Este patrón demuestra que los vendedores tienen el control y que es probable que el mercado siga bajando, lo que respalda el impulso bajista. El indicador comenzará a buscar señales de venta legítimas tan pronto como se verifique esta estructura.

Tendencia bajista

Ejemplo:

// Variables for Bearish Market Structure
double LH;              // Lower High: the high formed after the initial low in a downtrend
datetime LH_time;       // Time of the Lower High
string LH_letter;       // Label used to display the Lower High on the chart (e.g., "LH")
double LL;              // Lower Low: the new low formed after the Lower High in a downtrend
datetime LL_time;       // Time of the Lower Low
string LL_letter;       // Label used to display the Lower Low on the chart (e.g., "LL")
string sell_object; // Arrow object to indicate the Sell signal on the chart
// BEARISH TREND
if(show_bearish == true)  // Check if the user enabled the bearish trend display
  {
   if(rates_total >= bars_check)  // Ensure enough candles are available for processing
     {
      // Loop through historical bars to find a swing high (potential start of bearish structure)
      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingHigh(high, i, LookbackBars))  // Detect first swing high
           {
            H = high[i];
            H_time = time[i];
            H_letter = StringFormat("High B%d", i);  // Label for the high

            // From the swing high, look for the next swing low
            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingLow(low, j, LookbackBars) && time[j] > H_time)  // Confirm next swing low
                 {
                  L = low[j];
                  L_time = time[j];
                  L_letter = StringFormat("Low B%d", j);  // Label for the low

                  // From the swing low, look for the Lower High
                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     if(IsSwingHigh(high, k, LookbackBars) && time[k] > L_time)
                       {
                        LH = high[k];
                        LH_time = time[k];
                        LH_letter = StringFormat("Lower High%d", k);  // Label for the Lower High

                        // From the LH, find a Lower Low
                        for(int l = j ; l < rates_total - LookbackBars; l++)
                          {
                           if(IsSwingLow(low, l, LookbackBars) && time[l] > LH_time)
                             {
                              LL = low[l];
                              LL_time = time[l];
                              LL_letter = StringFormat("Lower Low%d", l);  // Label for Lower Low

                              // Calculate 50% retracement level from LH to LL
                              lvl_50 = LL + ((LH - LL)/2);

                              // Prepare object names
                              pre_dis_box = StringFormat("Gan Box B%d", i);
                              lvl_50_line = StringFormat("Level 50 Line B%d", i);

                              // Search for a bearish entry condition
                              for(int m = l; m < rates_total-1; m++)
                                {
                                 // Confirm bearish candle breaking below the LL
                                 if(close[m] < open[m] && close[m] < LL && time[m] >= time[l+LookbackBars])
                                   {
                                    // Count bars for pattern distance validation
                                    n_bars = Bars(_Symbol,PERIOD_CURRENT,LH_time, time[l+LookbackBars]);  // From LH to box end
                                    n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[l+LookbackBars], time[m]);  // From box end to break candle

                                    // Confirm valid bearish structure and proximity of break candle
                                    if(H > L && LH > L && LH < H && LL < L && open[l+LookbackBars] >= LL && n_bars_2 < n_bars)
                                      {
                                       // Draw the Premium/Discount box
                                       ObjectCreate(chart_id,pre_dis_box, OBJ_RECTANGLE,0,LH_time,LH, time[l+LookbackBars],LL);
                                       ObjectCreate(chart_id,lvl_50_line, OBJ_TREND,0,LH_time,lvl_50, time[l+LookbackBars],lvl_50);

                                       ObjectSetInteger(chart_id,pre_dis_box,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_50_line,OBJPROP_WIDTH,2);

                                       // Label the structure points
                                       ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H);
                                       ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H");
                                       ObjectSetInteger(chart_id,H_letter,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L);
                                       ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L");
                                       ObjectSetInteger(chart_id,L_letter,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, LH_letter, OBJ_TEXT, 0, LH_time, LH);
                                       ObjectSetString(chart_id, LH_letter, OBJPROP_TEXT, "LH");
                                       ObjectSetInteger(chart_id,LH_letter,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, LL_letter, OBJ_TEXT, 0, LL_time, LL);
                                       ObjectSetString(chart_id, LL_letter, OBJPROP_TEXT, "LL");
                                       ObjectSetInteger(chart_id,LL_letter,OBJPROP_FONTSIZE,15);

                                       ObjectSetInteger(chart_id,H_letter,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,L_letter,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,LL_letter,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,LH_letter,OBJPROP_WIDTH,2);

                                       // Calculate Take Profits based on 1:1 and 1:2 RR
                                       TP1 = close[m] - (lvl_50 - close[m]);
                                       TP2 = TP1 - (lvl_50 - close[m]);

                                       // Generate entry, SL and TP object names
                                       entry_line = StringFormat("Entry B%d", m);
                                       lvl_sl_line = StringFormat("SL B%d", m);
                                       lvl_tp_line =  StringFormat("TP B%d", m);
                                       lvl_tp2_line =  StringFormat("TP 2 B%d", m);

                                       // Draw entry, SL, TP1, TP2 levels
                                       ObjectCreate(chart_id,entry_line,OBJ_TREND,0,LH_time,close[m],time[m],close[m]);
                                       ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,0,LH_time,lvl_50, time[m],lvl_50);
                                       ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,0,LH_time,TP1, time[m],TP1);
                                       ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,0,LH_time,TP2, time[m],TP2);

                                       ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2);
                                       ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2);

                                       // Generate text labels
                                       entry_txt = StringFormat("Entry Text B%d", m);
                                       lvl_sl_txt = StringFormat("SL Text B%d", m);
                                       lvl_tp_txt = StringFormat("TP Text B%d", m);
                                       lvl_tp2_txt = StringFormat("TP 2 Text B%d", m);

                                       ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]);
                                       ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "SELL");
                                       ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50);
                                       ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL");
                                       ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1);
                                       ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1");
                                       ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15);

                                       ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2);
                                       ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2");
                                       ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15);

                                       // Draw sell arrow
                                       sell_object = StringFormat("Sell Object%d", m);
                                       ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[m],close[m]);
                                      }

                                    break;  // Exit loop after valid setup
                                   }
                                }

                              break;  // Exit LL search
                             }
                          }

                        break;  // Exit LH search
                       }
                    }

                  break;  // Exit L search
                 }
              }
           }
        }
     }
  }

Salida:

Figura 10. Señales de venta

Explicación:

El código comienza utilizando if(show_bearish == true) para ver si el usuario ha activado la lógica de tendencia bajista. Para encontrar una estructura bajista legítima del mercado, el indicador recorre las barras históricas si está habilitado y hay suficientes barras disponibles (rates_total >= bars_check). El primer paso del proceso es determinar un máximo oscilante (H). Después de identificar un máximo oscilante, el código busca un mínimo oscilante (L) que siga al máximo. Si se descubre, sigue buscando un mínimo más bajo (LL) que valide la estructura bajista, seguido de un máximo más bajo (LH), que es un máximo oscilante por debajo del primer máximo H. Las etiquetas como «H», «L», «LH» y «LL» se crean en el gráfico utilizando estos valores y las marcas de tiempo asociadas.

A continuación, se traza la zona premium/descuento desde el LH hasta el LL, y se determina el nivel de retroceso del 50 % entre el LH y el LL (lvl_50 = LL + ((LH - LL)/2). El indicador busca una vela bajista (close < open) que cierre por debajo del LL antes de colocar cualquier objeto relacionado con la operación (entrada, SL, TP1, TP2). Al utilizar dos variables, n_bars, que cuenta las barras desde el máximo inferior (LH) hasta el final del cuadro premium/descuento, y n_bars_2, que cuenta desde el final del cuadro hasta la vela bajista que rompe el mínimo inferior (LL), el código también se asegura de que la ruptura de la estructura se produzca dentro de un número razonable de barras. El código solo dibuja la línea de entrada al cierre de la vela, establece el Stop Loss (SL) en el nivel del 50 % y posiciona TP1 y TP2 en niveles de riesgo-recompensa de 1:1 y 1:2, respectivamente, cuando se cumplen todos los requisitos, incluyendo una estructura adecuada, una ruptura válida y una distancia apropiada.

Además, añade una flecha de venta y la palabra «SELL» a la vela bajista. En el caso de una tendencia bajista, la lógica es básicamente la misma que para un patrón de tendencia positiva, pero se invierte. Dado que la tendencia bajista es solo lo contrario de la tendencia alcista, que ya se ha detallado exhaustivamente, la explicación es breve. La estructura y el razonamiento son los mismos, excepto que se invierten para mostrar una tendencia a la baja en lugar de una al alza.


Conclusión

En este artículo, hemos creado un indicador personalizado MQL5 que identifica la estructura del mercado mediante la detección de puntos clave como Mínimo (L), Mínimo inferior (LL), Mínimo superior (HL), Máximo (H) y Máximo superior (HH). Utilizando estos puntos, el indicador determina las tendencias alcistas o bajistas y dibuja automáticamente los puntos de entrada, el stop loss en el nivel 50 y los niveles de take profit (TP1 y TP2) basándose en un patrón estructurado. También marca las zonas de prima y descuento para resaltar visualmente dónde es probable que reaccione el precio. Todos los objetos del gráfico se dibujan solo cuando se cumplen condiciones específicas, lo que garantiza que las señales sigan siendo claras y fiables. 


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17689

Archivos adjuntos |
Determinamos la sobrecompra y la sobreventa usando la teoría del caos Determinamos la sobrecompra y la sobreventa usando la teoría del caos
Hoy determinaremos la sobrecompra y la sobreventa del mercado mediante la teoría del caos; usando la integración de los principios de la teoría del caos, la geometría fractal y las redes neuronales, pronosticaremos los mercados financieros. El presente artículo demostrará la aplicación del exponente de Lyapunov como medida de la aleatoriedad del mercado y la adaptación dinámica de las señales comerciales. La metodología incluye un algoritmo de generación de ruido fractal, activación por tangente hiperbólica y optimización con impulso.
Redes neuronales en el trading: Jerarquía de habilidades para el comportamiento adaptativo de agentes (Final) Redes neuronales en el trading: Jerarquía de habilidades para el comportamiento adaptativo de agentes (Final)
El artículo analiza la aplicación práctica del framework HiSSD en tareas de trading algorítmico. Muestra cómo la jerarquía de habilidades y la arquitectura adaptativa pueden usarse para construir estrategias de negociación sostenibles.
Análisis angular de los movimientos de precios: un modelo híbrido para predecir los mercados financieros Análisis angular de los movimientos de precios: un modelo híbrido para predecir los mercados financieros
¿Qué es el análisis angular de los mercados financieros? ¿Cómo usar los ángulos de precios y el aprendizaje automático para predecir con una exactitud de 67? ¿Cómo combinar un modelo de regresión y clasificación con características angulares y obtener un algoritmo que funcione? ¿Qué tiene que ver Gann con esto? ¿Por qué los ángulos de movimiento de los precios son una buena señal para el aprendizaje automático?
Arbitraje estadístico mediante reversión a la media en el trading de pares: Cómo superar al mercado con matemáticas Arbitraje estadístico mediante reversión a la media en el trading de pares: Cómo superar al mercado con matemáticas
Este artículo describe los fundamentos del arbitraje estadístico a nivel de cartera. Su objetivo es facilitar la comprensión de los principios del arbitraje estadístico a lectores sin conocimientos matemáticos profundos y proponer un marco conceptual de partida. El artículo incluye un Asesor Experto en funcionamiento, algunas notas sobre su prueba retrospectiva de un año y las respectivas configuraciones de prueba retrospectiva (archivo .ini) para la reproducción del experimento.