Introducción a MQL5 (Parte 16): Creación de asesores expertos utilizando patrones técnicos de gráficos
Introducción
¡Bienvenidos de nuevo a la parte 16 de la serie Introducción a MQL5! Esta parte promete ser especialmente interesante, ya que continuaremos desarrollando todo lo que hemos aprendido hasta ahora, utilizando como siempre un enfoque basado en proyectos. Juntos, trabajaremos en un proyecto práctico que combina patrones de análisis técnico y codificación MQL5 para crear un Asesor Experto, lo que le ayudará a profundizar sus habilidades a través de la experiencia práctica.
Nos centraremos en el patrón «cabeza y hombros», un patrón técnico muy utilizado para identificar posibles cambios de tendencia. Nuestro proyecto se diseñará como un asesor experto capaz de reconocer automáticamente este patrón y ejecutar operaciones en consecuencia. Además, servirá como indicador para resaltar visualmente la formación de cabeza y hombros en el gráfico, lo que le facilitará detectar y comprender el patrón en situaciones reales de negociación.
En este artículo aprenderás:
- Automatización del trading con patrones gráficos
- Cómo identificar el patrón cabeza y hombros
- Dibujar puntos de giro mediante programación
- Uso de objetos gráficos en MQL5
- Definición de entrada, stop loss y take profit
- Evitar la repetición de señales
1. Comprender los patrones gráficos
Las formas visuales en los gráficos de precios que pueden utilizarse para predecir los movimientos futuros del mercado se denominan patrones gráficos. Estas tendencias, que suelen ser indicadores de si una tendencia es probable que continúe o se invierta, son el resultado de la lucha continua entre compradores y vendedores. Un patrón de cabeza y hombros, por ejemplo, generalmente señala una posible reversión de una tendencia ascendente a una tendencia descendente, mientras que un patrón de bandera durante una tendencia ascendente con frecuencia implica que es probable que la tendencia continúe. Su capacidad para detectar posibles oportunidades comerciales basadas en el comportamiento de precios anteriores mejorará si puede identificar patrones de gráficos como triángulos, rectángulos y techos dobles.
Analogía
Similares a huellas en la arena, los patrones gráficos son pistas obvias dejadas por el conflicto continuo entre compradores y vendedores. Los patrones gráficos le ayudan a comprender lo que ha sucedido en el mercado y predecir lo que podría suceder a continuación, de forma similar a como los rastros de animales pueden ayudar a un rastreador experimentado a determinar qué animal ha pasado y hacia dónde se dirige. Estos patrones gráficos en los gráficos de precios muestran cómo se comportan colectivamente los participantes del mercado y con frecuencia indican si es probable que la tendencia actual continúe o se revierta.
1.1. Categorías de patrones
Los patrones de gráficos generalmente se dividen en tres categorías principales:
1.1.1. Patrones de reversión
Los patrones gráficos conocidos como patrones de reversión se pueden utilizar para detectar posibles puntos de inflexión del mercado. Indican que una nueva tendencia puede estar comenzando en la dirección opuesta y que la tendencia existente puede estar terminando. Por ejemplo, un patrón de reversión puede sugerir que el precio se está preparando para entrar en una tendencia alcista si el mercado está en declive. De igual manera, estos patrones pueden indicar una posible reversión de la trayectoria ascendente del mercado. El patrón de cabeza y hombros y el doble suelo son patrones de reversión típicos.
Analogía
Los patrones de reversión funcionan como indicadores de un posible cambio en la dirección del mercado. Estos patrones sugieren que una tendencia, ya sea ascendente o descendente, puede estar decayendo y lista para revertirse, de forma muy similar a como las huellas que giran en la playa indican que alguien ha cambiado de dirección. Ayudan a los comerciantes a identificar posibles cambios en el movimiento de precios al señalar cuándo los compradores o vendedores están comenzando a ejercer poder.

1.1.2. Patrones de continuación
Los patrones gráficos conocidos como patrones de continuación indican cuándo es más probable que el mercado permanezca en su dirección actual. Por lo general, aparecen en momentos de consolidación o paradas cortas en el movimiento de precios antes de que la tendencia se reanude. Por ejemplo, un patrón de continuación en una tendencia alcista puede sugerir que el precio seguirá aumentando después de una pequeña caída lateral. En una tendencia bajista, implica que el precio probablemente continuará bajando después de la pausa. Los rectángulos y las banderas son patrones típicos de continuación.
Analogía
Al igual que las huellas, los patrones de continuación se detienen brevemente, pero siguen apuntando en la misma dirección. Imagínate siguiendo a una persona mientras pasea por la orilla. Siguiendo una línea continua de pasos, se observa que las huellas se detienen por un momento, posiblemente para atarse los cordones o echar un vistazo alrededor, pero luego reanudan su camino en la misma dirección. Esto indica que solo se trataba de una breve pausa y que nunca cambiaron de opinión. Del mismo modo, los patrones de continuación indican que el mercado se encuentra en una pausa temporal, pero que probablemente continuará su trayectoria actual. Estos patrones demuestran que el impulso no se ha revertido y solo se encuentra en un descanso momentáneo, independientemente de si el mercado se dirige al alza o a la baja.

1.1.3. Patrones neutrales
Los patrones gráficos que muestran un período de consolidación durante el cual el mercado puede romper en cualquier dirección se conocen como patrones neutrales. Sin que ninguno de los dos lados domine claramente, estos patrones muestran un equilibrio entre compradores y vendedores. Debido a esto, los traders se ven obligados a esperar una ruptura confirmada antes de actuar, y el precio se mueve dentro de un rango más estrecho. Si bien no pueden predecir la trayectoria de la ruptura, los patrones neutrales ayudan a los operadores a prepararse para un posible movimiento. Con frecuencia se observan patrones neutrales, como el Triángulo Simétrico.
Analogía
En el trading, los patrones neutrales son similares a observar a alguien dudando en una playa, sin estar seguro de su próximo movimiento. Los patrones neutrales, en los que compradores y vendedores están en igualdad de condiciones, reflejan la vacilación del mercado, de la misma manera que no se puede predecir su próximo movimiento a menos que se comprometan. Aunque no hay un sesgo obvio hacia arriba o hacia abajo, estos patrones indican que podría ocurrir un avance en cualquier dirección.

2. Configuración del proyecto
2.1. Cómo funciona el EA
El Asesor Experto (EA) de este proyecto está diseñado para identificar automáticamente el patrón de Cabeza y Hombros en el mercado y ejecutar operaciones basadas en esa estructura. Ya sea un cabeza y hombros estándar o un cabeza y hombros inverso, el EA identificará un patrón válido, elegirá el mejor punto de entrada y ejecutará una operación en la dirección de ruptura esperada.
Pero EA no se detiene ahí. Para que el patrón sea más visible y comprensible en el gráfico, también utilizaremos objetos gráficos para marcar el hombro izquierdo, la cabeza y el hombro derecho. Estos marcadores visuales ayudan al operador a confirmar el patrón y mejoran la claridad al revisar el gráfico o realizar pruebas retrospectivas de la estrategia. Este método ofrece una capa visible de confirmación, además de la automatización y el reconocimiento de patrones, lo que puede mejorar la depuración, el aprendizaje e incluso la supervisión en tiempo real.
2.1.1. Lógica para Comprar
Para activar una operación de compra, el EA identificará un patrón de cabeza y hombros inverso al detectar seis puntos de oscilación específicos en secuencia:
- Oscilación alta (X): Un máximo inicial, etiquetado como X.
- Oscilación baja (A): Una mínima más baja después de X, etiquetada como A.
- Oscilación alta (B): Un rebote que forma un máximo más bajo que X pero más alto que A, etiquetado como B.
- Oscilación baja (C): Una baja más profunda que A, etiquetada como C: esta es la cabeza.
- Oscilación alta (D): Una oscilación alta aproximadamente al mismo nivel que B, etiquetada como D, que forma parte de la línea del cuello.
- Oscilación baja (E): Un mínimo más alto en comparación con C, y aproximadamente al mismo nivel que A: este es el segundo hombro.
Una vez establecida esta estructura:
- El EA espera a que una vela cierre por encima del punto D (la línea del cuello).
- Cuando eso ocurra, se ejecutará una operación de compra.
- El Stop Loss (SL) se fijará en el mínimo del punto E.
- El Take Profit (TP) se fijará inicialmente en el punto X (el primer máximo del swing).
Sin embargo, si la distancia entre el punto de entrada y el TP (X) es inferior a 1 vez la distancia del stop loss, el EA ignorará X como objetivo y, en su lugar, establecerá un objetivo fijo de riesgo-recompensa 1:3 basado en la distancia SL. Esto garantiza que la estrategia mantenga una relación riesgo-recompensa mínima y evita realizar operaciones con baja recompensa que no compensan el riesgo.

2.1.1. Lógica para Vender
Para activar una operación de venta, el EA identificará un patrón estándar de cabeza y hombros detectando seis puntos de oscilación específicos en secuencia:
- Oscilación baja (X): Una baja inicial, etiquetada como X.
- Oscilación alta (A): Una oscilación alta más alta después de X, etiquetada como A.
- Oscilación baja (B): Un retroceso que forma un mínimo más alto que X pero más bajo que A, etiquetado como B.
- Oscilación alta (C): Una oscilación más alta que A, etiquetada como C: esta es la cabeza.
- Oscilación baja (D): Una oscilación baja aproximadamente al mismo nivel que B, etiquetada como D, que forma parte de la línea del cuello.
- Oscilación alta (E): Una oscilación alta inferior en comparación con C, y aproximadamente al mismo nivel que A: este es el segundo hombro.
Una vez confirmada esta estructura:
- El EA espera a que una vela cierre por debajo del punto D (la línea del cuello).
- Cuando eso ocurre, ejecuta una operación de venta.
- El Stop Loss (SL) se coloca en el punto más alto del punto E.
- El Take Profit (TP) se establece inicialmente en el punto X (el primer mínimo del swing).
Si la distancia desde la entrada hasta el TP (X) es inferior a una vez la distancia del stop-loss, el EA anula X como objetivo y aplica un objetivo fijo de riesgo-recompensa de 1:3 basado en el tamaño del SL.

Nota: Desarrollar sus habilidades en programación MQL5, especialmente en lo que respecta al uso de patrones gráficos y la creación de asesores expertos útiles, es el objetivo principal de la estrategia de trading que se explorará en este proyecto. No está destinado a ser utilizado con dinero real ni para operaciones bursátiles en tiempo real. Antes de implementar cualquier técnica en un mercado real, realice siempre pruebas exhaustivas y solicite asesoramiento a un experto financiero.
3. Identificación de los patrones de cabeza y hombros en el gráfico
A estas alturas, creo que ya tienes una idea clara de los patrones gráficos y sabes exactamente lo que queremos que haga nuestro Asesor Experto (EA). Incluso antes de que el EA realice una operación, es fundamental poder ver el patrón de cabeza y hombros en el gráfico. Esto le ayuda a identificar cualquier fallo de tiempo de ejecución o problema lógico en una fase temprana del proceso de prueba, además de confirmar que la lógica de EA coincide con la acción real del precio.
En este capítulo se explica cómo resaltar y validar manualmente la estructura de cabeza y hombros en el gráfico utilizando diversos componentes del gráfico, como líneas de tendencia, etiquetas de texto y formas. Más adelante en la EA, esto ayudará a crear una base sólida para automatizar la detección.
3.1. Recuperación de datos de velas japonesas
El primer paso es obtener datos de velas japonesas para localizar el patrón de cabeza y hombros en el gráfico. Contiene detalles sobre los precios de apertura, máximo, mínimo, hora y cierre de cada barra. Estos valores son cruciales porque la forma en que el precio oscila a lo largo de varias velas define la estructura del patrón. Sin embargo, no estamos recopilando esta información simplemente para identificar tendencias. También queremos que actúe como un indicador, ya que queremos crear un Asesor Experto que pueda reconocer estos patrones y realizar operaciones por sí mismo. Esto implica que debe ser capaz de llamar la atención sobre los patrones anteriores de cabeza y hombros en el gráfico para que pueda examinarlos y comprobarlos de nuevo.
En resumen, el EA que estamos desarrollando tendrá dos funciones: será un bot de trading que reconoce patrones de cabeza y hombros y ejecuta operaciones automáticamente, y también será un indicador de patrones que resalta estructuras similares en datos históricos. Además de permitir el trading automatizado, esta doble funcionalidad permite a los traders verificar visualmente las señales y examinar cómo se ha desarrollado y comportado el patrón en el pasado.
Ejemplo:input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points // Variable to store how many bars are available on the chart for the selected timeframe int rates_total; double open[]; // Array for opening prices double close[]; // Array for closing prices double low[]; // Array for lowest prices double high[]; // Array for highest prices datetime time[]; // Array for time (timestamps) of each bar //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); }
Explicación:
Hay dos variables de entrada definidas por el usuario al inicio de la función. El primero, el marco temporal, permite al usuario elegir el periodo de tiempo exacto que utilizará el Asesor Experto (EA). PERIOD_CURRENT es la configuración predeterminada. Por lo tanto, el EA utilizará el mismo marco temporal que el gráfico al que está vinculado. Gracias a esta flexibilidad, puede examinar varios periodos de tiempo sin cambiar el código. El EA recibe instrucciones del segundo parámetro, bars_check, sobre cuántas velas históricas (o barras) debe examinar al analizar el comportamiento de los precios. El EA buscará posibles estructuras de patrones en las últimas 1000 velas, ya que en este caso está configurado en 1000.
El código declara algunas matrices y una variable para almacenar datos de mercado después de las definiciones de entrada. El número total de barras (velas) disponibles para el símbolo y el intervalo de tiempo seleccionados se almacenará en la variable rates_total. Los datos de precios relacionados con cada vela se almacenan en matrices como open[], close[], low[] y high[]. También podemos determinar la hora exacta de cada vela utilizando las marcas de tiempo de cada barra, que se almacenan en la matriz time[]. Estas matrices son cruciales porque proporcionan al EA la información que necesita para examinar el gráfico y encontrar patrones como el de cabeza y hombros.
Este proyecto nos obliga a replicar manualmente los datos de las velas utilizando funciones como CopyOpen(), CopyHigh() y otras, ya que está diseñado como un Asesor Experto (EA) en lugar de como un indicador personalizado. Esta información ya se proporcionaría automáticamente a través de los parámetros de la función si se tratara de un indicador que utilizara el método OnCalculate(), lo que nos ahorraría la molestia de copiarla.
Tres funciones MQL5 comunes: OnInit(), OnDeinit() y OnTick(), también se incluyen en la estructura del EA. Cuando se carga el EA, se llama al método OnInit() una vez. Simplemente devuelve INIT_SUCCEEDED, lo que indica que el EA está preparado para la ejecución. La función OnDeinit(), que aún no tiene lógica de limpieza, se llama cuando la terminal se apaga o se elimina el EA.
La función OnTick(), que se ejecuta cada vez que se produce una nueva actualización de precio (tick), es donde tiene lugar la actividad real. La función Bars() determina inicialmente la cantidad de barras que están actualmente accesibles en el gráfico dentro de esta función. Luego, los datos de precio y tiempo se cargan en las matrices correspondientes utilizando cinco funciones de copia: los precios de apertura se completan en la matriz open[] mediante CopyOpen(), los precios de cierre se completan en la matriz close[] mediante CopyClose(), los precios más bajos y más altos se almacenan mediante CopyLow() y CopyHigh(), respectivamente, y la marca de tiempo de cada vela se almacena mediante CopyTime(). Debido a que prepara todos los datos históricos que el EA utilizará para buscar patrones de gráficos y examinar la actividad del mercado, esta configuración es esencial.
3.2. Identificando los cambios
Una vez que hayamos recopilado con éxito los datos históricos de las velas japonesas, el siguiente paso es encontrar los puntos de inflexión importantes (X, A, B, C, D y E) que componen la estructura del patrón de cabeza y hombros o cabeza y hombros invertidos. El EA debe identificar con precisión estas oscilaciones para determinar los patrones gráficos legítimos, que son los puntos de inflexión en el movimiento de los precios. El EA le ayudará a trazar el patrón paso a paso identificando las principales reversiones mediante el examen de los máximos y mínimos a lo largo del número de barras elegido.
Ejemplo://+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; // If the current high is not the highest, return false. } return true; }
Explicación:
Utilizamos dos funciones, IsSwingLow() e IsSwingHigh(), para determinar los puntos de oscilación del precio. Estas características determinan si una vela determinada, en relación con las velas cercanas, crea un swing alto o un swing bajo. Según el valor retrospectivo, la función garantiza que el mínimo de la vela actual sea inferior a los mínimos de un número predeterminado de velas anteriores y posteriores en el caso de un swing bajo. Del mismo modo, para un swing alto, verifica que el máximo de la vela actual sea mayor que los máximos de las velas circundantes. Dado que esta idea ya se ha tratado con gran detalle en la parte 14 de esta serie, no entraremos en demasiados detalles aquí.
3.2.1. Identificación de XABCDE
En el ensayo se enfatiza la importancia de determinar con precisión los puntos de oscilación primarios (X, A, B, C y D) para reconocer el patrón de cabeza y hombros tanto en configuraciones de compra como de venta. Estos puntos representan máximos y mínimos significativos que influyen en el patrón y dirigen las acciones comerciales del asesor experto.
Ejemplo:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- // Get the total number of bars available on the selected symbol and timeframe rates_total = Bars(_Symbol, timeframe); // Copy the open prices of the last 'rates_total' bars into the 'open' array CopyOpen(_Symbol, timeframe, 0, rates_total, open); // Copy the close prices of the last 'rates_total' bars into the 'close' array CopyClose(_Symbol, timeframe, 0, rates_total, close); // Copy the low prices of the last 'rates_total' bars into the 'low' array CopyLow(_Symbol, timeframe, 0, rates_total, low); // Copy the high prices of the last 'rates_total' bars into the 'high' array CopyHigh(_Symbol, timeframe, 0, rates_total, high); // Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array CopyTime(_Symbol, timeframe, 0, rates_total, time); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. break; } } break; } } break; } } break; } } break; } } } } } } } }Explicación:
//X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). 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_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_letter; // Unique name for the text label object. //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object.
En este código se utilizan tres tipos diferentes de variables para representar cada uno de los puntos de oscilación principales del patrón, que son X, A, B, C, D y E. El nivel de precio exacto de ese punto de oscilación se almacena primero en una variable doble. Doble A: mantiene el precio del swing alto con la etiqueta A, mientras que doble X mantiene el precio del swing bajo con la etiqueta X. Cada punto de swing utiliza una variable de fecha y hora para registrar la hora en que se produjo, además del precio. Esto permite al EA organizar con precisión los puntos de oscilación en secuencia cronológica en el gráfico. Fecha y hora X_time; Por ejemplo, registra la hora del swing bajo X, y fecha y hora A_time; registra la hora del swing alto A.
Por último, se genera un nombre de etiqueta distinto para cada punto de giro utilizando una variable de cadena. Para indicar visualmente la ubicación de cada punto de giro, estas etiquetas (como X_letter o A_letter) se utilizan para construir objetos de texto en el gráfico. El EA es más capaz de organizar y mostrar estos puntos para que los operadores puedan observar el patrón que se está desarrollando gracias a este sistema de etiquetado. El precio, el tiempo y la etiqueta son los tres datos que utiliza el EA para organizar cada punto de oscilación, de modo que pueda identificar correctamente y representar gráficamente el patrón de cabeza y hombros en el gráfico. La identificación de patrones y la colocación de señales visuales fácilmente interpretables para los operadores dependen de esta técnica metódica.
if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing low (B). E_time = time[n]; // Time of the swing low (B). E_letter = StringFormat("E%d", n); // Unique name for the text label object. ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicación:
Para determinar si el programa está ahora configurado para detectar patrones de venta, el código primero determina si la variable show_sell es true. Cuando no es necesario detectar patrones de venta, esta sencilla puerta condicional evita cálculos innecesarios. A continuación, comprueba si el número total de barras del gráfico (rates_total) supera o es igual al umbral mínimo bars_check. Esto garantiza que haya suficientes datos previos para un reconocimiento preciso de patrones.
Normalmente utilizada como tamaño de ventana o tiempo de retrospectiva para la detección de puntos de giro, la variable z se itera sobre un rango limitado de valores en el bucle más externo. La técnica tiene como objetivo mejorar la flexibilidad y la precisión de la detección de oscilaciones mediante la experimentación con múltiples períodos de retrospectiva, operando entre z = 7 y z = 10. Este bucle permite al algoritmo buscar patrones con resoluciones o sensibilidades ligeramente diferentes.
Desde rates_total - bars_check hasta rates_total - z, el bucle for posterior itera a través de las barras más recientes del gráfico. Este rango se centra en los patrones que pueden ser más relevantes para la acción actual del precio, limitando la región de búsqueda a las barras más recientes. La función IsSwingLow(low, i, z) se utiliza en este bucle para buscar un mínimo oscilante y determinar si la barra en la posición i es un mínimo local basándose en la ventana retrospectiva z.
Cuando se detecta un mínimo oscilante en la posición i, el código utiliza StringFormat(«X%d», i) para generar una cadena de etiqueta única denominada X_letter, almacena su precio en la variable X y su marca de tiempo en X_time. El EA puede resaltar visualmente este punto en el gráfico con mayor facilidad e identificarlo de forma única para su procesamiento posterior gracias a este etiquetado. El patrón Cabeza y Hombros comienza con este punto X como referencia.
Para determinar el siguiente máximo A que se produce después del tiempo X, el código inicia otro bucle anidado, comenzando en i. Para mantener la secuencia cronológica requerida para el patrón, utiliza IsSwingHigh(high, j, z) para determinar si la barra en j es un swing alto y se asegura de que su tiempo time[j] sea estrictamente mayor que X_time. El precio, el tiempo y la etiqueta para A se documentan de manera similar a X si se descubre un swing alto legítimo.
Los siguientes puntos, B, C, D y E, se identifican secuencialmente mediante esta estructura de bucles anidados. Al iterar a través de las barras, se localiza cada punto de oscilación, verificando que se trata de un mínimo o máximo legítimo y que su marca de tiempo es estrictamente mayor que la del punto anterior. El código crea una cadena de etiqueta distinta y guarda el precio y la duración de cada oscilación confirmada. Los puntos del patrón se mantienen en la secuencia adecuada gracias a esta rigurosa comprobación secuencial.
El bucle más interno utiliza una ventana retrospectiva algo más estrecha (z/2) para buscar el punto E, el último máximo del patrón. El algoritmo puede ajustar la sensibilidad de detección del último punto gracias a esta discrepancia. Cuando se localiza E, el código sale instantáneamente del bucle para evitar búsquedas adicionales innecesarias, lo que aumenta la eficiencia. A continuación, asigna su precio, tiempo y etiqueta.
El patrón Cabeza y Hombros se detecta con precisión en el orden correcto de los puntos de oscilación a lo largo de este proceso gracias a los bucles anidados y las comprobaciones de tiempo. El uso de etiquetas distintas como «X%d» o «A%d» permite al EA generar y controlar elementos de texto o gráficos en el gráfico, lo que ofrece a los operadores una representación visual del patrón. Gracias a esta metodología metódica y estructurada, el software puede identificar de forma fiable patrones complejos de comportamiento de los precios para utilizarlos en las decisiones de trading.
El objetivo del ejemplo de código era identificar los seis puntos de inflexión cruciales (X, A, B, C, D y E) que son necesarios para identificar un patrón de cabeza y hombros. Sin embargo, la implementación actual aún no aplica las restricciones lógicas precisas necesarias para validar la estructura de cabeza y hombros; en su lugar, solo determina estos puntos de oscilación basándose en las oscilaciones de precios y su orden cronológico. El algoritmo recopila los precios, las horas y las etiquetas de estos puntos, pero no verifica si realmente conforman el patrón distintivo.
Se utilizarán varios criterios importantes basados en la estructura de precios para verificar la validez de un patrón de venta de cabeza y hombros. A debe ser mayor que X, B debe estar en el medio (mayor que X pero menor que A) y C, la cabeza, debe ser mayor que B. El pico del hombro derecho está formado por el punto E alineado con el punto A, mientras que el valle del hombro derecho está representado por el punto D, que está cerca del punto B. Además, antes de enviar cualquier señal de operación, se debe verificar la estructura de un patrón concreto observando una serie concreta de máximos y mínimos entre estos puntos.
Ejemplo:
input color txt_clr = clrBlue; // Texts color //X double X; // Price of the swing low (X). datetime X_time; // Time of the swing low (X). string X_letter; // Unique name for the text label object. int x_a_bars; int x_lowest_index; double x_a_ll; datetime x_a_ll_t; //A double A; // Price of the swing high (A). datetime A_time; // Time of the swing high (A). string A_letter; // Unique name for the text label object. int a_b_bars; int a_highest_index; double a_b_hh; datetime a_b_hh_t; string A_zone; double A_low; //B double B; // Price of the swing low (B). datetime B_time; // Time of the swing low (B). string B_letter; // Unique name for the text label object. int b_c_bars; int b_lowest_index; double b_c_ll; datetime b_c_ll_t; string B_zone; double B_high; //C double C; // Price of the swing low (B). datetime C_time; // Time of the swing low (B). string C_letter; // Unique name for the text label object. int c_d_bars; int c_highest_index; double c_d_hh; datetime c_d_hh_t; //D double D; // Price of the swing low (B). datetime D_time; // Time of the swing low (B). string D_letter; // Unique name for the text label object. int d_e_bars; int d_lowest_index; double d_e_ll; datetime d_e_ll_t; double D_3bar_high; //E double E; // Price of the swing low (B). datetime E_time; // Time of the swing low (B). string E_letter; // Unique name for the text label object. double E_3bar_low; string xa; // Unique name for the trendline for XA. string ab; // Unique name for the trendline for AB. string bc; // Unique name for the trendline for BC. string cd; // Unique name for the trendline for CD. string de; // Unique name for the trendline for DE. string ex; // Unique name for the trendline for EX. //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); } break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicación:
El código contiene declaraciones de variables que se utilizan para almacenar información importante sobre la estructura y los componentes de un patrón XABCDE personalizado en la acción del precio. Cada variable desempeña un papel específico en la identificación y visualización de los máximos y mínimos que conforman el patrón, así como en el etiquetado y dibujo de la estructura en el gráfico. El primer grupo de variables está relacionado con el segmento desde el punto X hasta A. x_a_bars contiene el número de barras entre estos dos puntos. x_lowest_index almacena el índice de la barra de precio más bajo en ese segmento, mientras que x_a_ll guarda el precio más bajo real (el «mínimo más bajo») y x_a_ll_t guarda la hora correspondiente en la que se produjo este mínimo.
El tramo de A a B lo gestiona el siguiente grupo. El número de barras en esta sección es a_b_bars. El índice de la barra más alta se identifica mediante a_highest_index, mientras que el precio más alto real, o «máximo superior», se almacena en a_b_hh, y a_b_hh_t registra el momento en que se produjo. Esto respalda la idea de que el punto B es un mínimo más alto y el punto A es un máximo oscilante. Las variables b_c_bars, b_lowest_index, b_c_ll y b_c_ll_t registran entonces el recuento de barras, el índice más bajo, el precio más bajo y el momento en que se alcanzó ese precio, respectivamente, para el segmento comprendido entre B y C. Esto sirve para validar el mínimo más bajo posterior de la estructura.
Las variables de cadena A_zone y B_zone probablemente se utilizan para almacenar nombres de objetos distintos para crear zonas rectangulares cercanas a las ubicaciones A y B. Los niveles de precios A_low y B_high, que sirven como señales visuales o áreas de toma de decisiones en el gráfico, delimitan los límites inferior y superior de estas zonas. De forma similar a las variables anteriores, d_e_bars, d_lowest_index, d_e_ll y d_e_ll_t almacenan datos sobre el recuento de barras, el índice, el precio y el tiempo para el segmento de D a E, lo que ayuda a localizar el último tramo del patrón.
D_3bar_high y E_3bar_low se utilizan para almacenar el punto cercano más alto D y el punto cercano más bajo E, que normalmente se calculan a partir de estructuras de 3 barras. Esto ayuda a verificar la autenticidad de los puntos de inflexión y reduce los falsos positivos. Por último, las identificaciones de cadena para las líneas de tendencia que se crearán entre cada uno de los puntos de inflexión del patrón —de X a A, de A a B, y así sucesivamente— son las variables xa, ab, bc, cd, de y ex. Estas cadenas garantizan que cada objeto de línea de tendencia tenga un nombre distinto, lo que permite una representación gráfica precisa y armoniosa de todo el patrón en el gráfico.
Examen del movimiento de precios entre los seis puntos cruciales, indicados con las letras X, A, B, C, D y E. Se plantea la hipótesis de que estos puntos corresponden a máximos y mínimos oscilantes que crean relaciones estructurales particulares. Encontrar el mínimo más bajo (d_e_ll) entre los puntos D y E es el primer paso del script. A continuación, obtiene la hora y un máximo de referencia (D_3bar_high) que se encuentra tres barras antes de ese mínimo. Del mismo modo, encuentra el máximo más alto (c_d_hh) entre los puntos C y este d_e_ll. A continuación, recopila todos los máximos y mínimos pertinentes junto con sus marcas de tiempo correspondientes y repite el proceso hacia atrás en el tiempo hasta el punto X. De esta manera, el código utiliza el análisis de barras para construir toda la estructura de oscilación X-A-B-C-D-E.
Entre dos marcas de tiempo, el número de velas se cuenta mediante la función Bars(). Mediante el uso de ArrayMaximum() y ArrayMinimum(), ayuda a separar rangos concretos entre los puntos de oscilación, de modo que el código pueda examinar los máximos y mínimos dentro de ellos. Para localizar el máximo más alto o el mínimo más bajo, estas funciones escanean un número específico de barras desde un desplazamiento dado (i, j, k, etc.). Esto ayuda a localizar los puntos de giro. ArrayMaximum(high, l, c_d_bars), por ejemplo, encuentra el máximo más alto entre C y D, lo que da como resultado el punto C. El punto X se alcanza repitiendo el razonamiento a lo largo de toda la estructura.
La relación estructural entre los puntos se verifica evaluando un conjunto de condiciones. Estos criterios comparan los máximos y mínimos de los segmentos; por ejemplo, determinan si el punto A es más alto que el punto X, si el punto B es más bajo que A, pero aún más alto que X, si el punto C es más alto que A, y así sucesivamente. Antes de trazar la evolución del precio, esto confirma si realmente forma el patrón previsto. E > A_low y E_3bar_low < a_b_hh son ejemplos de comparaciones que garantizan que la pata E se encuentra en una posición legítima en relación con el resto de la estructura.
El método utiliza ObjectCreate() para designar gráficamente los puntos reconocidos en el gráfico con etiquetas de texto («X», «A», «B», etc.) y líneas de tendencia que los conectan una vez que el patrón cumple todos los requisitos. Estas líneas de tendencia utilizan objetos OBJ_TREND para dibujar los segmentos XA, AB, BC, CD, DE y EX del patrón, y objetos OBJ_TEXT para indicar los puntos. La legibilidad se mejora mediante colores, grosor de línea y capas visuales (a través de OBJPROP_BACK).
Por último, el código resalta con rectángulos las zonas A y B, dos zonas de precios cruciales. La zona A se extiende desde el máximo del punto A hasta el mínimo del punto E, mientras que la zona B se extiende desde el mínimo del punto B hasta el máximo del punto D. A la hora de tomar decisiones de trading, como entradas, stops u objetivos, estos rectángulos se utilizan probablemente como referencia visual para identificar reacciones o confluencias dentro de determinadas regiones. Los operadores pueden comprender más fácilmente los patrones complejos que el programa encuentra automáticamente gracias a esta pantalla.
3.2.2. Resaltar la estructura del patrón con formas triangulares
Después de localizar y etiquetar todos los puntos de oscilación importantes, que son X, A, B, C, D y E, lo siguiente es dibujar formas triangulares para resaltar la estructura del patrón, especialmente cuando surgen formaciones como «cabeza y hombros». Estos triángulos indicarán los picos y valles que caracterizan la «cabeza» y los «hombros» de cada patrón, en lugar de seguir las etapas completas del precio. Un patrón de cabeza y hombros en el que la línea no cae por debajo de la línea del cuello se representa visualmente mediante el triángulo XAB, que une X, A y B.
El triángulo BCD, que une B, C y D y enfatiza la segunda onda de la estructura, es el siguiente paso en este método. El triángulo DEX, que completa visualmente la forma, conecta D, E y X. Al servir como ayuda visual, estas formas triangulares permiten a los operadores identificar más rápidamente los puntos de inflexión cruciales y la geometría de los patrones sin saturar el gráfico con líneas.
Ejemplo:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // MA Time Frame input int bars_check = 1000; // Number of bars to check for swing points input bool show_sell = true; // Display sell signals input bool show_buy = true; // Display buy signals input color txt_clr = clrBlue; // Texts color input color head_clr = clrCornflowerBlue; // Head color input color shoulder_clr = clrLightSeaGreen; // Shoulder color
if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); 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_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); }
Resultado:

Explicación:
Esta sección del código se centra en el uso de triángulos y rectángulos para representar gráficamente estructuras de patrones, en particular XAB, BCD y DEX, en el gráfico de MetaTrader 5. Para mantener nombres dinámicos para los objetos triangulares que se producirán, se declaran tres variables de cadena: X_A_B, B_C_D y D_E_X. Las coordenadas temporales específicas se recuperan utilizando dos variables de fecha y hora (xa_line_t y ex_line_t) para fijar partes de las formas triangulares en las ubicaciones adecuadas del gráfico.
Para facilitar la comprensión del operador, el código comienza estableciendo dos áreas rectangulares denominadas «ZONA A» y «ZONA B». Estos rectángulos, que resaltan áreas significativas de la estructura de precios, se crean utilizando ObjectCreate(). La zona A se extiende hasta el punto E, desde el máximo entre los puntos A y B (a_b_hh) hasta el mínimo en el punto A (A_low). De manera similar, la zona B se extiende desde la posición más baja (b_c_ll) entre los puntos B y C hasta el punto más alto (B_high), concluyendo en la coordenada temporal del tramo D a E.
La siguiente sección llama a ObjectGetTimeByValue() para determinar las coordenadas temporales necesarias para anclar partes de los triángulos XAB y DEX. Esta función garantiza que los marcadores visuales coincidan adecuadamente con los niveles de precios que pretenden resaltar, buscando un valor de precio concreto a lo largo del recorrido de un objeto y devolviendo el tiempo correspondiente.
A continuación, cada triángulo se representa como una pata de patrón independiente. Aunque no conecta toda la pierna, el triángulo XAB indica la primera estructura de oscilación. Para evitar el desorden visual y concentrarse en la estructura, solo llama la atención sobre los puntos de inflexión importantes que se asemejan a hombros. De manera similar, el triángulo BCD, que une un punto bajo (B), un pico (C) y un retroceso (D), enfatiza la cabeza principal del patrón. El triángulo DEX, que representa el movimiento de retorno que refleja el hombro original, completa finalmente el marco.
Propiedades como OBJPROP_FILL y OBJPROP_BACK se utilizan para rellenar los tres triángulos con color y colocarlos detrás de otros elementos del gráfico. Los operadores pueden reconocer más fácilmente el patrón de un vistazo gracias a los colores empleados, que se guardan en variables como shoulder_clr y head_clr. Para manejar y reconocer muchos casos de estos patrones en el mismo gráfico, se incluye el índice i en el nombre de cada elemento.
3.2.3. Especificar el punto de entrada, el stop loss y el take profit
La siguiente etapa consiste en especificar los parámetros de la operación, que incluyen el punto de entrada, el stop loss (SL) y el take profit (TP), después de haber marcado con triángulos las estructuras XAB, BCD y DEX, enfatizando la forma de cabeza y hombros. Estos componentes son necesarios para transformar el patrón reconocido en una estrategia comercial integral. Cuando una vela cierra por debajo del punto D, se realiza la entrada. Esto demuestra que el precio puede haber rechazado la zona del cuello y se está moviendo en la dirección prevista por el patrón. Ahora se considera que el comercio es legítimo y que su ejecución es posible.
Justo por encima del punto E, se establece el stop loss. Colocar el SL aquí ayuda a proteger la operación si el patrón falla y el precio se revierte inesperadamente, ya que E es el máximo más reciente antes de que se complete el patrón. Además, mantiene la configuración razonable al invalidar la operación solo cuando se rompe el patrón. Dado que el punto X, el origen del patrón, es un nivel de referencia fiable en el que el precio se ha revertido anteriormente, el take profit se coloca originalmente allí. Sin embargo, el TP se amplía aún más hasta alcanzar un mínimo de 1:2, si la distancia desde la entrada hasta X no arroja una relación riesgo-recompensa (RRR) de al menos 1:1. Un elemento clave de cualquier estrategia comercial sostenible es garantizar que la operación tenga un potencial de rendimiento favorable en relación con el riesgo asumido.
Ejemplo:int n_bars; int n_bars_2; string sl_t; string tp_t; double sl_price; double tp_price;
for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 2)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } } } break; } }
Resultado:

Explicación:
La lógica en esta área del código se centra en determinar una configuración comercial legítima utilizando la estructura de cabeza y hombros que se destacó anteriormente. A continuación, se utilizan objetos gráficos para indicar gráficamente la posición de entrada, el stop loss (SL) y el take profit (TP) en el gráfico. El número de velas entre puntos significativos se calcula y se compara utilizando dos variables enteras, n_bars y n_bars_2. Esto ayuda a determinar si la acción del precio actual sigue estando dentro del margen aceptable de la configuración. Mientras que sl_price y tp_price almacenan sus respectivos niveles de precios, las cadenas sl_t y tp_t se utilizan para nombrar dinámicamente los objetos de texto SL y TP.
A partir del punto de finalización del patrón (n), la lógica principal comienza en un bucle que recorre las barras. Se busca una vela que cierre por debajo del nivel del cuello (punto D), lo que indica una posible entrada, mediante la condición if (close[o] < d_e_ll && time[o] >= time[n+(z/2)]). A continuación, se determina el número de barras entre los puntos X y E (n_bars) y entre el punto medio del patrón y la vela actual (n_bars_2) si se cumple este requisito. De esta manera, se garantiza que la operación sea válida dentro de un intervalo razonable.
A continuación, el código compara el precio de entrada con los puntos E y X, respectivamente, para determinar el importe del posible stop loss (sl_zone) y el take profit (tp_zone). Para descartar esta entrada como la ruptura inicial, además realiza un bucle para asegurarse de que ninguna vela anterior haya cerrado por debajo del punto D antes de la actual. El script continúa si esta comprobación tiene éxito.
Para representar visualmente las zonas de stop loss y take profit en el gráfico, el código utiliza OBJ_RECTANGLE para generar rectángulos gráficos si la recompensa potencial (TP) es mayor o igual que el riesgo (SL). Además, hay una flecha de venta (OBJ_ARROW_SELL) cerca del punto de entrada. Las variables sl_price y tp_price contienen los valores reales de los precios SL y TP. Las etiquetas se crean con OBJ_TEXT para mostrar de forma destacada estos niveles en el gráfico utilizando colores y tipos de letra seleccionados. Para proporcionar una RRR más favorable de al menos 1:2, el script se ajusta calculando una nueva zona TP que es tres veces la distancia SL si la relación riesgo-recompensa es inferior a 1:1 (es decir, TP es inferior a SL). A continuación, para esta configuración alternativa, se crean y modifican los objetos gráficos idénticos.
4. Ejecución de operaciones basadas en el patrón
Nuestro objetivo en esta parte es ir más allá del simple reconocimiento y exposición de situaciones comerciales. Esta sección trata sobre cómo llevar a cabo la operación basándose en el patrón identificado, mientras que la parte anterior se centraba en marcar los niveles de entrada, stop loss (SL) y take profit (TP) en el gráfico. El EA debería iniciar una operación automáticamente cuando se cumplan los requisitos, como cuando el precio cierre por debajo del punto D para una operación de venta. El take-profit debe fijarse en el punto X o modificarse para obtener una relación riesgo-recompensa de al menos 1:2, y el stop loss debe fijarse en el punto E. Al dar este paso, el EA pasa de ser una herramienta visual a convertirse en un sistema completamente automatizado que puede introducir y gestionar operaciones sin necesidad de intervención humana.
Ejemplo:
#include <Trade/Trade.mqh> CTrade trade; int MagicNumber = 5122025; datetime lastTradeBarTime = 0; double ask_price; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
ask_price = ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); datetime currentBarTime = iTime(_Symbol, timeframe, 0); //FOR SELL if(show_sell) { if(rates_total >= bars_check) { for(int z = 7; z <= 10; z++) { for(int i = rates_total - bars_check; i < rates_total - z; i++) { if(IsSwingLow(low, i, z)) { // 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_letter = StringFormat("X%d", i); // Unique name for the text label object. for(int j = i; j < rates_total - z; j++) { if(IsSwingHigh(high, j, z) && time[j] > X_time) { A = high[j]; // Price of the swing high (A). A_time = time[j]; // Time of the swing high (A) A_letter = StringFormat("A%d", j); // Unique name for the text label object for(int k = j; k < rates_total - z; k++) { if(IsSwingLow(low, k, z) && time[k] > A_time) { B = low[k]; // Price of the swing low (B). B_time = time[k]; // Time of the swing low (B). B_letter = StringFormat("B%d", k); // Unique name for the text label object. for(int l = k ; l < rates_total - z; l++) { if(IsSwingHigh(high, l, z) && time[l] > B_time) { C = high[l]; // Price of the swing high (C). C_time = time[l]; // Time of the swing high (C). C_letter = StringFormat("C%d", l); // Unique name for the text label object. for(int m = l; m < rates_total - z; m++) { if(IsSwingLow(low, m, z) && time[m] > C_time) { D = low[m]; // Price of the swing low (D). D_time = time[m]; // Time of the swing low (D). D_letter = StringFormat("D%d", m); // Unique name for the text label object. for(int n = m ; n < rates_total - (z/2) - 1; n++) { if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time) { E = high[n]; // Price of the swing high (E). E_time = time[n]; // Time of the swing high (E). E_letter = StringFormat("E%d", n); // Unique name for the text label object. d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point) d_e_ll_t = time[d_lowest_index]; // Store the corresponding time D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range c_d_hh = high[c_highest_index]; // Store the lowest high (C - D lowest point) c_d_hh_t = time[c_highest_index]; // Store the corresponding time b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point) b_c_ll_t = time[b_lowest_index]; // Store the corresponding time B_high = high[b_lowest_index]; // The high price of the bar that formed swing low D a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point) a_b_hh_t = time[a_highest_index]; // Store the corresponding time A_low = low[a_highest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point) x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh) { ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); xa = StringFormat("XA line%d", i); ab = StringFormat("AB line%d", i); bc = StringFormat("BC line%d", i); cd = StringFormat("CD line%d", i); de = StringFormat("DE line%d", i); ex = StringFormat("EX line%d", i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E); ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E"); ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh); ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true); ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true); ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh); ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true); ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll); ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true); ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E); ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, de, OBJPROP_BACK, true); ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll); ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3); ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown); ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true); A_zone = StringFormat("A ZONEe%d", i); B_zone = StringFormat("B ZONEe%d", i); ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low); ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high); xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0); ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0); X_A_B = StringFormat("XAB %d", i); ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll); ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true); ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr); 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_e_ll_t, d_e_ll); ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr); ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true); ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true); D_E_X = StringFormat("DEX %d", i); ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll); ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr); ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true); ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true); for(int o = n; o < rates_total - 1; o++) { if(close[o] < d_e_ll && time[o] >= time[n+(z/2)]) { n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time); n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]); if(n_bars_2 <= n_bars) { double sl_zone = MathAbs(E - close[o]); double tp_zone = MathAbs(close[o] - x_a_ll); bool no_cross = false; for(int p = n + (z/2); p < o; p++) { if(close[p] < d_e_ll) { no_cross = true; break; } } if(no_cross == false) { if(tp_zone >= sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = x_a_ll; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price); ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(tp_zone < sl_zone) { string loss_zone = StringFormat("Loss %d", i); ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]); ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr); string sell_object = StringFormat("Sell Object%d", i); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]); double n_tp = MathAbs(close[o] - (sl_zone * 3)); string win_zone = StringFormat("Win %d", i); ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp); ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true); ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr); sl_price = E; tp_price = n_tp; string sl_d_s = DoubleToString(sl_price,_Digits); string tp_d_s = DoubleToString(tp_price,_Digits); sl_t = StringFormat("sl %d", i); tp_t = StringFormat("tp %d", i); ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);; ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s); ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr); ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price); ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s); ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8); ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr); } if(time[o] == time[rates_total-2] && currentBarTime != lastTradeBarTime) { trade.Sell(lot_size,_Symbol,ask_price, sl_price,tp_price); lastTradeBarTime = currentBarTime; } } } break; } } } break; } } break; } } break; } } break; } } break; } } } } } } }
Resultado:

Explicación:
Este bloque de código gestiona la ejecución de operaciones utilizando la clase CTrade integrada en MQL5, que agiliza las tareas de negociación, incluyendo la colocación, edición y cierre de órdenes. La biblioteca de negociación, que proporciona acceso a la clase CTrade, se incluye en la parte superior utilizando #include <Trade/Trade.mqh>. A continuación, se crea una instancia de esta clase utilizando CTrade trade;, y se utiliza int MagicNumber = 5122025; para definir una identificación única para las operaciones realizadas por este Asesor Experto (EA). El MagicNumber es crucial, ya que ayuda a diferenciar las operaciones realizadas por este EA de las realizadas manualmente o por otros EA. CTrade trade; se utiliza para establecer su MagicNumber; SetExpertMagicNumber;.
SymbolInfoDouble(_Symbol, SYMBOL_ASK); se utiliza para inicializar la variable ask_price con el precio de venta actual del símbolo. A este precio se ejecutaría una orden de venta. Luego, el código utiliza time[o] == time[rates_total-2] para determinar si la barra actual es la más reciente. Además, se utiliza la condición currentBarTime!= lastTradeBarTime para confirmar que no se ha realizado ninguna operación en la barra actual. El EA no puede realizar más de un pedido en la misma barra gracias a esta comprobación.
El EA utiliza la transacción para emitir una orden de venta si se cumplen ambos requisitos. Función Sell(). Ask_price es el precio de entrada, sl_price es el nivel de stop loss, tp_price es el nivel de take profit, lot_size es la cantidad de lotes a negociar y _Symbol es el símbolo comercial actual (como EURUSD). El EA agrega el valor de currentBarTime a la variable lastTradeBarTime después de que se realiza exitosamente la transacción. Esta actualización es esencial porque garantiza que solo se realice una operación por cada indicación de patrón legítimo al evitar que el EA realice numerosas transacciones basadas en la misma barra.
Es fundamental recordar que el mismo razonamiento que se utiliza para identificar y mostrar una configuración de venta también se puede utilizar para identificar y mostrar una configuración de compra simplemente realizando lo inverso en cada etapa. La estrategia de compra sería esperar a que una vela cierre por encima de la línea del cuello (punto D) en lugar de por debajo de ella. Después de esto, el take profit se posicionaría en el punto más alto (X), y el stop loss en el punto más bajo (E). El razonamiento de riesgo-recompensa, las señales visuales y las condiciones son todas iguales; simplemente se reflejan en la estructura alcista. El código fuente que se incluirá con la publicación implementará completamente esta lógica para cualquiera que quiera examinarla o modificarla más a fondo.
Conclusión
En este artículo, exploramos cómo construir un Asesor Experto (EA) en MQL5 que identifica y opera en función de patrones de gráficos técnicos, en particular, el patrón de cabeza y hombros. Comenzamos detectando y marcando puntos de giro importantes, luego usamos herramientas gráficas como triángulos y rectángulos para representar visualmente las estructuras XAB, BCD y DEX. Luego, definimos parámetros comerciales claros, incluidos el punto de entrada, el stop loss y los niveles de toma de ganancias, al tiempo que nos aseguramos de que la relación riesgo-recompensa cumpliera con estándares lógicos. Finalmente, implementamos una lógica de ejecución comercial que coloca órdenes reales basadas en señales confirmadas, lo que hace que el EA sea completamente automatizado.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18147
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
Análisis cuantitativo de tendencias: Recopilamos estadísticas en Python
Particularidades del trabajo con números del tipo double en MQL4
Algoritmos avanzados de ejecución de órdenes en MQL5: TWAP, VWAP y órdenes Iceberg
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
Gracias, por eso siempre me anticipo a sus artículos. ¡Muy explicativos!
¡Eres increíble, Israel! Traduzco regularmente tus artículos al español de forma oficial, y siempre es un verdadero placer trabajar en ellos.
¡Sigue haciendo un trabajo fantástico! ❤️
¡Eres increíble, Israel! Traduzco regularmente tus artículos al español de forma oficial, y siempre es un verdadero placer trabajar en ellos.
¡Sigue haciendo un trabajo fantástico! ❤️
Hola, Miguel.
Gracias por tus amables palabras, significan mucho para mí ❤️
No soy programador así que entender esto me costó un poco pero la forma de desglosarlo y el paso a paso me ha motivado. 💯
Hola Daniels.
Me alegra saber de ti! ❤️