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

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

MetaTrader 5Sistemas comerciales |
18 6
Israel Pelumi Abioye
Israel Pelumi Abioye

Introducción

¡Bienvenido nuevamente a nuestra serie MQL5! En los artículos anteriores de esta serie, exploramos cómo crear indicadores personalizados en MQL5 utilizando buffers y gráficos. Los buffers nos permiten almacenar valores de indicadores, mientras que los gráficos ayudan a visualizarlos en el gráfico. Estos métodos son eficaces para muchos indicadores, pero tienen limitaciones cuando se trata de crear representaciones visuales complejas.

En este artículo, adoptaremos un nuevo enfoque al crear indicadores utilizando objetos gráficos de Meta Trader 5. Los objetos gráficos ofrecen flexibilidad adicional, ya que nos permiten crear etiquetas, formas y líneas de tendencia directamente en el gráfico sin necesidad de buffers de indicación. Esta técnica funciona bien para desarrollar indicadores que necesitan componentes gráficos únicos, mostrar patrones e identificar niveles de precios importantes.

Crearemos un indicador que se asemeje a los Patrones Armónicos para implementar esto. La lógica que utilizaremos puede modificarse para identificar y representar diferentes patrones armónicos, aunque no nos centraremos en ninguno en particular (como Gartley, Bat o Butterfly). En lugar de crear un detector de patrones armónicos completamente efectivo, el objetivo principal es aprender a usar objetos gráficos en MQL5 para desarrollar indicadores. En la Parte 9 de esta serie, vimos cómo crear y trabajar con objetos como líneas de tendencia, rectángulos y etiquetas en MQL5, donde abordamos por primera vez el uso de objetos gráficos. Basándonos en ese conocimiento, este artículo lo aplicará al desarrollo de indicadores. Al finalizar, tendrá una comprensión sólida de cómo desarrollar indicaciones visuales únicas trabajando con elementos del gráfico de forma dinámica.

En este artículo aprenderás:

  • Cómo crear un indicador personalizado utilizando objetos de gráficos de MetaTrader 5 en lugar de depender de buffers y gráficos.
  • Comprender la estructura de los patrones armónicos y cómo se identifican en la acción del precio.
  • Cómo detectar puntos de oscilación clave en el mercado para formar posibles patrones armónicos.
  • Uso de niveles de retroceso de Fibonacci para validar la formación de patrones.
  • Cómo dibujar formas geométricas mediante programación (como triángulos y líneas) para visualizar patrones en el gráfico.
  • Cómo filtrar patrones no válidos para mejorar la precisión en las señales comerciales.

1. Indicador de patrón armónico

1.1. Comprensión del indicador de patrón armónico

Las estructuras de precios conocidas como patrones armónicos utilizan proporciones de Fibonacci particulares para señalar posibles zonas de reversión del mercado. Los patrones Gartley, Bat y Butterfly, entre otros, dependen de las fluctuaciones de precios que crean formas geométricas que indican oportunidades de negociación con alta probabilidad.

Los patrones armónicos requieren la identificación de puntos de precio críticos y el uso de proporciones de Fibonacci para validar su estructura, mientras que los indicadores estándar crean señales utilizando buffers y gráficos. Como resultado, son más difíciles de identificar y mostrar que los indicadores más convencionales como el RSI o las medias móviles.

La lógica que utilizamos puede modificarse para identificar y representar diferentes patrones armónicos, aunque no nos centraremos en ninguno en particular (como Gartley, Bat o Butterfly). En lugar de construir un detector de patrones armónicos completamente efectivo, el objetivo principal es aprender a utilizar objetos gráficos en MQL5 para desarrollar indicadores.

Para ello, marcaremos puntos de oscilación importantes (máximos y mínimos) en el gráfico, utilizaremos objetos de texto (XABCD) para indicar niveles importantes, ilustraremos la estructura del patrón con diferentes objetos y aplicaremos niveles de Fibonacci para mejorar la precisión y la validación del patrón.

1.2. Configuración del proyecto

Como digo a menudo, primero debes visualizar cómo aparecerá un indicador antes de poder diseñarlo. Saber exactamente cómo debe configurarse el indicador facilita el proceso de desarrollo y garantiza que el producto final cumpla con nuestros requisitos.

En este proyecto, utilizaremos objetos gráficos de MetaTrader 5 para construir patrones armónicos alcistas y bajistas. El indicador dibujará varios elementos gráficos para formar la estructura del patrón, localizará puntos de oscilación importantes, los marcará con objetos de texto (XABCD) y agregará niveles de Fibonacci para mejorar la presentación del patrón.

Además de crear un indicador que se asemeja mucho a los patrones armónicos, este método nos brindará experiencia práctica con objetos de gráficos MQL5, lo que facilitará la creación de futuros indicadores personalizados. Además de discutir cómo crear estos patrones con objetos gráficos, también repasaremos los posibles errores que podrían ocurrir. Pueden surgir problemas, como objetos desalineados, niveles de Fibonacci mal ubicados y detección incorrecta del punto de oscilación. Para garantizar una visualización precisa del patrón y un funcionamiento perfecto del indicador, analizaremos estos problemas y ofreceremos soluciones.

1.2.1. Patrón alcista

Utilizaremos una técnica metódica para identificar los puntos de oscilación importantes que componen el patrón para desarrollar el Indicador de Patrón Armónico Alcista. El proceso implica determinar puntos de precios específicos y asegurarse de que sigan las pautas de patrones armónicos.

Identificar un mínimo oscilante (X) como punto de inicio del patrón será nuestro primer paso. A continuación, identificaremos un máximo (A) y un mínimo (B). B necesita retroceder entre el 61,8% y el 78,6% del tramo XA para que el patrón siga siendo válido. Luego buscaremos un máximo oscilante (C) y validaremos el mínimo oscilante (D). La estructura completa del patrón alcista se forma cuando el último punto, D, es inferior a X.

Cuando se cumplan estas condiciones, utilizaremos objetos gráficos (como líneas de tendencia que unen los puntos de oscilación, etiquetas para los puntos XABCD y niveles de retroceso de Fibonacci para confirmar la estructura) para representar gráficamente el patrón en el gráfico. Esto ofrecerá un enfoque metódico y transparente para identificar posibles zonas de reversión alcista. Para facilitarle el reconocimiento de zonas comerciales importantes dentro del patrón, también utilizaremos objetos gráficos para indicar posibles posiciones de entrada, niveles de stop-loss (SL) y take-profit (TP). Esto ayudará con la toma de decisiones comerciales al ofrecer un enfoque metódico y transparente para identificar posibles zonas de reversión alcista.

Figura 1.  Patrón alcista

Pseudocódigo:

// Paso 1: Identificar los puntos de giro

  • Detectar mínimo oscilante (X)
  • Identifica el máximo oscilante (A) después de X
  • Detectar mínimo oscilante (B) después de A

SI el retroceso B NO está entre el 61,8 % y el 78,6 % de XA

  • Descartar patrón y reiniciar detección  

Si el retroceso B se encuentra entre el 61,8 % y el 78,6 % de XA

  • Identifica el máximo oscilante (C) después de B
  • Detectar mínimo oscilante (D) después de C

SI D NO es menor que X 

  • A CONTINUACIÓN, descarte el patrón y reinicie la detección

// Paso 2: Dibujar objetos del gráfico

  • Dibuja objetos conectando X → A → B → C → D 
  •  Etiqueta los puntos con objetos de texto: X, A, B, C, D
  • Dibuja el retroceso de Fibonacci desde X hasta A para su validación 

// Paso 3: Definir los niveles comerciales 

  • Cree objetos gráficos para marcar la entrada, el SL y el TP

1.2.2. Patrón bajista

Similar en estructura al patrón alcista, pero a la inversa, se encuentra el indicador de patrón armónico bajista. Para asegurarnos de que siguen las pautas de los patrones armónicos, señalaremos los puntos de oscilación importantes. El patrón se identificará identificando primero un máximo oscilante (X), al que le seguirá un mínimo oscilante (A). A continuación, se determinará el máximo oscilante (B), que debe retroceder entre el 61,8 % y el 78,6 % del tramo XA. El máximo final (D) se confirmará cuando hayamos detectado un mínimo (C). D debe estar por encima de X, creando una posible zona de reversión bajista, para validar el patrón.

Tras confirmar la estructura, utilizaremos objetos gráficos, como líneas de tendencia, etiquetas XABCD y niveles de retroceso de Fibonacci, para representar gráficamente el patrón. Para ayudarle a localizar zonas de negociación importantes dentro del patrón, indicaremos además los niveles de entrada, stop-loss (SL) y take-profit (TP).

Figura 2. Patrón bajista

Pseudocódigo:

// Paso 1: Identificar los puntos de giro

  • Detectar máximo oscilante (X)
  • Detectar mínimo (A) después de X
  • Detectar el máximo oscilante (B) después de A

SI el retroceso B NO está entre el 61,8 % y el 78,6 % de XA

  • Descartar patrón y reiniciar detección  

Si el retroceso B se encuentra entre el 61,8 % y el 78,6 % de XA

  • Detectar mínimo oscilante (C) después de B
  • Detectar el máximo oscilante (D) después de C

SI D NO es mayor que X

  • Descartar patrón y reiniciar detección

// Paso 2: Dibujar objetos del gráfico

  • Dibuja objetos conectando X → A → B → C → D 
  • Etiqueta los puntos con objetos de texto: X, A, B, C, D 
  • Dibuja el retroceso de Fibonacci desde X hasta A para su validación 

// Paso 3: Definir los niveles comerciales 

  • Cree objetos gráficos para marcar la entrada, el SL y el TP


2. Creación de patrones armónicos alcistas

Comenzaremos a integrar el indicador de patrón armónico en MQL5 en esta sección. Especificaremos las funciones necesarias para identificar puntos de oscilación, confirmar niveles de retroceso y visualizar el patrón en el gráfico utilizando objetos gráficos. Además, personalizaremos la apariencia de los objetos dibujados para mejorar la claridad y la usabilidad.

2.1. Identificación de máximos y mínimos oscilantes

Es necesario contar con una técnica fiable para localizar los máximos y mínimos oscilantes en el gráfico antes de poder detectar un patrón armónico. La estructura XABCD se traza utilizando los puntos de oscilación como base. Determinamos si el máximo de una vela es el más alto entre un rango específico de velas cercanas para identificar un máximo oscilante. De manera similar, se identifica un mínimo oscilante cuando el mínimo de una vela es el más bajo en un rango determinado. El tamaño de este rango determina la sensibilidad de nuestra detección: los valores más grandes capturan oscilaciones importantes, mientras que los valores más pequeños detectan fluctuaciones menores. En el siguiente paso, desarrollaremos un algoritmo para escanear datos históricos de precios, localizar puntos de inflexión clave y garantizar que se correspondan con los principios de formación de patrones.

2.1.1. Funciones de máximos y mínimos oscilantes

Ejemplo:

#property indicator_chart_window

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

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
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 HIGH                                          |
//+------------------------------------------------------------------+
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; 
  }

Explicación:

MetaTrader 5 recibe la información de la directiva #property indicator_chart_window de que el indicador personalizado debe mostrarse en el gráfico de precios principal en lugar de en una subventana diferente. Esto resulta útil a la hora de crear indicaciones basadas en patrones, como los patrones armónicos, o indicadores que se superponen a la actividad de los precios, como las líneas de tendencia, los niveles de soporte y resistencia, etc. En ausencia de esta característica, el indicador podría, por defecto, trazarse en una ventana de indicador diferente, al igual que los osciladores como MACD o RSI.

El objetivo de la función IsSwingLow es identificar los mínimos oscilantes del gráfico de precios. Cuando el mínimo de una vela cae por debajo de los mínimos de las velas circundantes dentro de un determinado rango, se conoce como mínimo oscilante. La función acepta un valor retrospectivo que establece cuántas velas antes y después deben tenerse en cuenta, una matriz de precios bajos y el índice de la vela que se está evaluando. Determina si el mínimo actual es el más bajo iterando a través de las velas cercanas. Devuelve falso si descubre un valor inferior en el rango circundante, lo que demuestra que la vela actual no es un mínimo oscilante. Si no es así, devuelve verdadero, lo que indica que hay un mínimo oscilante legítimo.

De manera similar, los máximos oscilantes (puntos en los que el máximo de una vela supera los máximos de las velas cercanas) se encuentran utilizando la función IsSwingHigh. La función utiliza la misma lógica que IsSwingLow, excepto que se asegura de que el máximo en el índice especificado sea el valor más alto en el rango de retrospectiva, en lugar de buscar el valor más bajo. La función devuelve falso si alguna de las velas circundantes tiene un valor más alto. Si no es así, confirma un máximo oscilante y devuelve verdadero.

La detección de momentos significativos de cambio de tendencia en los precios, que sirven de base para definir los patrones armónicos, requiere ambas capacidades. Tras identificar los máximos y mínimos oscilantes, se puede trazar la estructura XABCD del patrón uniendo dichos puntos con líneas de tendencia y utilizando los retrocesos de Fibonacci para su confirmación. Con este método, se garantiza que el indicador se ajuste dinámicamente a la información de precios actualizada y actualice los patrones identificados de forma adecuada.

Analogía:

La función IsSwingLow opera buscando el punto más bajo de un mínimo. Imagínate que estás parado en un sendero para caminatas y tratando de localizar el desnivel más profundo del camino. Durante el tiempo que pasa mirando hacia atrás, das unos cuantos pasos hacia adelante y unos cuantos pasos hacia atrás. El punto actual se verifica como el mínimo más bajo (mínimo oscilante) si es inferior a todos los demás puntos de ese rango. Tu ubicación actual es solo otra sección de la pendiente y no el punto más bajo real si hay puntos vecinos más bajos. Las zonas de posible reversión en las que los precios podrían comenzar a subir se identifican mediante el uso de esta función.

Encontrar el punto más alto de una cordillera es similar a utilizar la función IsSwingHigh. Se considera un máximo si te paras en un punto y das unos pasos hacia adelante y hacia atrás para asegurarte de que tu posición actual sea la más alta dentro de ese rango. Hasta qué punto se debe verificar antes de anunciar un pico o una caída depende del tiempo de retrospección; si es demasiado pequeño, pequeñas variaciones podrían confundirse con puntos importantes, y si es demasiado grande, se podrían perder tendencias más pequeñas pero importantes. Este equilibrio garantiza que los puntos de oscilación identificados sean significativos y no meras fluctuaciones arbitrarias en el precio.

Figura 3. Oscilación alta y baja

2.1.2. Identificación del mínimo oscilante (X)

El siguiente paso es incorporar las funciones IsSwingLow e IsSwingHigh en la función OnCalculate una vez que se hayan creado. Aquí, los datos de precios en tiempo real se someterán a la lógica de detección de puntos de oscilación. Encontrar el mínimo inicial (X), que es donde comienza el patrón armónico, es el primer reto. Utilizando la función que hemos creado, podemos examinar iterativamente cada vela del historial de precios de OnCalculate para ver si cumple los criterios de un mínimo oscilante.

Ejemplo:

#property indicator_chart_window

// 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

// CHART ID
long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.)

//X
double X; // Price of the swing low (X).
datetime X_time; // Time of the swing low (X).
string X_line; // Unique name for the trend line object.
string X_letter; // Unique name for the text label object.

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

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

   if(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X
            ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".

            ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
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 HIGH                                          |
//+------------------------------------------------------------------+
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; 
  }
//+------------------------------------------------------------------+

Resultado:

Figura 4. Mínimo oscilante (X)

Explicación:

Comenzamos por establecer algunos datos de entrada cruciales, ya que determinar el mínimo oscilante (X) es el primer paso para encontrar un patrón armónico alcista. El número de barras que deben examinarse antes y después de un punto específico para asegurarse de que se trata de un punto de oscilación legítimo viene determinado por la variable LookbackBars. Esto ayuda a eliminar pequeñas fluctuaciones de precios que no dan lugar a mínimos significativos. Para garantizar que el código solo procese un número manejable de barras históricas y así mantener la eficiencia, el argumento bars_check especifica cuántas barras deben analizarse.

Además, utilizamos ChartID() para obtener el chart_id, lo que nos permite crear y gestionar objetos de gráfico, como líneas de tendencia y etiquetas de texto. Declaramos las variables «X» para almacenar el precio del mínimo oscilante detectado (X), «X_time» para registrar la hora en que se produjo y «X_line» y «X_letter» para dar nombres distintos a los objetos que se utilizarán para identificar este punto en el gráfico. La etiqueta de texto que indica el mínimo oscilante se creará utilizando la variable X_letter, y se creará una línea de tendencia para mejorar la visualización utilizando la variable X_line.

A continuación, utilizamos estas configuraciones preliminares para determinar el mínimo oscilante (X) en la función OnCalculate. Solo comenzamos a comprobar cuando hay suficientes datos de precios disponibles, gracias a la condición if(rates_total >= bars_check). Para evitar comprobar datos de precios incompletos, el bucle itera a través de barras históricas, comenzando en rates_total - bars_check y terminando en rates_total - LookbackBars. La función IsSwingLow del bucle detecta si un punto determinado es un mínimo oscilante. Cuando se descubre un mínimo legítimo, registramos su hora y su precio, creamos nombres de objetos originales y, a continuación, producimos componentes visuales para el gráfico.

Se crea un objeto de texto utilizando ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X) para indicar el mínimo oscilante con la letra «X». Para ilustrar mejor el mínimo identificado, utilizamos ObjectCreate(chart_id, X_line, OBJ_TREND, 0, X_time, X, time[i+LookbackBars], X) para construir una línea de tendencia en el nivel X. Esto garantiza que el primer punto importante de nuestra estructura de patrón armónico se identifique con precisión y se muestre en el gráfico, sentando las bases para determinar los puntos posteriores del patrón.

2.1.3. Identificación del máximo oscilante (A)

Encontrar el máximo oscilante (A) es la siguiente etapa tras identificar con éxito el mínimo oscilante (X). Este punto es esencial para la formación del patrón armónico, ya que es el primer movimiento alcista notable tras X. Del mismo modo que descubrimos X, utilizaremos la función IsSwingHigh para encontrar A, pero esta vez buscaremos un pico en lugar de una caída.

Buscaremos un punto en los datos de precios después de X donde el máximo supere los máximos de las barras circundantes dentro del rango LookbackBars designado. Después de identificar un máximo oscilante legítimo, registraremos su precio y momento, le daremos un nombre distintivo y utilizaremos objetos gráficos para describirlo adecuadamente.

Ejemplo:

#property indicator_chart_window

// 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

// CHART ID
long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.)

//X
double X; // Price of the swing low (X).
datetime X_time; // Time of the swing low (X).
string X_line; // Unique name for the trend line object.
string X_letter; // Unique name for the text label object.

//A
double A; // Price of the swing high (A).
datetime A_time; // Time of the swing high (A).
string A_line; // Unique name for the trend line object.
string A_letter; // Unique name for the text label object.

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

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

   if(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X
            ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
            ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X

            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingHigh(high, j, LookbackBars)  && time[j] > X_time)
                 {

                  A = high[j]; // Price of the swing high (A).
                  A_time = time[j]; // Time of the swing high (A).
                  A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object.
                  A_letter = StringFormat("A%d", j); // Unique name for the text label object.

                  ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A
                  ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                  ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                  ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A
                  ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

                  break;
                 }
              }
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
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 HIGH                                          |
//+------------------------------------------------------------------+
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; 
  }
//+------------------------------------------------------------------+

Resultado:

Figura 5. Máximo oscilante (A)

Explicación:

Primero definimos las variables necesarias para determinar el máximo oscilante (A). A_time registra el momento exacto en que se produjo el máximo oscilante, mientras que A almacena el valor del precio del máximo oscilante. Utilizamos A_line como nombre para el objeto de línea de tendencia y A_letter como etiqueta de texto para garantizar que cada punto A detectado tenga una representación visual distinta. Estas variables ayudan a crear objetos gráficos que muestran de forma destacada el punto A, lo que facilita la visualización de la estructura del patrón armónico.

Una vez identificado X, el código recorre los datos de precios, comenzando por la ubicación de X, para encontrar A. La función IsSwingHigh, que determina si una barra es un máximo local dentro del rango LookbackBars designado, se utiliza para comprobar cada barra. La condición time[j] > X_time se utiliza para garantizar que el máximo A se produzca después de X. 

Esto garantiza que se mantenga la secuencia correcta en el patrón armónico seleccionando A solo si su marca de tiempo es mayor que X_time. Se registran el precio y el momento de un máximo legítimo, y se asignan identidades distintivas a los objetos de línea de tendencia y etiqueta de texto si se descubre alguno. Se traza una línea de tendencia verde para resaltar el máximo identificable, y se coloca allí una etiqueta de texto con la letra «A». Se traza una línea de tendencia verde para resaltar el máximo identificable, y se coloca allí una etiqueta de texto con la letra «A».As soon as the first legitimate swing high (A) has been identified, the loop is broken using the break; expression. El bucle seguiría buscando más máximos sin interrupción, tal vez sobrescribiendo A con un máximo posterior. Dado que solo queremos la primera instancia de A después de X, la interrupción; garantiza que el bucle termine de iterar tan pronto como se detecte un máximo, protegiendo la primera A válida y evitando cálculos inútiles. 

2.1.4. Identificación del mínimo oscilante (B)

Encontrar el siguiente mínimo oscilante, B, es el siguiente paso después de determinar el máximo oscilante, A. La búsqueda de B comienza tan pronto como se identifica A, ya que B debe venir después de A. Esto se logra iterando a través de los datos de precios, comenzando en la posición A, y utilizando la función IsSwingLow para comparar una barra con las barras cercanas en el rango LookbackBars para determinar si se trata de un mínimo oscilante. Además, se prevé que B caiga por debajo de un nivel de retroceso de Fibonacci concreto de XA en la identificación del patrón armónico. Sin embargo, no discutiremos la validación de Fibonacci en este momento; solo nos interesa identificar B.

El precio y el momento de un mínimo oscilante se registran en B y B_time, respectivamente, tan pronto como se identifica. Proporcionamos nombres distintivos para la línea de tendencia (B_line) y la etiqueta de texto (B_letter) para indicar esta ubicación en el gráfico. Se crea una línea de tendencia para conectar visualmente y resaltar el objeto de texto con la etiqueta «B» que se crea en el punto más bajo del swing. Esta etapa garantiza que los tres puntos principales (X, A y B) que ahora sirven como marco para nuestra estructura de patrón armónico estén en su lugar.

Ejemplo:

#property indicator_chart_window

// 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

// CHART ID
long chart_id = ChartID(); // Get the ID of the current chart to manage objects (lines, text, etc.)

//X
double X; // Price of the swing low (X).
datetime X_time; // Time of the swing low (X).
string X_line; // Unique name for the trend line object.
string X_letter; // Unique name for the text label object.

//A
double A; // Price of the swing high (A).
datetime A_time; // Time of the swing high (A).
string A_line; // Unique name for the trend line object.
string A_letter; // Unique name for the text label object.

//B
double B; // Price of the swing low (B).
datetime B_time; // Time of the swing low (B).
string B_line; // Unique name for the trend line object.
string B_letter; // Unique name for the text label object.

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

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// This function is called when the indicator is removed or the chart is closed.
// Delete all objects (lines, text, etc.) from the chart to clean up.
   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(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X
            ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
            ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X


            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time)
                 {

                  A = high[j]; // Price of the swing high (A).
                  A_time = time[j]; // Time of the swing high (A).
                  A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object.
                  A_letter = StringFormat("A%d", j); // Unique name for the text label object.

                  ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A
                  ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                  ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                  ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A
                  ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green


                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time)
                       {

                        // If a swing low is found, store its price, time, and create a name for the object.
                        B = low[k]; // Price of the swing low (B).
                        B_time = time[k]; // Time of the swing low (B).
                        B_line = StringFormat("BLow%d", k); // Unique name for the trend line object.
                        B_letter = StringFormat("B%d", k); // Unique name for the text label object.

                        ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B
                        ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
                        ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                        ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B
                        ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to green


                        break;

                       }
                    }

                  break;
                 }
              }
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
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 HIGH                                          |
//+------------------------------------------------------------------+
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;  
  }
//+------------------------------------------------------------------+

Resultado:

Figura 6: Mínimo oscilante (B)

Explicación:

El precio del mínimo oscilante que sigue a A se representa mediante la variable B. Para identificarlo, se utiliza un bucle que comienza en el punto A y utiliza la función IsSwingLow para encontrar el siguiente mínimo oscilante. El precio, la hora y los nombres distintivos de la línea de tendencia y la etiqueta de texto se guardan después de identificarlos. A continuación, para indicar B en el gráfico, el ordenador genera una línea de tendencia en azul medianoche y una etiqueta de texto («B») en verde. Para mantener la secuencia adecuada de puntos de giro para la generación de patrones, una instrucción break garantiza que solo se elija la primera B válida. Dado que X y B se reconocen como mínimos oscilantes a lo largo de la iteración del bucle, en ocasiones pueden solaparse en la implementación actual. Dado que el programa evalúa cada punto de giro por separado, este comportamiento es previsible. Sin embargo, podría dar lugar a anotaciones redundantes en el gráfico si X y B son iguales.

Puede incluir una condición para asegurarse de que B sea un mínimo independiente que siga a A, pero B no sea igual a X si desea evitar que X y B se superpongan. Incluiré una opción en el código que te permita elegir si X y B pueden superponerse. El indicador funcionará como lo hace ahora, donde X y B pueden ser el mismo mínimo oscilante si se permite el solapamiento. Sin embargo, el indicador se asegurará de que B sea un mínimo oscilante diferente de X si se prohíbe la superposición.

Dado que B debe ser un mínimo independiente y no puede ser igual que X, evitar la superposición dará lugar a que se descubran menos patrones armónicos. Dependiendo de las preferencias del usuario en cuanto al reconocimiento de patrones, esta opción ofrece flexibilidad.

Ejemplo:

input bool overlap = false; //Allow Overlaping
for(int k = j; k < rates_total - LookbackBars; k++)
  {
   if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time)
     {

      // If a swing low is found, store its price, time, and create a name for the object.
      B = low[k]; // Price of the swing low (B).
      B_time = time[k]; // Time of the swing low (B).
      B_line = StringFormat("BLow%d", k); // Unique name for the trend line object.
      B_letter = StringFormat("B%d", k); // Unique name for the text label object.

      ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B
      ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
      ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
      ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B
      ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to green

      if(overlap == false)
        {
         i = k;
        }
      if(overlap == true)
        {
         i = i;
        }

      break;

     }
  }
Resultado:


Figura 7. X y B se solapan

Explicación:

Que X y B puedan tener la misma oscilación baja depende del parámetro de entrada de superposición. La línea i = k; garantiza que la iteración salta hacia adelante una vez que se localiza B, evitando que B sea igual a X cuando la superposición se establece en falso. Esto limita la cantidad de patrones armónicos que se pueden reconocer, al tiempo que minimiza la redundancia al mantener una separación clara entre los puntos de oscilación.

Sin embargo, la línea i = i; indica que el bucle continúa normalmente sin necesidad de omitir nada cuando la superposición es verdadera. Esto permite descubrir más patrones armónicos al permitir que X y B se superpongan cuando se producen de forma natural al mismo nivel de precios. Sin embargo, también podría provocar cierta repetición en las marcas del patrón. Al evitar con éxito que X y B se superpongan, el código se ha asegurado de que sean puntos de oscilación separados. Como ilustra la imagen, B ahora se manifiesta independientemente de X, conservando una estructura de patrón más distintiva.

2.1.5. Identificación del máximo oscilante (C)

La búsqueda del máximo oscilante (C) se realiza después de determinar el mínimo oscilante (X), el máximo oscilante (A) y el siguiente mínimo oscilante (B). Este punto es esencial para construir la estructura de un patrón armónico, ya que determina la forma general del patrón y la posible zona de reversión.

Tanto la línea de tendencia (C_line) como la etiqueta de texto (C_letter) reciben nombres distintos para indicar visualmente este punto. Se crea una línea de tendencia para conectar visualmente y resaltar un elemento de texto con la etiqueta «C» que se encuentra en el punto más alto del gráfico. Esta etapa garantiza que los cuatro puntos principales (X, A, B y C) que conforman la estructura del patrón armónico estén ahora en su lugar.

Ejemplo:

//C
double C; // Price of the swing high (C).
datetime C_time; // Time of the swing high (C).
string C_line; // Unique name for the trend line object.
string C_letter; // Unique name for the text label object.
//+------------------------------------------------------------------+
//| 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(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X
            ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
            ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X

            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time)
                 {

                  A = high[j]; // Price of the swing high (A).
                  A_time = time[j]; // Time of the swing high (A).
                  A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object.
                  A_letter = StringFormat("A%d", j); // Unique name for the text label object.

                  ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A
                  ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                  ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                  ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A
                  ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time)
                       {

                        // If a swing low is found, store its price, time, and create a name for the object.
                        B = low[k]; // Price of the swing low (B).
                        B_time = time[k]; // Time of the swing low (B).
                        B_line = StringFormat("BLow%d", k); // Unique name for the trend line object.
                        B_letter = StringFormat("B%d", k); // Unique name for the text label object.

                        ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B
                        ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
                        ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                        ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B
                        ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

                        for(int l = j ; l < rates_total - LookbackBars; l++)
                          {
                           if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time)
                             {
                              C = high[l]; // Price of the swing high (C).
                              C_time = time[l]; // Time of the swing high (C).
                              C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object.
                              C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                              ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, C_time, C); // Create text object for C
                              ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
                              ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                              ObjectCreate(chart_id,C_line,OBJ_TREND,0,C_time,C,time[l+LookbackBars],C); // Create line to mark C
                              ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                              if(overlap == false)
                                {
                                 i = l;
                                }
                              if(overlap == true)
                                {
                                 i = i;
                                }

                              break;

                             }
                          }

                        break;

                       }
                    }

                  break;
                 }
              }
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }
Resultado:


Figura 8. Máximo oscilante (C)

Explicación:

Después de determinar el mínimo oscilante (B), esta parte del código supervisa la determinación del máximo oscilante (C), que es el siguiente paso crucial. Iterando sobre los datos de precios, el bucle for(int l = j; l < rates_total - LookbackBars; l++) comienza con el índice j, donde se encontró el máximo anterior (A). Para asegurarse de que C viene después de B en la secuencia, sigue buscando hasta que encuentra las barras más recientes. Para mantener el orden adecuado del patrón, la condición if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time) verifica si la barra actual es un swing alto dentro del rango de retrospectiva especificado y se asegura de que su marca de tiempo sea posterior a B. 

El precio y el momento de un máximo legítimo se guardan en C y C_time, respectivamente, y a los objetos de línea de tendencia y etiqueta de texto se les asignan nombres distintos (C_line y C_letter). Utilizando ObjectCreate(chart_id, C_line, OBJ_TREND, 0, C_time, C, time[l+LookbackBars], C), se construye una línea de tendencia para resaltar la etiqueta de texto «C» en el máximo indicado. Para diferenciar visualmente la línea de tendencia C de otros segmentos, ObjectSetInteger(chart_id, C_line, OBJPROP_COLOR, clrSaddleBrown); establece el color de la línea en SaddleBrown.

El bloque condicional final garantiza que los patrones superpuestos se gestionen correctamente: i = l; impide que los puntos de oscilación anteriores se utilicen en otro patrón si la superposición se establece en falso. Se pueden superponer varios patrones si la superposición es verdadera, ya que i = i; conserva el valor actual. Después de determinar el primer máximo legítimo (C), se rompe; finalmente se termina el bucle, garantizando la eficiencia al evitar repeticiones innecesarias.

2.1.6. Identificación del mínimo oscilante (D)

La identificación del mínimo oscilante (D) se realiza después de determinar el máximo oscilante (C). Esto garantiza que la acción del precio se secuencie correctamente para la producción de patrones armónicos. Para encontrar el siguiente mínimo oscilante después de C en orden cronológico, la búsqueda de D comienza desde la ubicación donde se identificó C. Para verificar un mínimo oscilante legítimo, deben cumplirse dos requisitos. En primer lugar, dentro del periodo de referencia designado, el precio actual debe considerarse un mínimo oscilante. En segundo lugar, para mantener el orden correcto de los patrones, su marca de tiempo debe ser posterior a la de C.

Se anotan el precio y el momento de un mínimo oscilante legítimo, y se crean identificadores distintos para la línea de tendencia y la etiqueta de texto que le corresponde. Para que sea más fácil detectar el mínimo dentro del patrón, se crea una línea de tendencia para enfatizarlo y se inserta una etiqueta de texto en su ubicación para designarlo visualmente.

Ejemplo:

//D
double D; // Price of the swing low (D).
datetime D_time; // Time of the swing low (D).
string D_line; // Unique name for the trend line object.
string D_letter; // Unique name for the text label object.

//Trend
string XA_line; // Unique name for XA trend line object.
string AB_line; // Unique name for AB trend line object.
string BC_line; // Unique name for BC trend line object.
string CD_line; // Unique name for CD trend line object.

//+------------------------------------------------------------------+
//| 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(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, X_time, X); // Create text object for X
            ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
            ObjectCreate(chart_id,X_line,OBJ_TREND,0,X_time,X,time[i+LookbackBars],X); // Create line to mark X

            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time)
                 {

                  A = high[j]; // Price of the swing high (A).
                  A_time = time[j]; // Time of the swing high (A).
                  A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object.
                  A_letter = StringFormat("A%d", j); // Unique name for the text label object.

                  ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, A_time, A); // Create text object for A
                  ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                  ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                  ObjectCreate(chart_id,A_line,OBJ_TREND,0,A_time,A,time[j+LookbackBars],A); // Create line to mark A
                  ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time)
                       {

                        // If a swing low is found, store its price, time, and create a name for the object.
                        B = low[k]; // Price of the swing low (B).
                        B_time = time[k]; // Time of the swing low (B).
                        B_line = StringFormat("BLow%d", k); // Unique name for the trend line object.
                        B_letter = StringFormat("B%d", k); // Unique name for the text label object.

                        ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, B_time, B); // Create text object for B
                        ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
                        ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                        ObjectCreate(chart_id,B_line,OBJ_TREND,0,B_time,B,time[k+LookbackBars],B); // Create line to mark B
                        ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

                        for(int l = j ; l < rates_total - LookbackBars; l++)
                          {
                           if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time)
                             {
                              C = high[l]; // Price of the swing high (C).
                              C_time = time[l]; // Time of the swing high (C).
                              C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object.
                              C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                              ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, C_time, C); // Create text object for C
                              ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
                              ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                              ObjectCreate(chart_id,C_line,OBJ_TREND,0,C_time,C,time[l+LookbackBars],C); // Create line to mark C
                              ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                              for(int m = l; m < rates_total - (LookbackBars / 2); m++)
                                {
                                 if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time)
                                   {
                                    D = low[m]; // Price of the swing low (D).
                                    D_time = time[m]; // Time of the swing low (D).
                                    D_line = StringFormat("DLow%d", m); // Unique name for the trend line object.
                                    D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                    ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
                                    ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
                                    ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
                                    ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                                    XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line.
                                    ObjectCreate(chart_id,XA_line,OBJ_TREND,0,X_time,X,A_time,A); // Create line to connect XA
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

                                    AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line.
                                    ObjectCreate(chart_id,AB_line,OBJ_TREND,0,A_time,A,B_time,B); // Create line to connect AB
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

                                    BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line.
                                    ObjectCreate(chart_id,BC_line,OBJ_TREND,0,B_time,B,C_time,C); // Create line to connect BC
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

                                    CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line.
                                    ObjectCreate(chart_id,CD_line,OBJ_TREND,0,C_time,C,D_time,D); // Create line to connect CD
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

                                    if(overlap == false)
                                      {
                                       i = m;
                                      }
                                    if(overlap == true)
                                      {
                                       i = i;
                                      }
                                    break;

                                   }
                                }

                              break;

                             }
                          }

                        break;

                       }
                    }

                  break;
                 }
              }
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }

Resultado:

Figura 9. Mínimo oscilante (D)

Explicación:

El precio del mínimo oscilante en el punto D del patrón reconocido se representa mediante la variable D. D es un punto crucial en el trading con patrones armónicos, donde la acción del precio puede dar un giro y completar el patrón. D_time registra la marca de tiempo en la que se produjo este mínimo, además de D. Para identificar visualmente este mínimo oscilante, también se crean la línea de tendencia y el elemento de texto en el gráfico, y se etiquetan utilizando D_line y D_letter.

El programa recorre los datos de precios para encontrar el punto D en el bucle que comienza con for(int m = l; m < rates_total - (LookbackBars / 2); m++).... Para determinar si el precio actual en el índice "m" es un mínimo oscilante, utiliza la función IsSwingLow(), asegurándose de que el mínimo se encuentre después del punto C en el tiempo. El precio y el tiempo de un mínimo válido se asignan a D y D_time, respectivamente.

El indicador genera elementos visuales en el gráfico después de que se haya localizado el punto D. En la ubicación del punto más bajo, la función ObjectCreate() crea una etiqueta de texto (D_letter) con la letra "D" y la tiñe de verde para mayor comodidad. Para mantener la representación visual consistente, también se dibuja una línea de tendencia (D_line) en el punto D, que se extiende durante períodos de LookbackBars y se le asigna el color marrón (clrSaddleBrown).

En este punto, el programa también crea líneas de tendencia que vinculan los puntos de oscilación que se han identificado. Para conectar visualmente los puntos correspondientes (X a A, A a B, B a C y C a D), se crean los objetos XA_line, AB_line, BC_line y CD_line. Estas líneas ayudan a los traders a analizar posibles cambios de precios al delinear el patrón armónico. El ancho y el color de estas líneas se ajustan para mejorar la visibilidad en el gráfico.

Después de reconocer con éxito el patrón XABCD, es fundamental resolver algunas cuestiones cruciales. La lógica del programa es que después de identificar el punto X, no actualizará X hasta que finalice la secuencia ABCD. Esto implica que el programa no cambiará X si surge un nuevo mínimo inferior al X determinado originalmente entre X y A. En consecuencia, el patrón podría no reflejar con precisión la estructura real del mercado.

Debemos implementar un sistema que determine continuamente si X sigue siendo el valor más bajo entre X y A para garantizar la validez del patrón. Es necesario actualizar dinámicamente X si se produce un mínimo más bajo dentro de este rango. Para garantizar que no se pasen por alto puntos más altos en este segmento, A también debe ser el máximo más alto entre A y B. De manera similar, C debe ser el máximo más alto entre C y D, y B debe ser el mínimo más bajo entre B y C. Podemos mejorar la precisión de detección de patrones y evitar identificaciones erróneas provocadas por una selección estricta de puntos siguiendo estos requisitos. Este método preserva la integridad de la estructura del patrón armónico y al mismo tiempo permite que la indicación se mantenga adaptable y receptiva a nuevas actividades de precios.

Ejemplo:

//X
int x_a_bars; // Number of bars between XA
int x_lowest_index; // Index of the lowest bar
double x_a_ll; // Price of the lowest bar
datetime x_a_ll_t; // Time of the lowest bar

//A
int a_b_bars; // Number of bars between AB
int a_highest_index; // Index of the highest bar
double a_b_hh; // Price of the highest bar
datetime a_b_hh_t; // Time of the highest bar

//B
int b_c_bars; // Number of bars between BC
int b_lowest_index; // Index of the lowest bar
double b_c_ll; // Price of the lowest bar
datetime b_c_ll_t; // Time of the lowest bar

//C
int c_d_bars; // Number of bars between CD
int c_highest_index; // Index of the highest bar
double c_d_hh; // Price of the highest bar
datetime c_d_hh_t; // Time of the highest bar

//+------------------------------------------------------------------+
//| 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(rates_total >= bars_check)
     {

      for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
        {
         if(IsSwingLow(low, i, LookbackBars))
           {
            // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
            X = low[i]; // Price of the swing low (X).
            X_time = time[i]; // Time of the swing low (X).
            X_line = StringFormat("XLow%d", i); // Unique name for the trend line object.
            X_letter = StringFormat("X%d", i); // Unique name for the text label object.

            for(int j = i; j < rates_total - LookbackBars; j++)
              {
               if(IsSwingHigh(high, j, LookbackBars) && time[j] > X_time)
                 {

                  A = high[j]; // Price of the swing high (A).
                  A_time = time[j]; // Time of the swing high (A).
                  A_line = StringFormat("AHigh%d", j); // Unique name for the trend line object.
                  A_letter = StringFormat("A%d", j); // Unique name for the text label object.

                  for(int k = j; k < rates_total - LookbackBars; k++)
                    {
                     if(IsSwingLow(low, k, LookbackBars) && time[k] > A_time)
                       {

                        // If a swing low is found, store its price, time, and create a name for the object.
                        B = low[k]; // Price of the swing low (B).
                        B_time = time[k]; // Time of the swing low (B).
                        B_line = StringFormat("BLow%d", k); // Unique name for the trend line object.
                        B_letter = StringFormat("B%d", k); // Unique name for the text label object.

                        for(int l = j ; l < rates_total - LookbackBars; l++)
                          {
                           if(IsSwingHigh(high, l, LookbackBars) && time[l] > B_time)
                             {
                              C = high[l]; // Price of the swing high (C).
                              C_time = time[l]; // Time of the swing high (C).
                              C_line = StringFormat("CHigh%d", l); // Unique name for the trend line object.
                              C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                              for(int m = l; m < rates_total - (LookbackBars / 2); m++)
                                {
                                 if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time)
                                   {
                                    D = low[m]; // Price of the swing low (D).
                                    D_time = time[m]; // Time of the swing low (D).
                                    D_line = StringFormat("DLow%d", m); // Unique name for the trend line object.
                                    D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                    //D
                                    ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
                                    ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
                                    ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
                                    ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                                    //C
                                    c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time);
                                    c_highest_index = ArrayMaximum(high,l, c_d_bars);

                                    c_d_hh = high[c_highest_index]; //C - D Highest High and time
                                    c_d_hh_t = time[c_highest_index];

                                    ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C
                                    ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
                                    ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C
                                    ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                                    //B
                                    b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t);
                                    b_lowest_index = ArrayMinimum(low,k, b_c_bars);

                                    b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time
                                    b_c_ll_t = time[b_lowest_index];

                                    ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B
                                    ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
                                    ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B
                                    ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

                                    //A
                                    a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t);
                                    a_highest_index = ArrayMaximum(high,j, a_b_bars);

                                    a_b_hh = high[a_highest_index]; //A - B Highest High and time
                                    a_b_hh_t = time[a_highest_index];

                                    ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A
                                    ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                                    ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A
                                    ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

                                    //X
                                    x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t);
                                    x_lowest_index = ArrayMinimum(low,i, x_a_bars);

                                    x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time
                                    x_a_ll_t = time[x_lowest_index];

                                    ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X
                                    ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
                                    ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X

                                    XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line.
                                    ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

                                    AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line.
                                    ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

                                    BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line.
                                    ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

                                    CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line.
                                    ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

                                    if(overlap == false)
                                      {
                                       i = m;
                                      }
                                    if(overlap == true)
                                      {
                                       i = i;
                                      }
                                    break;

                                   }
                                }

                              break;

                             }
                          }

                        break;

                       }
                    }

                  break;
                 }
              }
           }
        }
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }

Resultado:

Figura 10. Nuevo XABCD

Explicación:

El punto X sirve como punto de referencia inicial para detectar posibles patrones de reversión en el trading de patrones armónicos. En un patrón alcista, significa un gran mínimo oscilante; en un patrón bajista, significa un máximo oscilante. Dado que sienta las bases para medir los retrocesos y extensiones de Fibonacci (que ayudan a confirmar las formaciones armónicas), este punto es esencial. El propósito del código es reconocer X, registrar su tiempo y precio e indicarlo visualmente en el gráfico. Para crear la estructura completa del patrón, también une X a los otros puntos de oscilación (A, B, C y D).

Se utilizan varias variables importantes para almacenar datos sobre X. La cantidad de barras entre X y A se registra mediante la variable x_a_bars, que ayuda a determinar las separaciones relativas entre los tramos del patrón. Para señalar con precisión la barra donde ocurrió X, la variable x_lowest_index mantiene un registro del índice de la matriz del precio más bajo dentro de este rango. La marca de tiempo asociada con el precio más bajo se guarda en x_a_ll_t, mientras que el precio en sí se asigna a "x_a_ll".

Encontrar la cantidad de barras entre X y A usando la función Bars() es el primer paso para determinar X. El rango requerido para el análisis lo proporciona esta función, que cuenta la cantidad de barras entre estos dos puntos de oscilación. El programa utiliza ArrayMinimum() para buscar entre las barras designadas y devolver el índice del precio más bajo cuando se ha determinado el rango. Luego, utilizando las matrices low[x_lowest_index] y time[x_lowest_index], respectivamente, se obtienen el precio real y el tiempo de X.

Una vez identificado X, el código utiliza el objeto de MetaTrader para indicarlo de forma visible en el gráfico. Para asegurarse de que pueda identificar rápidamente este momento crucial, se utiliza ObjectCreate() para producir una etiqueta de texto (X_letter) en el punto más bajo del eje X. Para mayor claridad, se asigna el texto «X» a la etiqueta de texto, que se coloca en el momento y el precio de X. Para resaltar adecuadamente el mínimo oscilante, también se forma una línea de tendencia (X_line) en X utilizando ObjectCreate(). La línea se extiende por un número predeterminado de barras (LookbackBars).

El programa crea líneas de tendencia para vincular X con otras ubicaciones importantes (A, B, C y D) después de que se haya determinado X. Puedes ver mejor el patrón armónico con estas líneas de tendencia. La función ObjectCreate() se utiliza para dibujar la primera conexión, la línea XA, que conecta X y A. Esta línea tiene un ancho de tres píxeles y es de color marrón (clrSaddleBrown) para garantizar la visibilidad. Las patas restantes del patrón (la línea AB, que conecta A con B; la línea BC, que conecta B con C; y la línea CD, que conecta C con D) se representan de manera similar con líneas de tendencia adicionales. Para producir una representación visual unificada, cada una de estas líneas mantiene las mismas características de ancho y color.

2.2. Validación del punto B mediante retrocesos de Fibonacci de XA

En esta sección verificaremos el mínimo B comprobando si se mantiene por encima del nivel del 78,6 % y cae por debajo del nivel de retroceso del 61,8 % de XA. Esta fase de validación es esencial en el trading con patrones alcistas, ya que garantiza que el punto B cumpla con las proporciones de Fibonacci necesarias, que ayudan a definir la estructura del patrón. Hay más posibilidades de que se produzca una disposición armónica legítima si B se encuentra en este rango. De lo contrario, el patrón podría no ser genuino o necesitar más confirmación si B es demasiado alto o demasiado bajo.

Primero determinamos los niveles de retroceso de Fibonacci para el tramo XA para llevar a cabo esta validación. Un método popular de análisis técnico para detectar posibles reversiones en la acción del precio es el retroceso de Fibonacci.

Utilizaremos las herramientas gráficas de MQL5 para agregar un objeto de retroceso de Fibonacci a la representación del gráfico además de validar B. La herramienta de Fibonacci se trazará utilizando la función ObjectCreate(), con X como punto de inicio y A como punto final. Una vez creado el objeto, lo modificaremos incluyendo niveles de Fibonacci adicionales que MetaTrader 5 no admite de forma predeterminada. Esto garantiza la visualización de todas las zonas de retroceso pertinentes.

Ejemplo:

//Fibo
string XA_fibo; // Unique name for XA fibo
double lvl_61_8; // Level 61.8
double lvl_78_6; // Level 78.6
string fibo_618_786; // Unique name for the object that marks 61.8 and 78.6.
string fibo_78_6_txt; // Unique name for text "78.6" cause its not available by default
for(int m = l; m < rates_total - (LookbackBars / 2); m++)
  {
   if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time)
     {
      D = low[m]; // Price of the swing low (D).
      D_time = time[m]; // Time of the swing low (D).
      D_line = StringFormat("DLow%d", m); // Unique name for the trend line object.
      D_letter = StringFormat("D%d", m); // Unique name for the text label object.

      //D
      ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
      ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
      ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
      ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
      ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

      //C
      c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time);
      c_highest_index = ArrayMaximum(high,l, c_d_bars);

      c_d_hh = high[c_highest_index]; //C - D Highest High and time
      c_d_hh_t = time[c_highest_index];

      ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C
      ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
      ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
      ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C
      ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

      //B
      b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t);
      b_lowest_index = ArrayMinimum(low,k, b_c_bars);

      b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time
      b_c_ll_t = time[b_lowest_index];

      ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B
      ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
      ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
      ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B
      ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

      //A
      a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t);
      a_highest_index = ArrayMaximum(high,j, a_b_bars);

      a_b_hh = high[a_highest_index]; //A - B Highest High and time
      a_b_hh_t = time[a_highest_index];

      ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A
      ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
      ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
      ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A
      ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

      //X
      x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t);
      x_lowest_index = ArrayMinimum(low,i, x_a_bars);

      x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time
      x_a_ll_t = time[x_lowest_index];

      ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X
      ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
      ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X

      XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line.
      ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA
      ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
      ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

      AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line.
      ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB
      ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
      ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

      BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line.
      ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC
      ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
      ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

      CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line.
      ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD
      ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
      ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

      lvl_61_8 = a_b_hh - ((61.8/100) * (a_b_hh - x_a_ll)); // Calculating level 61.8
      lvl_78_6 = a_b_hh - ((78.6/100) * (a_b_hh - x_a_ll)); // Calculating level 78.6

      //XA FIBO
      XA_fibo = StringFormat("XA FIB0 %d", i);
      ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo
      ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels
      for(int i = 1; i <= 6; i++)
        {

         ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown

        }

      fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i);
      fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i);

      ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6
      ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown

      ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6
      ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed
      ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color
      ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size

      if(overlap == false)
        {
         i = m;
        }
      if(overlap == true)
        {
         i = i;
        }
      break;

     }
  }
Resultado:

Figura 11. Fibonacci

Explicación:

Para confirmar que el punto B se encuentra dentro de un rango dado, aplicamos el retroceso de Fibonacci, para el tramo XA en esta sección. En el análisis técnico, el retroceso de Fibonacci se utiliza con frecuencia para señalar posibles zonas de reversión. Es esencial para verificar si el punto B corresponde con el retroceso anticipado de XA. En particular, lo ideal sería que B se situara en el rango de los niveles de retroceso del 61,8 % al 78,6 % de XA. Será más sencillo determinar si el punto B se encuentra dentro del rango aceptable si creamos un objeto de retroceso de Fibonacci y resaltamos esta zona en el gráfico. Para ello, primero establecemos algunas variables importantes. Para garantizar que cada dibujo de Fibonacci se identifique correctamente, la variable XA_fibo mantiene un nombre único para el objeto de retroceso de Fibonacci.

El objeto de retroceso de Fibonacci en el tramo XA se crea utilizando la función ObjectCreate() una vez determinados los niveles. Puede comprender cómo interactúa el precio con los niveles de Fibonacci observando este objeto, que se extiende desde el punto X (mínimo) hasta el punto A (máximo). Hemos establecido el número de niveles de Fibonacci en seis y hemos cambiado su color a SaddleBrown para mejorar la claridad de la visualización y garantizar que cada nivel sea fácilmente observable y se distinga de otros elementos del gráfico. 

Destacamos aún más la zona de retroceso del 61,8 % al 78,6 % creando un objeto rectangular que llama la atención sobre ella, ya que se trata de un área importante. Es fácil determinar si el punto B se encuentra dentro del rango esperado, ya que el rectángulo abarca los niveles del 61,8 % y el 78,6 %. Esto facilita la rápida validación de patrones sin necesidad de realizar laboriosas comprobaciones de los niveles de retroceso. Además, el rectángulo tiene el color SaddleBrown, lo que mantiene la coherencia visual del gráfico.

También incluimos una etiqueta de texto para el nivel de retroceso del 78,6 %, lo que supone otro cambio significativo. Generamos manualmente un objeto de texto para mostrar «78,6» en el nivel de precio correspondiente, ya que la herramienta Fibonacci de MetaTrader 5 no incluye este nivel de forma predeterminada. Esto aumenta la precisión en la identificación de patrones armónicos, ya que garantiza que los operadores puedan observar la ubicación exacta del retroceso del 78,6 %. Simplificamos la verificación de si el punto B es legítimo dentro del patrón armónico poniendo en práctica estos cambios. El objeto de retroceso de Fibonacci, la zona marcada entre el 61,8 % y el 78,6 %, y la etiqueta de texto adicional garantizan una técnica precisa y visualmente intuitiva para validar los patrones armónicos. 

Ahora que tenemos todo lo necesario, debemos asegurarnos de que todos los objetos del gráfico solo se dibujen cuando se cumplan los requisitos para un patrón válido. En concreto, el punto D debe estar por debajo del punto X, y el punto B debe estar dentro del retroceso del 61,8 % al 78,6 % de XA. No se deben añadir objetos al gráfico si no se cumplen estos requisitos.

Ejemplos:

for(int m = l; m < rates_total - (LookbackBars / 2); m++)
  {
   if(IsSwingLow(low, m, LookbackBars / 2) && time[m] > C_time)
     {
      D = low[m]; // Price of the swing low (D).
      D_time = time[m]; // Time of the swing low (D).
      D_line = StringFormat("DLow%d", m); // Unique name for the trend line object.
      D_letter = StringFormat("D%d", m); // Unique name for the text label object.

      //C
      c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time, D_time);
      c_highest_index = ArrayMaximum(high,l, c_d_bars);

      c_d_hh = high[c_highest_index]; //C - D Highest High and time
      c_d_hh_t = time[c_highest_index];

      //B
      b_c_bars = Bars(_Symbol,PERIOD_CURRENT,B_time, c_d_hh_t);
      b_lowest_index = ArrayMinimum(low,k, b_c_bars);

      b_c_ll = low[b_lowest_index]; //B - C Lowest Low and time
      b_c_ll_t = time[b_lowest_index];

      //A
      a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time, b_c_ll_t);
      a_highest_index = ArrayMaximum(high,j, a_b_bars);

      a_b_hh = high[a_highest_index]; //A - B Highest High and time
      a_b_hh_t = time[a_highest_index];

      //X
      x_a_bars = Bars(_Symbol,PERIOD_CURRENT,X_time, a_b_hh_t);
      x_lowest_index = ArrayMinimum(low,i, x_a_bars);

      x_a_ll = low[x_lowest_index]; //X - A Lowest Low and time
      x_a_ll_t = time[x_lowest_index];

      lvl_61_8 = a_b_hh - ((61.8/100) * (a_b_hh - x_a_ll)); // Calculating level 61.8
      lvl_78_6 = a_b_hh - ((78.6/100) * (a_b_hh - x_a_ll)); // Calculating level 78.6

      if((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll))
        {

         //D
         ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
         ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
         ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
         ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
         ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

         //C
         ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C
         ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
         ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
         ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C
         ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

         //B
         ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B
         ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
         ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
         ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B
         ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

         //A
         ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A
         ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
         ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
         ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A
         ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

         //X
         ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X
         ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
         ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X

         XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line.
         ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA
         ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
         ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

         AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line.
         ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB
         ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
         ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

         BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line.
         ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC
         ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
         ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

         CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line.
         ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD
         ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
         ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

         //XA FIBO
         XA_fibo = StringFormat("XA FIB0 %d", i);
         ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo
         ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels
         for(int i = 1; i <= 6; i++)
           {
            ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown
           }

         fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i);
         fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i);

         ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6
         ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown

         ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6
         ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed
         ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color
         ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size

        }

      if(overlap == false)
        {
         i = m;
        }
      if(overlap == true)
        {
         i = i;
        }
      break;
     }
  }

Resultado:

Figure 12. B y X

Explicación:

Al evaluar como verdadera, la condición if ((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll)) garantiza que todos los objetos del gráfico solo se dibujen cuando se encuentre un patrón armónico adecuado. Esto evita que elementos superfluos saturen el gráfico con patrones erróneos. A continuación, el código crea y muestra todos los componentes necesarios, como los niveles de retroceso de Fibonacci, las líneas de tendencia que unen X, A, B, C y D, y las etiquetas de texto que identifican cada punto importante, cuando se cumple el requisito. Al garantizar que el indicador solo muestre patrones correctamente formados, este método mejora la precisión y mantiene una visualización clara e informativa del gráfico.

2.3. Visualización de los triángulos XAB y BCD

Para visualizar mejor el patrón armónico, marcaremos las formas XAB y BCD con triángulos en esta sección. Como ayuda visual, estos triángulos facilitarán el reconocimiento y el examen de la estructura del patrón en el gráfico.

Esto se logrará conectando los puntos X, A y B para el primer triángulo y B, C y D para el segundo triángulo utilizando objetos OBJ_TRIANGLE. El triángulo BCD representará el último tramo del patrón, reforzando la estructura que conduce al punto D, la posible zona de reversión, mientras que el triángulo XAB enfatizará el primer tramo, confirmando la relación entre X, A y B.

Ejemplo:

string X_A_B;
string B_C_D;
if((b_c_ll <= lvl_61_8 && b_c_ll >= lvl_78_6) && (D < x_a_ll))
  {

//D
   ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
   ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
   ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
   ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
   ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

//C
   ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_hh_t, c_d_hh); // Create text object for C
   ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
   ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
   ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_hh_t,C,time[c_highest_index+LookbackBars],c_d_hh); // Create line to mark C
   ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

//B
   ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_ll_t, b_c_ll); // Create text object for B
   ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
   ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
   ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,time[b_lowest_index+LookbackBars],b_c_ll); // Create line to mark B
   ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

//A
   ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_hh_t, a_b_hh); // Create text object for A
   ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
   ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
   ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,time[a_highest_index+LookbackBars],a_b_hh); // Create line to mark A
   ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

//X
   ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_ll_t, x_a_ll); // Create text object for X
   ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
   ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,time[x_lowest_index+LookbackBars],x_a_ll); // Create line to mark X

   XA_line = StringFormat("XA Line%d", m); // Unique name for the XA line.
   ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create line to connect XA
   ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
   ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

   AB_line = StringFormat("AB Line%d", m); // Unique name for the AB line.
   ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); // Create line to connect AB
   ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
   ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

   BC_line = StringFormat("BC Line%d", m); // Unique name for the BC line.
   ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); // Create line to connect BC
   ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
   ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

   CD_line = StringFormat("CD Line%d", m); // Unique name for the CD line.
   ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_hh_t,c_d_hh,D_time,D); // Create line to connect CD
   ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
   ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

//XA FIBO
   XA_fibo = StringFormat("XA FIB0 %d", i);
   ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); // Create XA fibo
   ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels
   for(int i = 1; i <= 6; i++)
     {
      ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown
     }

   fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 %d", i);
   fibo_78_6_txt = StringFormat("Fibo 78.6 Text %d", i);

   ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_ll_t, lvl_61_8,b_c_ll_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6
   ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown

   ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_ll_t,lvl_78_6); // Create text for level 78.6
   ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed
   ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color
   ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size

   X_A_B = StringFormat("XAB %d", i);
   ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,x_a_ll_t,x_a_ll, a_b_hh_t, a_b_hh, b_c_ll_t, b_c_ll);
   ObjectSetInteger(chart_id,X_A_B,OBJPROP_COLOR,clrCornflowerBlue);
   ObjectSetInteger(chart_id,X_A_B,OBJPROP_FILL,true);

   B_C_D = StringFormat("BCD %d", i);
   ObjectCreate(chart_id,B_C_D,OBJ_TRIANGLE,0,b_c_ll_t,b_c_ll, c_d_hh_t, c_d_hh, D_time, D);
   ObjectSetInteger(chart_id,B_C_D,OBJPROP_COLOR,clrCornflowerBlue);
   ObjectSetInteger(chart_id,B_C_D,OBJPROP_FILL,true);
  }

Resultado:

Figura 13. Patrón alcista

Explicación:

Los nombres únicos de los triángulos que constituyen los segmentos XAB y BCD del patrón armónico se almacenan en las variables X_A_B y B_C_D. Se utilizan tres puntos (X, A y B) para generar el triángulo X_A_B. Estos puntos se identifican por sus respectivas marcas de tiempo y valores de precio (x_a_ll_t, x_a_ll, a_b_hh_t, a_b_hh, b_c_ll_t, b_c_ll). El triángulo B_C_D, que utiliza (b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, D_time, D), une de manera similar B, C y D. StringFormat() se utiliza para dar a cada triángulo un nombre único, lo que permite dibujar varios patrones sin problemas de nombres.

ObjectSetInteger() se utiliza para establecer las características visuales de los triángulos una vez que se han generado utilizando ObjectCreate(). El atributo OBJPROP_FILL se establece como verdadero, lo que garantiza que los triángulos se rellenen en lugar de delinearlos, y ambos triángulos tengan el color azul aciano (clrCornflowerBlue). Al mejorar la claridad y simplificar la verificación de posibles oportunidades comerciales, estos componentes visuales ayudan a los operadores a identificar rápidamente la estructura del patrón.

2.4. Punto de entrada, stop loss y take profit

En esta sección, estableceremos el punto de entrada, el stop loss (SL) y el take profit (TP) para identificar los niveles importantes de negociación para el patrón armónico descubierto. El punto D, el mínimo más bajo del patrón, es donde se establece el stop loss. Dado que los patrones armónicos predicen reversiones de precios, establecer el stop loss en D minimiza las posibles pérdidas al garantizar que el patrón se invalida si el mercado sube por encima de este nivel.

El índice del punto D más la mitad de los LookbackBars es el punto de entrada. Esto permite un pequeño retraso tras la identificación de D, lo que permite confirmar la evolución del precio antes de realizar una transacción. Dado que el punto C es el máximo antes del movimiento alcista previsto, sirve como nivel de take profit. Las etiquetas y las líneas horizontales para los niveles SL, de entrada y TP se crean para representar estos niveles en el gráfico. Estos elementos garantizan la claridad al operar basándose en el patrón, ya que ayudan a los operadores a identificar rápidamente la configuración de la operación.

Ejemplo:

//Signal
string buy_txt;
string sell_txt;
string entry_line;
string tp_line;
string sl_line;
string tp_txt;
string sl_txt;
buy_txt = StringFormat("Buy %d", i);

ObjectCreate(chart_id,buy_txt,OBJ_TEXT,0,time[m + (LookbackBars / 2)],open[m  + (LookbackBars / 2)]);
ObjectSetString(chart_id,buy_txt,OBJPROP_TEXT,"BUY");
ObjectSetInteger(chart_id,buy_txt,OBJPROP_COLOR,clrCornflowerBlue);
ObjectSetInteger(chart_id,buy_txt,OBJPROP_FONTSIZE,10);

entry_line = StringFormat("Buy Entry Line %d", i);
ObjectCreate(chart_id,entry_line,OBJ_TREND,0,time[m  + (LookbackBars / 2)],open[m  + (LookbackBars / 2)], c_d_hh_t,open[m  + (LookbackBars / 2)]);
ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,3);
ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrCornflowerBlue);

sl_txt = StringFormat("Buy SL %d", i);
ObjectCreate(chart_id,sl_txt,OBJ_TEXT,0,time[m + (LookbackBars / 2)],D);
ObjectSetString(chart_id,sl_txt,OBJPROP_TEXT,"SL");
ObjectSetInteger(chart_id,sl_txt,OBJPROP_COLOR,clrCornflowerBlue);
ObjectSetInteger(chart_id,sl_txt,OBJPROP_FONTSIZE,10);

sl_line = StringFormat("Buy SL Line %d", i);
ObjectCreate(chart_id,sl_line,OBJ_TREND,0,time[m + (LookbackBars / 2)],D,c_d_hh_t,D);
ObjectSetInteger(chart_id,sl_line,OBJPROP_WIDTH,3);
ObjectSetInteger(chart_id,sl_line,OBJPROP_COLOR,clrCornflowerBlue);

tp_txt = StringFormat("Buy TP %d", i);
ObjectCreate(chart_id,tp_txt,OBJ_TEXT,0,time[m +(LookbackBars / 2)],c_d_hh);
ObjectSetString(chart_id,tp_txt,OBJPROP_TEXT,"TP");
ObjectSetInteger(chart_id,tp_txt,OBJPROP_COLOR,clrCornflowerBlue);
ObjectSetInteger(chart_id,tp_txt,OBJPROP_FONTSIZE,10);

tp_line = StringFormat("Buy TP Line %d", i);
ObjectCreate(chart_id,tp_line,OBJ_TREND,0,time[m + (LookbackBars / 2)],c_d_hh,c_d_hh_t,c_d_hh);
ObjectSetInteger(chart_id,tp_line,OBJPROP_WIDTH,3);
ObjectSetInteger(chart_id,tp_line,OBJPROP_COLOR,clrCornflowerBlue);

Resultado:

Figura 14. Señales

Explicación:

Los niveles de señal de compra, punto de entrada, stop loss (SL) y take profit (TP) para el patrón armónico se definen y crean como objetos visuales en el gráfico en esta sección. Para garantizar que los elementos se mantengan e identifiquen fácilmente en el gráfico, las variables buy_txt, sell_txt, entry_line, tp_line, sl_line, tp_txt y sl_txt almacenan nombres únicos para los objetos correspondientes. En el punto de entrada, el objeto buy_txt es una etiqueta de texto que dice "BUY" para indicar la dirección de la operación. La configuración de la operación se visualiza claramente gracias a la línea de entrada (entry_line), una línea de tendencia horizontal que indica el precio de entrada en la apertura [m + (LookbackBars / 2)].

Del mismo modo, la etiqueta de stop loss y su línea de tendencia asociada se denotan por sl_txt y sl_line, respectivamente, y ambas se sitúan en D, el mínimo más bajo del patrón. El stop loss garantiza que la operación se anule si el precio supera este umbral. Tp_txt y tp_line, que se sitúan en c_d_hh, el máximo anterior, indican el nivel de take profit. Estos elementos gráficos están configurados con un color distintivo (clrCornflowerBlue) para que puedas reconocer rápidamente los niveles de negociación importantes en el gráfico. Estos componentes respaldan el desarrollo de una estrategia comercial ordenada y transparente basada en el patrón armónico.


3. Creación de patrones armónicos bajistas

La misma lógica que utilizamos para identificar patrones armónicos alcistas se modificará para los patrones bajistas en esta parte. No hay necesidad de enfatizar cada característica, ya que solo se trata del patrón opuesto al alcista. Ahora, un oscilación alta se representará con X, una oscilación baja con A, una oscilación alta con B, una oscilación baja con C y una oscilación alta final con D. Esta es la principal diferencia. Esto garantiza que el patrón represente con precisión un escenario bajista, lo que indica un posible momento para vender.

El punto B se seguirá verificando utilizando los niveles de retroceso de Fibonacci para asegurarse de que se encuentra dentro del rango adecuado en relación con el tramo XA. De manera similar, el patrón se delineará mediante líneas de tendencia, triángulos y otros elementos gráficos, pero sus ubicaciones se modificarán para ajustarse a la estructura bajista. La entrada se fijará en el índice de D más la mitad de las LookbackBars, el take-profit (TP) en el punto C y el stop-loss (SL) en el punto D. La misma lógica se aplica porque en realidad se trata de la inversa del patrón alcista; por lo tanto, no repetiremos los argumentos en detalle.

Ejemplo:

int x_highest_index;   // Stores the index of the highest price point (X) in the pattern
double x_a_hh;         // Holds the price value of the swing high at point X
datetime x_a_hh_t;     // Stores the timestamp of when the swing high at X occurred

int a_lowest_index;   // Stores the index of the lowest price point (A) in the pattern
double a_b_ll;        // Holds the price value of the swing low at point A
datetime a_b_ll_t;    // Stores the timestamp of when the swing low at A occurred

int b_highest_index;   // Stores the index of the highest price point (B) in the pattern
double b_c_hh;         // Holds the price value of the swing high at point B
datetime b_c_hh_t;     // Stores the timestamp of when the swing high at B occurred
int c_lowest_index;   // Stores the index of the lowest price point (C) in the pattern
double c_d_ll;        // Holds the price value of the swing low at point C
datetime c_d_ll_t;    // Stores the timestamp of when the swing low at C occurred
if(rates_total >= bars_check)
  {
   for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++)
     {
      if(IsSwingHigh(high, i, LookbackBars))
        {

         X = high[i];                   // Assign the highest price at index 'i' to X
         X_time = time[i];              // Assign the corresponding time value to X_time
         X_line = StringFormat("XHigh%d", i);  // Create a unique string identifier for the X-high trendline
         X_letter = StringFormat("XB%d", i);   // Create a unique string identifier for the X-high label

         for(int j = i; j < rates_total - LookbackBars; j++)
           {
            if(IsSwingLow(low, j, LookbackBars) && time[j] > X_time)
              {
               A = low[j];                    // Assign the lowest price at index 'j' to A
               A_time = time[j];               // Assign the corresponding time value to A_time
               A_line = StringFormat("ALow%d", j);  // Create a unique string identifier for the A-low trendline
               A_letter = StringFormat("AB%d", j);  // Create a unique string identifier for the A-low label

               for(int k = j; k < rates_total - LookbackBars; k++)
                 {
                  if(IsSwingHigh(high, k, LookbackBars) && time[k] > A_time)
                    {

                     B = high[k];                    // Assign the highest price at index 'k' to B
                     B_time = time[k];                // Assign the corresponding time value to B_time
                     B_line = StringFormat("BHigh%d", k);  // Create a unique string identifier for the B-high trendline
                     B_letter = StringFormat("BB%d", k);   // Create a unique string identifier for the B-high label

                     for(int l = k; l < rates_total - LookbackBars; l++)
                       {
                        if(IsSwingLow(low, l, LookbackBars) && time[l] > B_time)
                          {

                           C = low[l];                      // Assign the lowest price at index 'l' to C
                           C_time = time[l];                // Assign the corresponding time value to C_time
                           C_line = StringFormat("CLow%d", l);  // Create a unique string identifier for the C-low trendline
                           C_letter = StringFormat("CB%d", l);   // Create a unique string identifier for the C-low label

                           for(int m = l; m < rates_total - (LookbackBars / 2); m++)
                             {
                              if(IsSwingHigh(high, m, LookbackBars / 2) && time[m] > C_time)
                                {
                                 D = high[m];                      // Assign the highest price at index 'm' to D
                                 D_time = time[m];                 // Assign the corresponding time value to D_time
                                 D_line = StringFormat("DHigh%d", m);  // Create a unique string identifier for the D-high trendline
                                 D_letter = StringFormat("DB%d", m);   // Create a unique string identifier for the D-high label

                                 // C - D Segment: Find the lowest low between C and D
                                 c_d_bars = Bars(_Symbol, PERIOD_CURRENT, C_time, D_time); // Count the number of bars between C and D
                                 c_lowest_index = ArrayMinimum(low, l, c_d_bars); // Find the index of the lowest low in the range

                                 c_d_ll = low[c_lowest_index]; // Store the lowest low (C - D lowest point)
                                 c_d_ll_t = time[c_lowest_index]; // Store the corresponding time for C - D

                                 // B - C Segment: Find the highest high between B and C
                                 b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_ll_t); // Count the number of bars between B and C
                                 b_highest_index = ArrayMaximum(high, k, b_c_bars); // Find the index of the highest high in the range

                                 b_c_hh = high[b_highest_index]; // Store the highest high (B - C highest point)
                                 b_c_hh_t = time[b_highest_index]; // Store the corresponding time for B - C

                                 // A - B Segment: Find the lowest low between A and B
                                 a_b_bars = Bars(_Symbol, PERIOD_CURRENT, A_time, b_c_hh_t); // Count the number of bars between A and B
                                 a_lowest_index = ArrayMinimum(low, j, a_b_bars); // Find the index of the lowest low in the range

                                 a_b_ll = low[a_lowest_index]; // Store the lowest low (A - B lowest point)
                                 a_b_ll_t = time[a_lowest_index]; // Store the corresponding time for A - B

                                 // X - A Segment: Find the highest high between X and A
                                 x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_ll_t); // Count the number of bars between X and A
                                 x_highest_index = ArrayMaximum(high, i, x_a_bars); // Find the index of the highest high in the range

                                 x_a_hh = high[x_highest_index]; // Store the highest high (X - A highest point)
                                 x_a_hh_t = time[x_highest_index]; // Store the corresponding time for X - A

                                 // Fibonacci Retracement Levels: Calculate 61.8% and 78.6% retracement levels from X to A
                                 lvl_61_8 = a_b_ll + ((61.8 / 100) * (x_a_hh - a_b_ll)); // 61.8% retracement level
                                 lvl_78_6 = a_b_ll + ((78.6 / 100) * (x_a_hh - a_b_ll)); // 78.6% retracement level

                                 if((b_c_hh >= lvl_61_8 && b_c_hh <= lvl_78_6) && (D > x_a_hh))
                                   {
                                    //D
                                    ObjectCreate(chart_id, D_letter, OBJ_TEXT, 0, D_time, D); // Create text object for D
                                    ObjectSetString(chart_id, D_letter, OBJPROP_TEXT, "D"); // Set the text to "D".
                                    ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,D_line,OBJ_TREND,0,D_time,D,time[m+LookbackBars],D); // Create line to mark D
                                    ObjectSetInteger(chart_id,D_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                                    //C
                                    ObjectCreate(chart_id, C_letter, OBJ_TEXT, 0, c_d_ll_t, c_d_ll); // Create text object for C
                                    ObjectSetString(chart_id, C_letter, OBJPROP_TEXT, "C"); // Set the text to "C".
                                    ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,C_line,OBJ_TREND,0,c_d_ll_t,C,time[c_lowest_index+LookbackBars],c_d_ll); // Create line to mark C
                                    ObjectSetInteger(chart_id,C_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown

                                    //B
                                    ObjectCreate(chart_id, B_letter, OBJ_TEXT, 0, b_c_hh_t, b_c_hh); // Create text object for B
                                    ObjectSetString(chart_id, B_letter, OBJPROP_TEXT, "B"); // Set the text to "B".
                                    ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,B_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,time[b_highest_index+LookbackBars],b_c_hh); // Create line to mark B
                                    ObjectSetInteger(chart_id,B_line,OBJPROP_COLOR,clrMidnightBlue); // Set line color to MidnightBlue

                                    //A
                                    ObjectCreate(chart_id, A_letter, OBJ_TEXT, 0, a_b_ll_t, a_b_ll); // Create text object for A
                                    ObjectSetString(chart_id, A_letter, OBJPROP_TEXT, "A"); // Set the text to "A".
                                    ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,clrGreen); // Set text color to green
                                    ObjectCreate(chart_id,A_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,time[a_lowest_index+LookbackBars],a_b_ll); // Create line to mark A
                                    ObjectSetInteger(chart_id,A_line,OBJPROP_COLOR,clrGreen); // Set line color to green

                                    //X
                                    ObjectCreate(chart_id, X_letter, OBJ_TEXT, 0, x_a_hh_t, x_a_hh); // Create text object for X
                                    ObjectSetString(chart_id, X_letter, OBJPROP_TEXT, "X"); // Set the text to "X".
                                    ObjectCreate(chart_id,X_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,time[x_highest_index+LookbackBars],x_a_hh); // Create line to mark X

                                    XA_line = StringFormat("XA LineB%d", m); // Unique name for the XA line.
                                    ObjectCreate(chart_id,XA_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); // Create line to connect XA
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,XA_line,OBJPROP_WIDTH,3); // Line width

                                    AB_line = StringFormat("AB LineB%d", m); // Unique name for the AB line.
                                    ObjectCreate(chart_id,AB_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); // Create line to connect AB
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,AB_line,OBJPROP_WIDTH,3); // Line width

                                    BC_line = StringFormat("BC LineB%d", m); // Unique name for the BC line.
                                    ObjectCreate(chart_id,BC_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll); // Create line to connect BC
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,BC_line,OBJPROP_WIDTH,3); // Line width

                                    CD_line = StringFormat("CD LineB%d", m); // Unique name for the CD line.
                                    ObjectCreate(chart_id,CD_line,OBJ_TREND,0,c_d_ll_t,c_d_ll,D_time,D); // Create line to connect CD
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_COLOR,clrSaddleBrown); // Set line color to SaddleBrown
                                    ObjectSetInteger(chart_id,CD_line,OBJPROP_WIDTH,3); // Line width

                                    //XA FIBO
                                    XA_fibo = StringFormat("XA FIB0 B%d", i);
                                    ObjectCreate(chart_id,XA_fibo,OBJ_FIBO,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); // Create XA fibo
                                    ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELS,6); // Number of default levels
                                    for(int i = 1; i <= 6; i++)
                                      {
                                       ObjectSetInteger(chart_id,XA_fibo,OBJPROP_LEVELCOLOR,i,clrSaddleBrown); // Set each level color to SaddleBrown
                                      }

                                    fibo_618_786 = StringFormat("Fibo 78.6 - 61.8 B%d", i);
                                    fibo_78_6_txt = StringFormat("Fibo 78.6 Text B%d", i);

                                    ObjectCreate(chart_id,fibo_618_786,OBJ_RECTANGLE,0,x_a_hh_t, lvl_61_8,b_c_hh_t,lvl_78_6); // Create rectangle object marking 61.8 and 78.6
                                    ObjectSetInteger(chart_id,fibo_618_786,OBJPROP_COLOR,clrSaddleBrown); // Set color to clrSaddleBrown

                                    ObjectCreate(chart_id,fibo_78_6_txt,OBJ_TEXT,0,b_c_hh_t,lvl_78_6); // Create text for level 78.6
                                    ObjectSetString(chart_id,fibo_78_6_txt,OBJPROP_TEXT,"78.6"); // Text to be displayed
                                    ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_COLOR,clrSaddleBrown); // Set text color
                                    ObjectSetInteger(chart_id,fibo_78_6_txt,OBJPROP_FONTSIZE,8); // Set text size
                                    // Create and format the first bearish harmonic pattern (XAB)
                                    X_A_B = StringFormat("XAB B%d", i);
                                    ObjectCreate(chart_id, X_A_B, OBJ_TRIANGLE, 0, x_a_hh_t, x_a_hh, a_b_ll_t, a_b_ll, b_c_hh_t, b_c_hh);
                                    ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, clrMistyRose); // Set color for the pattern
                                    ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); // Fill the triangle shape

                                    // Create and format the second bearish harmonic pattern (BCD)
                                    B_C_D = StringFormat("BCD B%d", i);
                                    ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_hh_t, b_c_hh, c_d_ll_t, c_d_ll, D_time, D);
                                    ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, clrMistyRose);
                                    ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true);

                                    // Create and format the SELL text at the entry position
                                    sell_txt = StringFormat("Sell %d", i);
                                    ObjectCreate(chart_id, sell_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], open[m + (LookbackBars / 2)]);
                                    ObjectSetString(chart_id, sell_txt, OBJPROP_TEXT, "SELL");
                                    ObjectSetInteger(chart_id, sell_txt, OBJPROP_COLOR, clrMagenta); // Set text color
                                    ObjectSetInteger(chart_id, sell_txt, OBJPROP_FONTSIZE, 10); // Set font size

                                    // Create and format the SELL entry line
                                    entry_line = StringFormat("Sell Entry Line %d", i);
                                    ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], open[m + (LookbackBars / 2)], c_d_ll_t, open[m + (LookbackBars / 2)]);
                                    ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 3); // Set line thickness
                                    ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrMagenta); // Set line color

                                    // Create and format the Stop Loss (SL) text
                                    sl_txt = StringFormat("Sell SL %d", i);
                                    ObjectCreate(chart_id, sl_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], D);
                                    ObjectSetString(chart_id, sl_txt, OBJPROP_TEXT, "SL");
                                    ObjectSetInteger(chart_id, sl_txt, OBJPROP_COLOR, clrMagenta);
                                    ObjectSetInteger(chart_id, sl_txt, OBJPROP_FONTSIZE, 10);

                                    // Create and format the Stop Loss (SL) line
                                    sl_line = StringFormat("Sell SL Line %d", i);
                                    ObjectCreate(chart_id, sl_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], D, c_d_ll_t, D);
                                    ObjectSetInteger(chart_id, sl_line, OBJPROP_WIDTH, 3);
                                    ObjectSetInteger(chart_id, sl_line, OBJPROP_COLOR, clrMagenta);

                                    // Create and format the Take Profit (TP) text
                                    tp_txt = StringFormat("Sell TP %d", i);
                                    ObjectCreate(chart_id, tp_txt, OBJ_TEXT, 0, time[m + (LookbackBars / 2)], c_d_ll);
                                    ObjectSetString(chart_id, tp_txt, OBJPROP_TEXT, "TP");
                                    ObjectSetInteger(chart_id, tp_txt, OBJPROP_COLOR, clrMagenta);
                                    ObjectSetInteger(chart_id, tp_txt, OBJPROP_FONTSIZE, 10);

                                    // Create and format the Take Profit (TP) line
                                    tp_line = StringFormat("Sell TP Line %d", i);
                                    ObjectCreate(chart_id, tp_line, OBJ_TREND, 0, time[m + (LookbackBars / 2)], c_d_ll, c_d_ll_t, c_d_ll);
                                    ObjectSetInteger(chart_id, tp_line, OBJPROP_WIDTH, 3);
                                    ObjectSetInteger(chart_id, tp_line, OBJPROP_COLOR, clrMagenta);

                                    if(overlap == false)
                                      {
                                       i = m;
                                      }
                                    if(overlap == true)
                                      {
                                       i = i;
                                      }
                                    break;

                                   }

                                 break;

                                }
                             }

                           break;

                          }
                       }

                     break;

                    }
                 }

               break;

              }
           }

        }
     }
  }

Resultado:

Figura 15. Patrón bajista

Explicación:

Encontrar una oscilación alta en X, que significa el inicio del patrón, es el primer paso en el procedimiento de detección. Lo siguiente que busca es un mínimo oscilante (A), un máximo (B), un mínimo (C) y un máximo (D). Se garantiza una estructura de patrón legítima verificando cada punto según su posición relativa con respecto al anterior. La validación del patrón depende principalmente de los niveles de retroceso, especialmente los retrocesos de Fibonacci del 61,8 % y el 78,6 % desde X hasta A. Se encuentra una posible configuración de VENTA si se cumplen estos requisitos y D es mayor que X.

No se repetirán las explicaciones detalladas, ya que los patrones armónicos bajistas y alcistas son similares. La principal diferencia es que la forma alcista de este patrón predice una subida del precio, mientras que este patrón predice una bajada del precio. La orientación del patrón y la dirección de negociación asociada son las únicas diferencias. Por lo demás, las ideas fundamentales son las mismas.


Conclusión

En este artículo, hemos explorado cómo crear un indicador de patrones armónicos utilizando los objetos gráficos de MetaTrader 5. Hemos explicado la lógica que hay detrás de la detección de puntos clave de inflexión, la estructuración del patrón y su validación mediante los niveles de retroceso de Fibonacci. Mediante la implementación de patrones alcistas y bajistas, demostramos cómo identificar posibles oportunidades comerciales basadas en la evolución de los precios. Con este enfoque, puede visualizar las formaciones armónicas directamente en sus gráficos, lo que le permite tomar mejores decisiones sin depender de los indicadores tradicionales. Este método mejora la flexibilidad y proporciona una base sólida para una mayor personalización y perfeccionamiento de las estrategias de negociación basadas en patrones.

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

Israel Pelumi Abioye
Israel Pelumi Abioye | 1 abr 2025 en 12:03
Simon Simson #:
Muchas gracias por su buen trabajo en estas series MQL5 .
Hola Simon.
Gracias por sus amables palabras.
Oluwatosin Mary Babalola
Oluwatosin Mary Babalola | 1 abr 2025 en 12:15
Wow, este es el mejor hasta ahora desde que sigo tus artículos. Keep up the good work
Israel Pelumi Abioye
Israel Pelumi Abioye | 1 abr 2025 en 13:02
Oluwatosin Mary Babalola #:
Wow, este es el mejor hasta ahora desde que sigo tus artículos. Sigan así
Gracias.
Louai Habiche
Louai Habiche | 19 abr 2025 en 17:03

me gustaria saber si se puede hacer algo con los articulos.

¡podemos poner en alerta cuando el show sihnal !

Israel Pelumi Abioye
Israel Pelumi Abioye | 20 abr 2025 en 10:37
Louai Habiche #:

me gustaria saber si me pueden ayudar con el tema de los articulos.

¡podemos poner en alerta cuando el show sihnal !

Hola, Gracias por sus amables palabras. es posible hacerlo mediante el uso de "PlaySound()" función

Redes neuronales en el trading: Detección de anomalías en el dominio de la frecuencia (CATCH) Redes neuronales en el trading: Detección de anomalías en el dominio de la frecuencia (CATCH)
El framework CATCH combina la transformada de Fourier y el parcheo de frecuencias para detectar con precisión anomalías del mercado inaccesibles a los métodos tradicionales. En el presente artículo, analizaremos cómo este enfoque revela patrones ocultos en los datos financieros.
Explorando técnicas avanzadas de aprendizaje automático en la estrategia Darvas Box Breakout Explorando técnicas avanzadas de aprendizaje automático en la estrategia Darvas Box Breakout
La estrategia Darvas Box Breakout, creada por Nicolas Darvas, es un enfoque técnico de negociación que detecta posibles señales de compra cuando el precio de una acción sube por encima de un rango establecido, lo que sugiere un fuerte impulso alcista. En este artículo, aplicaremos este concepto estratégico como ejemplo para explorar tres técnicas avanzadas de aprendizaje automático. Entre ellas se incluyen el uso de un modelo de aprendizaje automático para generar señales en lugar de filtrar operaciones, el empleo de señales continuas en lugar de discretas y el uso de modelos entrenados en diferentes marcos temporales para confirmar las operaciones.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Algoritmo de optimización de neuroboides 2 — Neuroboids Optimization Algorithm 2 (NOA2) Algoritmo de optimización de neuroboides 2 — Neuroboids Optimization Algorithm 2 (NOA2)
El nuevo algoritmo de optimización de autor, NOA2 (Neuroboids Optimisation Algorithm 2), combina los principios de la inteligencia de enjambre con el control neuronal. El NOA2 combina la mecánica del comportamiento de los enjambres de neuroboids con un sistema neuronal adaptativo que permite a los agentes ajustar de forma autónoma su comportamiento a medida que buscan un óptimo. El algoritmo se está desarrollando activamente y muestra potencial para resolver problemas complejos de optimización.