Introducción a MQL5 (Parte 15): Guía para principiantes sobre cómo crear indicadores personalizados (IV)
Introducción
¡Bienvenido de nuevo a la serie Introducción a MQL5! Descubrirás que este artículo se basa directamente en las ideas y técnicas que ya hemos tratado en artículos anteriores. Dado que utilizaremos gran parte de lo que hemos aprendido hasta ahora, esta parte parecerá más una continuación que un nuevo comienzo. A estas alturas, ya deberías tener una sólida comprensión de los fundamentos de MQL5, y en este artículo daremos un paso más allá combinando esos conocimientos para desarrollar indicadores personalizados más interesantes.
Tu valía depende de los proyectos en los que hayas trabajado en MQL5, por lo que esta serie siempre adopta un enfoque basado en proyectos. Es el método más útil para aprender y desarrollarse personalmente. En esta parte de la serie crearemos un indicador capaz de reconocer tendencias, utilizar rupturas de estructura y generar señales de compra y venta. El punto de entrada, el stop loss y varios niveles de take profit están incluidos en estas señales, lo que le proporciona una estrategia completa que puede probar y desarrollar. En este artículo, aprenderás a diseñar indicadores personalizados en MQL5 utilizando conceptos de acción del precio. Para crear una estrategia que siga las tendencias, aprenderás a reconocer estructuras importantes del mercado, como máximos más altos, mínimos más altos y máximos más bajos, mínimos más bajos.
En este artículo aprenderás:
- Cómo crear un indicador de acción del precio.
- Reconocer puntos importantes como mínimo (L), máximo (H), mínimo más alto (HL), máximo más alto (HH), mínimo más bajo (LL) y máximo más bajo (LH) para comprender la estructura de una tendencia alcista y bajista.
- Dibujar la zona de prima y descuento basándose en puntos clave de la tendencia y marcar el nivel de retroceso del 50 %.
- Cómo aplicar la relación riesgo-recompensa al calcular los objetivos de ganancia potenciales en una configuración de tendencia alcista.
- Calcular y marcar el punto de entrada, el stop loss (SL) y los múltiples niveles de take profit (TP) basándose en la estructura de la tendencia.
1. Configuración del proyecto
1.1. Cómo funciona el indicador
El indicador identificará un mínimo, un máximo, un mínimo más alto y un máximo más alto para indicar una tendencia alcista para las señales de compra. A continuación, se determinará el nivel de retroceso del 50 % entre el mínimo más alto y el máximo más alto. Una ruptura en la estructura por encima del máximo más alto iniciará la entrada, y el nivel de retroceso del 50 % servirá como stop loss. Una relación riesgo-recompensa de 1:1 será el objetivo de Take Profit 1, y una relación de 1:2 será el objetivo de Take Profit 2.

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

2. Construcción del indicador de acción del precio
Todas las estrategias de trading pueden transformarse en un indicador, solo que aún no se han visualizado. Cualquier elemento que se ajuste a un conjunto de directrices puede codificarse y mostrarse en el gráfico, ya sea la oferta y la demanda, la evolución de los precios o el soporte y la resistencia. Aquí es donde entra en juego MQL5. Para los operadores algorítmicos, es uno de los lenguajes de programación más potentes y sencillos, que permite transformar cualquier lógica de negociación en una herramienta útil y visualmente atractiva. En esta sección comenzaremos a desarrollar un indicador que analiza el movimiento de los precios, reconoce la estructura del mercado, como máximos, mínimos, máximos más altos y mínimos más bajos, y luego utiliza esos datos para generar señales de compra y venta reveladoras que incluyen niveles de entrada, stop loss y take profit.
En el capítulo uno, describí el objetivo del proyecto y cómo el indicador detectará tendencias, identificará rupturas estructurales y generará señales comerciales completas que incluyen entrada, stop loss y take profit. Ahora comenzaremos a poner en práctica todo lo aprendido en MQL5 en este capítulo. Tomaremos la lógica que discutimos y comenzaremos, paso a paso, a implementarla en el código.
2.1. Identificar los máximos y mínimos
Encontrar los máximos y mínimos oscilantes es la primera etapa en la creación de nuestro indicador de acción del precio. Estos momentos significativos de inflexión en el mercado ayudan a identificar la estructura de la tendencia. Al comparar el máximo o mínimo de la vela actual con el de las velas anteriores y posteriores, podemos identificarlas en MQL5. La detección de máximos más altos, mínimos más altos, máximos más bajos y mínimos más bajos, todos ellos cruciales para identificar patrones y rupturas estructurales, se basará en esto.
Ejemplos://+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOWS | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGHS | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; }
Las dos funciones se utilizaron en el artículo anterior y ayudan a identificar tanto los máximos como los mínimos de oscilación.
2.2. Tendencia alcista
Utilizando la estructura del mercado, este indicador debe verificar primero si existe una tendencia alcista antes de indicar una señal de compra. Un mínimo, un máximo, un mínimo más alto y un máximo más alto son los principales puntos de precio que deben identificarse para lograrlo. Este patrón denota una tendencia alcista, lo que significa que los compradores tienen el control y que es probable que el mercado siga subiendo. Tras confirmarse este patrón, el indicador estará listo para generar una señal de compra legítima.

Ejemplo:
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Salida:

Explicación:
Los parámetros de entrada que se establecen al principio del código se utilizan para examinar la estructura del mercado. Para determinar los máximos y mínimos oscilantes y garantizar la relevancia en el mercado actual, LookbackBars especifica el número de barras que se evalúan antes y después de la barra actual. Mientras tanto, bars_check controla el número total de barras de datos de precios que se examinarán, lo que permite al script buscar hasta 1000 barras en busca de posibles patrones alcistas.
Aunque también requerirá más potencia de cálculo, los valores más altos para bars_check indican que el algoritmo considerará un rango más amplio de datos al buscar estas ubicaciones. Una entrada booleana llamada show_bullish controla si deben mostrarse las señales de compra o las señales alcistas. La aplicación procederá a analizar el movimiento de los precios y determinará la estructura alcista del mercado basándose en los puntos de oscilación si show_bullish se establece en verdadero. Incluso si se cumplen los requisitos, el script no trazará ni resaltará ninguna estructura alcista si está configurado en falso.
La comprobación de show_bullish == true es la primera acción que se lleva a cabo en términos de la lógica del indicador. Esto garantiza que solo cuando desee ver señales de compra se identificará la estructura alcista. A continuación, el programa comprueba si hay suficientes datos de precios disponibles para el análisis, si se cumplen los criterios. La condición if (rates_total >= bars_check) se utiliza para verificar esto. Para evitar errores debidos a datos inadecuados, el análisis se omite si hay menos barras disponibles de las necesarias. A continuación, el script recorre los datos de precios para encontrar puntos de oscilación si se cumple el criterio.
El bucle externo comienza con i, que busca mínimos oscilantes legítimos escaneando los datos de precios hacia atrás desde la barra más reciente. La función IsSwingLow() determina si la barra actual es la más baja del rango especificado por LookbackBars, identificando así un mínimo oscilante. El precio y el momento del mínimo se registran en las variables L y L_time tan pronto como se identifica un mínimo oscilante. Esto sienta las bases para la fase posterior del proceso de descubrimiento del patrón alcista. Tras identificar el mínimo oscilante, el programa busca otro máximo oscilante. Utilizando IsSwingHigh(), el segundo bucle, indexado por j, busca un swing alto en cada barra sucesiva. Los valores de cualquier máximo oscilante que se descubra que sigue al mínimo se registran en H y H_time. Esto crea el segmento inicial de la estructura alcista del mercado, que consiste en un mínimo y un máximo.
Después del máximo del swing, el tercer bucle, que está indexado por k, busca un mínimo más alto. El script utiliza IsSwingLow() una vez más para encontrar un mínimo más alto, que se define como un mínimo mayor que el mínimo inicial L. Cuando se descubre un mínimo más alto, HL y HL_time se actualizan con su valor y tiempo. Tras identificar este mínimo más alto, el programa sigue buscando el máximo más alto posterior. El swing alto que sigue al mínimo más alto se comprueba en el cuarto bucle, que está indexado por l. Sus valores se guardan en HH y HH_time si se descubre este máximo superior. El código determina si los cuatro puntos críticos (mínimo, máximo, mínimo más alto y máximo más alto) siguen un patrón alcista legítimo después de haber sido detectados. El primer mínimo debe ser inferior al primer máximo, el mínimo superior debe ser inferior al primer máximo, el mínimo superior debe ser superior al primer mínimo y el máximo superior debe ser superior al primer máximo. Estos criterios se comprueban mediante la condición if (L < H && HL < H && HL > L && HH > H). Esto confirma una tendencia alcista al garantizar que el patrón sigue la secuencia prevista de máximos y mínimos cada vez más altos.
A continuación, el programa crea y muestra objetos de texto en el gráfico para resaltar los puntos que se han identificado si se cumplen todos estos requisitos. El gráfico muestra los puntos como etiquetas en los períodos y precios correspondientes: Mínimo (L) Máximo (H), Mínimo más alto (HL) y Máximo más alto (HH). ObjectCreate() se utiliza para crear los objetos de texto, mientras que ObjectSetInteger() y ObjectSetString() se utilizan para establecer sus características, incluyendo el tamaño y el color de la fuente. Con los puntos indicados de forma destacada como referencia, el usuario ahora puede reconocer fácilmente la estructura alcista en el gráfico. En resumen, el objetivo del programa es encontrar un patrón de máximos y mínimos cada vez más altos en los datos de precios para evaluar si se trata de una estructura de mercado alcista. Para ello, analiza los puntos de oscilación dentro de un rango predeterminado de barras, registra la información relevante y determina si la estructura muestra el patrón adecuado. Si se verifica el patrón, el usuario puede verlo visualmente en el gráfico. Los parámetros de entrada, que permiten modificaciones según las preferencias del usuario, rigen todo el procedimiento.
2.2.1. Mapeo de los niveles premium y de descuento desde el mínimo más alto al máximo más alto
La división ayuda a definir los términos «zona premium» y «zona de descuento». Por debajo del 50 %, la zona de descuento denota el rango de precios que se consideran más ventajosos o «más baratos» para posibles compras. La zona premium, por otro lado, se encuentra por encima del 50 % y denota tarifas comparativamente «caras». Para optimizar la relación riesgo-recompensa, los operadores suelen optar por comprar desde la zona de descuento en muchos métodos de negociación; sin embargo, para este indicador específico, adoptamos una estrategia ligeramente diferente.

En este caso, solo nos interesa comprar cuando el mercado cotiza por encima de la zona premium o rompe la estructura de máximos superiores. Este patrón indica que la estructura alcista sigue vigente y que es probable que el precio siga subiendo. Para seguir la tendencia y reducir la posibilidad de comprar en un retroceso o reversión, se recomienda esperar a que se supere el máximo más alto o la zona premium.
Ejemplo:
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") // Variables for Premium and Discount string pre_dis_box; // Name/ID for the premium-discount zone box (rectangle object on chart) double lvl_50; // The price level representing the 50% retracement between Higher Low and Higher High string lvl_50_line; // Name/ID for the horizontal line marking the 50% level //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Salida:

Explicación:
El código determina el nivel de retroceso del 50 %, que es la mitad entre el mínimo más alto (HL) y el máximo más alto (HH). Las zona premium (arriba) y de descuento (abajo) están separadas por este nivel. El punto medio entre HL y HH se determina utilizando la fórmula lvl_50 = HL + ((HH - HL)/2);. A continuación, el índice de bucle i se incluye en los nombres de dos variables de cadena, pre_dis_box y lvl_50_line. Estos sirven como identificadores distintivos para los elementos visuales que dibujaremos en el gráfico. Cada dibujo será único y no sustituirá a los anteriores si se incluye el índice de bucle.
Se crea un rectángulo en el gráfico mediante la línea ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); que representa gráficamente la transición desde el mínimo más alto al máximo más alto. Los operadores pueden determinar rápidamente el rango de la oscilación alcista más reciente con el uso de este cuadro. El extremo del rectángulo se fija en una barra futura (l + LookbackBars) utilizando el precio HH, mientras que su inicio se fija en el tiempo y el precio HL. Esto mantiene la caja visible al extenderla ligeramente hacia el futuro.
A continuación, se traza una línea horizontal en el gráfico en el nivel del 50 % mediante la línea ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);. Este umbral es importante porque, según la lógica de este indicador, solo buscamos posibles señales de compra cuando el mercado cotiza por encima de la zona de prima, o por encima del nivel del 50 % de la última subida. Los colores del cuadro y de la línea se establecen en clrDarkGreen, y se utiliza la función ObjectSetInteger para aumentar su grosor (o anchura) a 2 para que ambos sean claramente visibles. El mercado debe superar completamente el HH para que una señal de compra se considere válida; en otras palabras, el precio debe cerrar fuera y por encima de toda la zona de prima. En otras palabras, solo queremos comprar cuando la estructura del mercado es claramente alcista y ha superado el máximo anterior (HH).
2.2.2. Indicar el punto de entrada, el stop loss y el take profit
Una vez marcadas correctamente las zonas premium y de descuento entre el máximo más alto (HH) y el mínimo más alto (HL), el siguiente paso es encontrar posibles señales de compra. Esperar a que una barra alcista rompa por encima del máximo más alto (HH), lo que indica que el mercado sigue moviéndose con fuerza al alza, es la clave para una configuración de compra legítima.
Pero cruzar solo el HH no es suficiente. La barra alcista debe cerrar por encima del HH para que la entrada se verifique, ya que debemos asegurarnos de que la ruptura es real. El hecho de que el precio haya cerrado por encima del HH sugiere que existe una demanda de compra continua y que probablemente seguirá subiendo. El punto de entrada se establece al cierre de la barra alcista que rompe por encima del HH después de que se haya verificado la ruptura. Estamos seguros de que el mercado ha demostrado suficiente fortaleza actualmente como para que podamos entrar en el mercado.
Establecemos el Stop Loss (SL) en el nivel del 50 % (lvl_50), que es el punto medio entre el HL y el HH, para protegernos contra una posible reversión. Para evitar quedar atrapados en una posible caída en la zona de descuento (por debajo del nivel del 50 %), lo que puede indicar un cambio en el estado de ánimo del mercado, hemos colocado el SL aquí. Nuestro método se basa en la relación riesgo-recompensa (R:R) para los niveles de Take Profit (TP). El objetivo de ganancia para el primer nivel de Take Profit, TP1, es igual a la distancia de riesgo entre el punto de entrada y el SL, ya que se establece en una relación R:R de 1:1. El objetivo de beneficio para el segundo nivel de Take Profit, o TP2, es el doble de la distancia entre el punto de entrada y el SL, con una configuración R:R de 1:2. Para los operadores que prefieren asegurar beneficios parciales en el TP1, estos dos niveles de take profit ofrecen flexibilidad, permitiéndoles dejar una parte de la operación abierta para beneficiarse de ganancias adicionales en caso de que el mercado continúe su tendencia positiva.
Ejemplo:
// Variables for Entry, Stop Loss, and Take Profit string entry_line; // Line object to represent the entry point on the chart string entry_txt; // Text object for displaying "BUY" at the entry point double lvl_SL; // Stop Loss level (set at the 50% retracement level) string lvl_sl_line; // Line object for representing the Stop Loss level string lvl_sl_txt; // Text object for labeling the Stop Loss level double TP1; // Take Profit 1 level (1:1 risk-reward ratio) double TP2; // Take Profit 2 level (1:2 risk-reward ratio) string lvl_tp_line; // Line object for representing the Take Profit 1 level string lvl_tp2_line; // Line object for representing the Take Profit 2 level string lvl_tp_txt; // Text object for labeling the Take Profit 1 level string lvl_tp2_txt; // Text object for labeling the Take Profit 2 level string buy_object; // Arrow object to indicate the Buy signal on the chart
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); for(int m = l; m < rates_total-1; m++) { if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { TP1 = close[m] + (close[m] - lvl_50); TP2 = TP1 + (close[m] - lvl_50); entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); ObjectCreate(chart_id,entry_line,OBJ_TREND,0,HL_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line,OBJ_TREND,0,HL_time,lvl_50,time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line,OBJ_TREND,0,HL_time,TP1,time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line,OBJ_TREND,0,HL_time,TP2,time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_COLOR,clrDarkGreen); entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id,buy_object,OBJ_ARROW_BUY,0,time[m],close[m]); break; } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }Salida:

Se puede observar que las zonas premium y de descuento, así como los marcadores de señales de compra, no están dibujados con precisión en la imagen anterior. Esto se debe a que se pasaron por alto ciertas condiciones antes de dibujar los objetos en el gráfico. Para mejorar el enfoque, debemos incluir más verificaciones para asegurarnos de que la señal de compra sea legítima. Antes de dibujar cualquier objeto en el gráfico, primero debemos asegurarnos de que una vela realmente haya rompido el máximo más alto (HH). Una ruptura del HH significa una continuación de la tendencia alcista, lo cual es necesario para que la señal de compra se considere legítima, por lo que este es un criterio fundamental. No debemos comenzar los cálculos de entrada y gestión de riesgos hasta que se haya cumplido este requisito.
A continuación, se debe contar el número de barras desde el mínimo más alto (HL) hasta el final del cuadro premium y descuento. Esto garantiza que la evolución del precio se mantenga dentro de un rango aceptable y nos ayuda a comprender hasta qué punto se ha movido el mercado. Una vez finalizado este recuento, debemos confirmar que el precio de cierre de la barra alcista, que rompió el máximo más alto (HH), se encuentra cerca del cuadro premium y descuento. Esto garantiza que la señal de compra no se aleje demasiado de la estructura de mercado prevista y se produzca dentro de un rango de precios razonable.
Ejemplo:
// Declare variables to count bars int n_bars; // Number of bars from Higher Low to the end of the Premium/Discount box int n_bars_2; // Number of bars from the end of the Premium/Discount box to the bullish bar that broke HH
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Loop through the bars to check for the conditions for entry for(int m = l; m < rates_total-1; m++) { // Check if the current bar is a bullish bar and if the price has broken the higher high (HH) if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { // Count the bars between HL_time and the end of the Premium/Discount box n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]); // Count the bars between the end of the Premium/Discount box and the candle that broke HH n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H && open[l+LookbackBars] <= HH && n_bars_2 < n_bars) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); // Calculate Take Profit levels based on the 50% retracement TP1 = close[m] + (close[m] - lvl_50); // TP1 at 1:1 risk-reward ratio TP2 = TP1 + (close[m] - lvl_50); // TP2 at 1:2 risk-reward ratio // Create unique object names for Entry, Stop Loss, and Take Profit lines and text entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); // Create the lines on the chart for Entry, Stop Loss, and Take Profit levels ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, HL_time, close[m], time[m], close[m]); ObjectCreate(chart_id, lvl_sl_line, OBJ_TREND, 0, HL_time, lvl_50, time[m], lvl_50); ObjectCreate(chart_id, lvl_tp_line, OBJ_TREND, 0, HL_time, TP1, time[m], TP1); ObjectCreate(chart_id, lvl_tp2_line, OBJ_TREND, 0, HL_time, TP2, time[m], TP2); // Set the properties for the lines (width, color, etc.) ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_COLOR, clrDarkGreen); // Create the text labels for Entry, Stop Loss, and Take Profit levels entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); // Create the text objects for the Entry, Stop Loss, and Take Profit labels ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0, time[m], lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0, time[m], close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, entry_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0, time[m], TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0, time[m], TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_FONTSIZE, 15); // Create a Buy arrow object to indicate the Buy signal on the chart buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id, buy_object, OBJ_ARROW_BUY, 0, time[m], close[m]); break; // Exit the loop once a Buy signal is found } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }
Salida:

Explicación:
Se declararon dos variables enteras, n_bars y n_bars_2, en el espacio global del código. El número de velas (barras) entre puntos importantes en el patrón de estructura alcista del mercado viene determinado por estas variables. En concreto, n_bars es el número de barras que separan el final del cuadro Premium/Descuento (time[l + LookbackBars]) del máximo más alto (HL). Sin embargo, n_bars_2 cuenta el número de barras que separan la vela alcista que rompió el máximo más alto (HH) del final del cuadro premium/descuento. Este recuento se utiliza para evaluar si la acción del precio se ha desviado demasiado de la zona óptima de negociación o si la señal de compra sigue siendo válida.
Estas variables se emplean más adelante en el código como parte de una condición de validación adicional que refuerza el argumento a favor de una estructura de mercado alcista. Se comprueba una condición adicional n_bars_2 < n_bars después de determinar los valores mínimo, máximo, mínimo superior y máximo superior (asegurándose de que se ajustan a la estructura L < H, HL < H, HL > L y HH > H) y de verificar que el precio de apertura de la vela que cerró el cuadro premiun/descuento no es superior al máximo superior. Esto garantiza que la vela alcista de ruptura (la que rompe por encima del HH) no aparezca demasiado lejos después de la formación, lo que podría sugerir una configuración débil o inválida, y que aparezca bastante cerca del patrón.
Todas las rutinas ObjectCreate() y ObjectSet*() que se utilizaban anteriormente para dibujar los marcadores de mínimo, máximo, mínimo superior, máximo superior, cuadro premium/descuento, línea del 50 % y entrada/SL/TP del gráfico se transfirieron dentro de esta instrucción if para imponer esta inspección más estricta. Esto implica que solo cuando se cumplan todos los requisitos de estructura alcista y tiempo se producirán y mostrarán estos componentes visuales. De esta manera, el gráfico se mantiene claro y no se sobrecarga con elementos erróneos o prematuros debido a señales engañosas.
2.3. Tendencia bajista
Este indicador debe utilizar primero la estructura del mercado para validar una caída antes de poder indicar una venta. Esto se logra encontrando una serie de puntos de precios significativos: un máximo, un mínimo, un máximo más bajo y un mínimo más bajo. Este patrón demuestra que los vendedores tienen el control y que es probable que el mercado siga bajando, lo que respalda el impulso bajista. El indicador comenzará a buscar señales de venta legítimas tan pronto como se verifique esta estructura.

Ejemplo:
// Variables for Bearish Market Structure double LH; // Lower High: the high formed after the initial low in a downtrend datetime LH_time; // Time of the Lower High string LH_letter; // Label used to display the Lower High on the chart (e.g., "LH") double LL; // Lower Low: the new low formed after the Lower High in a downtrend datetime LL_time; // Time of the Lower Low string LL_letter; // Label used to display the Lower Low on the chart (e.g., "LL") string sell_object; // Arrow object to indicate the Sell signal on the chart
// BEARISH TREND if(show_bearish == true) // Check if the user enabled the bearish trend display { if(rates_total >= bars_check) // Ensure enough candles are available for processing { // Loop through historical bars to find a swing high (potential start of bearish structure) for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingHigh(high, i, LookbackBars)) // Detect first swing high { H = high[i]; H_time = time[i]; H_letter = StringFormat("High B%d", i); // Label for the high // From the swing high, look for the next swing low for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingLow(low, j, LookbackBars) && time[j] > H_time) // Confirm next swing low { L = low[j]; L_time = time[j]; L_letter = StringFormat("Low B%d", j); // Label for the low // From the swing low, look for the Lower High for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingHigh(high, k, LookbackBars) && time[k] > L_time) { LH = high[k]; LH_time = time[k]; LH_letter = StringFormat("Lower High%d", k); // Label for the Lower High // From the LH, find a Lower Low for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingLow(low, l, LookbackBars) && time[l] > LH_time) { LL = low[l]; LL_time = time[l]; LL_letter = StringFormat("Lower Low%d", l); // Label for Lower Low // Calculate 50% retracement level from LH to LL lvl_50 = LL + ((LH - LL)/2); // Prepare object names pre_dis_box = StringFormat("Gan Box B%d", i); lvl_50_line = StringFormat("Level 50 Line B%d", i); // Search for a bearish entry condition for(int m = l; m < rates_total-1; m++) { // Confirm bearish candle breaking below the LL if(close[m] < open[m] && close[m] < LL && time[m] >= time[l+LookbackBars]) { // Count bars for pattern distance validation n_bars = Bars(_Symbol,PERIOD_CURRENT,LH_time, time[l+LookbackBars]); // From LH to box end n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[l+LookbackBars], time[m]); // From box end to break candle // Confirm valid bearish structure and proximity of break candle if(H > L && LH > L && LH < H && LL < L && open[l+LookbackBars] >= LL && n_bars_2 < n_bars) { // Draw the Premium/Discount box ObjectCreate(chart_id,pre_dis_box, OBJ_RECTANGLE,0,LH_time,LH, time[l+LookbackBars],LL); ObjectCreate(chart_id,lvl_50_line, OBJ_TREND,0,LH_time,lvl_50, time[l+LookbackBars],lvl_50); ObjectSetInteger(chart_id,pre_dis_box,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_50_line,OBJPROP_WIDTH,2); // Label the structure points ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id,H_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id,L_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LH_letter, OBJ_TEXT, 0, LH_time, LH); ObjectSetString(chart_id, LH_letter, OBJPROP_TEXT, "LH"); ObjectSetInteger(chart_id,LH_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LL_letter, OBJ_TEXT, 0, LL_time, LL); ObjectSetString(chart_id, LL_letter, OBJPROP_TEXT, "LL"); ObjectSetInteger(chart_id,LL_letter,OBJPROP_FONTSIZE,15); ObjectSetInteger(chart_id,H_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,L_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LL_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LH_letter,OBJPROP_WIDTH,2); // Calculate Take Profits based on 1:1 and 1:2 RR TP1 = close[m] - (lvl_50 - close[m]); TP2 = TP1 - (lvl_50 - close[m]); // Generate entry, SL and TP object names entry_line = StringFormat("Entry B%d", m); lvl_sl_line = StringFormat("SL B%d", m); lvl_tp_line = StringFormat("TP B%d", m); lvl_tp2_line = StringFormat("TP 2 B%d", m); // Draw entry, SL, TP1, TP2 levels ObjectCreate(chart_id,entry_line,OBJ_TREND,0,LH_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,0,LH_time,lvl_50, time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,0,LH_time,TP1, time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,0,LH_time,TP2, time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); // Generate text labels entry_txt = StringFormat("Entry Text B%d", m); lvl_sl_txt = StringFormat("SL Text B%d", m); lvl_tp_txt = StringFormat("TP Text B%d", m); lvl_tp2_txt = StringFormat("TP 2 Text B%d", m); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "SELL"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); // Draw sell arrow sell_object = StringFormat("Sell Object%d", m); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[m],close[m]); } break; // Exit loop after valid setup } } break; // Exit LL search } } break; // Exit LH search } } break; // Exit L search } } } } } }
Salida:

Explicación:
El código comienza utilizando if(show_bearish == true) para ver si el usuario ha activado la lógica de tendencia bajista. Para encontrar una estructura bajista legítima del mercado, el indicador recorre las barras históricas si está habilitado y hay suficientes barras disponibles (rates_total >= bars_check). El primer paso del proceso es determinar un máximo oscilante (H). Después de identificar un máximo oscilante, el código busca un mínimo oscilante (L) que siga al máximo. Si se descubre, sigue buscando un mínimo más bajo (LL) que valide la estructura bajista, seguido de un máximo más bajo (LH), que es un máximo oscilante por debajo del primer máximo H. Las etiquetas como «H», «L», «LH» y «LL» se crean en el gráfico utilizando estos valores y las marcas de tiempo asociadas.
A continuación, se traza la zona premium/descuento desde el LH hasta el LL, y se determina el nivel de retroceso del 50 % entre el LH y el LL (lvl_50 = LL + ((LH - LL)/2). El indicador busca una vela bajista (close < open) que cierre por debajo del LL antes de colocar cualquier objeto relacionado con la operación (entrada, SL, TP1, TP2). Al utilizar dos variables, n_bars, que cuenta las barras desde el máximo inferior (LH) hasta el final del cuadro premium/descuento, y n_bars_2, que cuenta desde el final del cuadro hasta la vela bajista que rompe el mínimo inferior (LL), el código también se asegura de que la ruptura de la estructura se produzca dentro de un número razonable de barras. El código solo dibuja la línea de entrada al cierre de la vela, establece el Stop Loss (SL) en el nivel del 50 % y posiciona TP1 y TP2 en niveles de riesgo-recompensa de 1:1 y 1:2, respectivamente, cuando se cumplen todos los requisitos, incluyendo una estructura adecuada, una ruptura válida y una distancia apropiada.
Además, añade una flecha de venta y la palabra «SELL» a la vela bajista. En el caso de una tendencia bajista, la lógica es básicamente la misma que para un patrón de tendencia positiva, pero se invierte. Dado que la tendencia bajista es solo lo contrario de la tendencia alcista, que ya se ha detallado exhaustivamente, la explicación es breve. La estructura y el razonamiento son los mismos, excepto que se invierten para mostrar una tendencia a la baja en lugar de una al alza.
Conclusión
En este artículo, hemos creado un indicador personalizado MQL5 que identifica la estructura del mercado mediante la detección de puntos clave como Mínimo (L), Mínimo inferior (LL), Mínimo superior (HL), Máximo (H) y Máximo superior (HH). Utilizando estos puntos, el indicador determina las tendencias alcistas o bajistas y dibuja automáticamente los puntos de entrada, el stop loss en el nivel 50 y los niveles de take profit (TP1 y TP2) basándose en un patrón estructurado. También marca las zonas de prima y descuento para resaltar visualmente dónde es probable que reaccione el precio. Todos los objetos del gráfico se dibujan solo cuando se cumplen condiciones específicas, lo que garantiza que las señales sigan siendo claras y fiables.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17689
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.
Determinamos la sobrecompra y la sobreventa usando la teoría del caos
Redes neuronales en el trading: Jerarquía de habilidades para el comportamiento adaptativo de agentes (Final)
Análisis angular de los movimientos de precios: un modelo híbrido para predecir los mercados financieros
Arbitraje estadístico mediante reversión a la media en el trading de pares: Cómo superar al mercado con matemáticas
- 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