Introducción a MQL5 (Parte 11): Guía de trabajo con indicadores incorporados en MQL5 para principiantes (II)
Introducción
¡Bienvenidos de nuevo a nuestra serie MQL5! En este capítulo trataremos un tema fascinante que creo que te resultará muy útil e interesante. En el último capítulo vimos cómo utilizar los indicadores integrados de MQL5, prestando especial atención al RSI. No solo eso, sino que incluso trabajamos en un proyecto práctico que te enseñó cómo incorporar el RSI a tu estrategia de trading con facilidad. Esta vez vamos un paso más allá al incluir no una, sino tres potentes indicaciones en nuestro proyecto (RSI, el oscilador estocástico y la media móvil). Eso no es todo; también aprenderás sobre la intrigante idea de la detección de divergencias ocultas a través de nuestro proyecto. Analizaremos específicamente cómo detectar divergencias alcistas y bajistas ocultas.
Es fundamental recordar que el único objetivo de este proyecto es la formación. Los objetivos principales son desarrollar su confianza en el uso de MQL5 y aprender a reconocer estos patrones mediante programación. Por lo tanto, comencemos con este proyecto práctico que aumentará su comprensión de MQL5 y le permitirá mejorar sus habilidades comerciales.
En este artículo aprenderás:
- Cómo examinar los mínimos y máximos del RSI en relación con el movimiento correspondiente del precio para descubrir divergencias alcistas y bajistas ocultas.
- El desarrollo de asesores expertos utilizando múltiples indicadores (RSI, MA y oscilador estocástico).
- Identificar y visualizar divergencias alcistas y bajistas ocultas en el gráfico.
- Implementación de la gestión de riesgos mediante el cálculo del tamaño de los lotes en función del riesgo en dólares y la distancia de stop-loss para tamaños de velas inconsistentes cuando se utilizan indicadores integrados.
- Automatización de operaciones con una relación riesgo-recompensa (Risk-Reward Ratio, RRR) predefinida utilizando señales de divergencia.
1. Configuración del proyecto
1.1. Cómo funciona el EA
En este proyecto se utilizan los indicadores de media móvil y RSI para encontrar divergencias alcistas y bajistas ocultas. Mientras que el RSI encuentra diferencias entre los niveles del RSI y los precios correspondientes, la MA ayuda a identificar la tendencia. El oscilador estocástico solo se utiliza para validar las señales de trading al final y no para identificar divergencias.
1.1.1. Divergencia alcista oculta
En este punto, el RSI forma un mínimo más bajo (Lower Low, LL), mientras que el precio forma un mínimo más alto (Higher Low, HL). Esta divergencia implica que el RSI es débil, lo que sugiere un posible impulso alcista en el futuro, a pesar de que el precio de correspondencia (C-Price) haya alcanzado un mínimo más alto.

1.1.2. Divergencia bajista oculta
En este punto, el RSI forma un máximo más alto, mientras que el precio forma un máximo más bajo. Lo que sugiere un posible impulso a la baja en el futuro.

1.1.3. Lógica para las ventas
Filtro de tendencias:
- Asegúrate de que el precio actual esté por debajo de la media móvil exponencial (EMA) de 200 para confirmar una tendencia bajista.
Marca el último máximo significativo del RSI:
- Identifica el máximo más reciente del RSI formado después de una vela alcista, seguido de tres velas bajistas consecutivas.
- Trace una línea horizontal en el gráfico RSI a este nivel.
Marcar el precio correspondiente:
- En el gráfico de precios (cambia a un gráfico de líneas), coloca una línea horizontal en el nivel de precio correspondiente (C-PRICE) del máximo del RSI.
Divergencia bajista oculta:
- Busca un nuevo máximo del RSI por encima de la línea horizontal del RSI.
- Asegúrese de que el precio máximo correspondiente esté por debajo de la línea horizontal del precio en el gráfico.
Confirmación estocástica:
- Una vez que se cumplan todas las condiciones, espere a que el estocástico cruce por encima del nivel de sobrecompra (por ejemplo, 80) y luego vuelva a cruzarlo por debajo en un plazo de 11 barras.
- Si la confirmación no se produce en un plazo de 11 barras, no se realiza ninguna operación.
Establecer SL y TP:
- SL: Último máximo alcista.
- TP: Definido por un parámetro de entrada para la Relación riesgo-recompensa (Risk-Reward Ratio, RRR).
1.1.4. Lógica para compras
Filtro de tendencias:
- Asegúrate de que el precio actual esté por encima de la media móvil exponencial (EMA) de 200.
Marca el último mínimo significativo del RSI:
- Identifique el mínimo más reciente del RSI formado después de una vela bajista, seguidade tres velas alcistas consecutivas.
- Trace una línea horizontal en el gráfico RSI a este nivel.
Marcar el precio correspondiente:
- En el gráfico de precios (cambie a un gráfico de líneas), coloque una línea horizontal en el nivel de precio correspondiente (C-PRICE) del mínimo del RSI.
Divergencia alcista oculta:
- Busca un nuevo mínimo del RSI por debajo de la línea horizontal del RSI.
- Asegúrese de que el precio mínimo correspondiente esté por encima de la línea horizontal del precio (C-PRICE) en el gráfico.
Confirmación estocástica:
- Una vez que se cumplan todas las condiciones, espere a que el estocástico cruce por debajo del nivel de sobreventa (por ejemplo, 20) y luego vuelva a cruzarlo por encima en un plazo de 11 barras.
- Si la confirmación no se produce en un plazo de 11 barras, no se realiza ninguna operación.
Establecer SL y TP:
- SL: Último mínimo.
- TP: Definido por un parámetro de entrada para la Relación riesgo-recompensa (Risk-Reward Ratio, RRR).
2. Añadir propiedades del indicador y recuperar los datos
2.1. Configuración de las propiedades del indicador
Configuraremos las propiedades de los indicadores que utilizaremos en nuestro proyecto: el oscilador estocástico, el RSI y la media móvil. Estas propiedades ofrecen libertad a la hora de diseñar el EA y especifican cómo se comportarán los indicadores.
Analogía
Imagina que estás preparando un kit de herramientas para una tarea específica. Al igual que una cinta métrica, la media móvil te permite comprender las dimensiones generales de la tarea (la dirección de la tendencia). Al actuar como una lupa, el RSI le ayuda a detectar los matices más pequeños o las divergencias ocultas. Antes de pasar a la siguiente etapa (confirmación final), el oscilador estocástico sirve como herramienta de nivelación para asegurarse de que todo esté perfectamente alineado.
Considere la computadora como su ayudante en esta tarea. Tienes que explicarle exactamente qué son estas herramientas y cómo deberían funcionar antes de poder pedirle que las utilice. No se puede entregar una herramienta genérica y esperar que sepa qué hacer. Más bien, debes especificar las características de cada herramienta, como el tipo de cinta métrica, el nivel de aumento de la lupa o la sensibilidad de la herramienta de nivel. Al configurar estas funciones, se garantiza que la computadora comprenda exactamente qué indicadores utilizar y cómo utilizarlos de manera eficiente.
Ejemplos://DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); return(INIT_SUCCEEDED); }
Explicación:
Imagínese que está en un taller con tres equipos especializados para su proyecto: una cinta métrica (MA), una lupa (RSI) y un nivel de burbuja (oscilador estocástico). Asegúrate de que todas las herramientas estén listas para usar y configuradas correctamente antes de comenzar. ¿No los sacarías de la caja de herramientas y las usarías ahora mismo?
Declaración de indicadores (preparación de las herramientas)
int ma_handle; int rsi_handle; int stoch_handle;
Es similar a poner nombre a los estantes de tu taller donde guardarás tus herramientas. Cada herramienta debe tener su propia ubicación para que puedas encontrarla cuando la necesites.
- ma_handle es para la cinta métrica (media móvil).
- rsi_handle es para la lupa (RSI).
- stoch_handle es para el nivel de burbuja (oscilador estocástico).
Configuración de las herramientas (inicialización de indicadores)
Configuración de la media móvil (iMA)ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);
Es como decirle a tu asistente: «Necesito preparar la cinta métrica para un proyecto». Le estás indicando exactamente cómo utilizarla:
- _Symbol: Esto permite al asistente saber qué estás midiendo, como el activo, las acciones o el par de divisas con el que estás operando. Esto describe el activo que estás estudiando (por ejemplo, EURUSD), de la misma manera que identificarías lo que estás midiendo con tu cinta métrica.
- PERIOD_CURRENT: Esto se refiere al plazo con el que estás trabajando. Piénsalo como la longitud de una cinta métrica: ¿estás midiendo el día, la semana o el mes pasado? PERIOD_CURRENT significa que estás trabajando con el marco temporal del gráfico actual.
- 200: Es la longitud de la cinta métrica (el número de velas que cubrirá la media móvil). Entonces, le estás pidiendo a tu asistente que utilice una cinta métrica que analice los últimos 200 periodos para medir la tendencia general.
- 0: Este es el desplazamiento. Indica cuánto desea desplazar la medición hacia la derecha o hacia la izquierda. Aquí, 0 significa que no hay desplazamiento: se mide directamente el precio actual y los últimos 200 períodos.
- MODE_EMA: Esto especifica el tipo de medida que estás utilizando: media móvil exponencial (EMA). La EMA es más sensible y reacciona más rápidamente a los cambios de precio que una media móvil simple. Entonces, estás eligiendo una cinta métrica que se adapta rápidamente a los nuevos datos.
- PRICE_CLOSE: Este es el punto de referencia para tu cinta métrica. En este caso, la medición se basa en el precio de cierre de cada período. Al igual que una cinta métrica se puede calibrar para medir distancias específicas, le estás diciendo a tu asistente que mida específicamente los precios de cierre.

Configuración del iRSI (Índice de fuerza relativa)
rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
Ahora, vas a configurar la lupa (RSI). Le explicas a tu asistente exactamente cómo utilizarlo:
- _Symbol: Esto funciona igual que antes, especificando el activo que estás analizando.
- PERIOD_CURRENT: Al igual que con la media móvil, se trabaja con el marco temporal del gráfico actual.
- 14: Este es el período para el RSI. Piensa en ello como si estuvieras ajustando la potencia de aumento de tu lupa. Le estás diciendo a tu asistente que se centre en 14 períodos (o velas), ayudándote a analizar la fuerza relativa y detectar divergencias.
- PRICE_CLOSE: Al igual que con la media móvil, para calcular el RSI hay que fijarse en los precios de cierre. Esto garantiza que se esté observando el punto final de cada periodo para obtener una lectura precisa.

Configuración de iStochastic (oscilador estocástico)
stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);
- _Symbol: Una vez más, esto especifica qué activo estás analizando, al igual que los indicadores anteriores.
- PERIOD_CURRENT: Estás trabajando con el marco temporal del gráfico actual.
- 5, 3 y 3: Estos son los parámetros del oscilador estocástico:
- 5 es el período %K: es como establecer la sensibilidad del nivel de burbuja (oscilador estocástico) a los movimientos de los precios. Comprueba los últimos 5 periodos para ver dónde se encuentra el precio en relación con su rango.
- 3 es el período %D: es la versión suavizada de %K, que se utiliza a menudo para obtener una señal más clara.
- 3 es el periodo de desaceleración: esto suaviza aún más el indicador para garantizar que no reaccione demasiado ante pequeños cambios, lo que ayuda a evitar señales falsas.
- MODE_SMA: Le estás pidiendo al nivel de espíritu que utilice un método de media móvil simple (SMA) para suavizar los valores %K y %D, que es una forma básica pero fiable de promediar los datos.
- STO_LOWHIGH: Indica al asistente que utilice el método bajo/alto para calcular el oscilador estocástico. Significa que te centras en el rango de precios (mínimo más bajo y máximo más alto) durante cada periodo, en lugar de utilizar los precios de cierre.

Al indicar estos parámetros al ordenador, te aseguras de que cada uno de tus indicadores (herramientas) esté correctamente calibrado para realizar su trabajo de forma eficaz y precisa en el proyecto.
3. Recuperación de indicadores y datos de velas japonesas
3.1. Recuperación de datos de indicadores
La extracción de datos de los indicadores que establecimos en la sección anterior será el tema principal de esta sección. Podemos utilizar los datos del indicador para informar las recomendaciones comerciales de nuestro Asesor Experto recuperándolos. También examinaremos cómo extraer información del oscilador estocástico, el RSI y la media móvil (MA). Estos factores son cruciales porque proporcionan a nuestro EA las señales que necesita para realizar operaciones. Nuestro EA tiene que obtener los datos relevantes de estos indicadores para determinar si se cumplen las condiciones para operar, del mismo modo que un constructor necesita mediciones precisas de su equipo para asegurarse de que un proyecto avanza correctamente. Examinaremos cómo llamar y almacenar eficazmente estas variables en las siguientes fases, de modo que el EA pueda utilizar datos de mercado en tiempo real para fundamentar sus juicios.
Ejemplo:
//DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA double ma_buffer[]; double rsi_buffer[]; double stoch_buffer_k[], stoch_buffer_d[]; //DECLEAR VARIABLES TO STORE CANDLE DATA double open[]; double close[]; double high[]; double low[]; datetime time[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(ma_buffer,true); ArraySetAsSeries(rsi_buffer,true); ArraySetAsSeries(stoch_buffer_k,true); ArraySetAsSeries(stoch_buffer_d,true); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyBuffer(ma_handle,0,1,30,ma_buffer); CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k); CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d); CopyBuffer(rsi_handle,0,1,30,rsi_buffer); //COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time); }
Explicación:
Declarar variables dobles que almacenan los datos de indicadores
Las matrices deben declararse antes de poder almacenar los datos de los indicadores. Dado que los valores numéricos de los indicadores (que incluyen el oscilador estocástico, el RSI y la media móvil) se almacenarán en estas matrices, son de tipo doble. El ma_buffer[] contendrá los valores de la media móvil (MA), mientras que el rsi_buffer[] conservará los valores del RSI. Las matrices stoch_buffer_k[] y stoch_buffer_d[] almacenan específicamente las líneas K y D del oscilador estocástico. Estas matrices están inicialmente vacías y se llenarán rápidamente con los datos indicadores que obtenemos del gráfico.
Empezando por la última vela del gráfico
En este paso, utilizamos la función ArraySetAsSeries() para ordenar las matrices de modo que los datos más recientes se encuentren en el primer índice de cada matriz. Al pasar true como argumento, le estamos indicando al programa que comience a rellenar las matrices con los valores más recientes. Esto es importante porque, por defecto, las matrices se rellenan con los datos más antiguos (el primer elemento), pero en este caso necesitamos asegurarnos de que los valores más recientes del indicador sean los primeros en ser accesibles. Este acuerdo nos facilita trabajar con los datos más recientes a la hora de tomar decisiones comerciales.
Copia de datos del indicador a las matrices
Una vez configuradas las matrices, utilizamos la función CopyBuffer() para recuperar los datos reales del indicador del gráfico. La función CopyBuffer() copia los datos del búfer del indicador a nuestras matrices declaradas anteriormente. Cada llamada a CopyBuffer() es específica para un indicador:
- Para la media móvil, copiamos los datos de ma_handle y los colocamos en ma_buffer[].
- Recuperamos los valores %K y %D del oscilador estocástico y los almacenamos en stoch_buffer_k[] y stoch_buffer_d[], respectivamente.
- Copiamos los datos del RSI en rsi_buffer[].
La función recibe la instrucción de transferir los datos de las últimas 30 barras mediante la cuarta entrada, 30, y comenzar con la segunda barra más reciente mediante el tercer parámetro, lo que nos permite extraer de los indicadores los datos históricos necesarios para el análisis y la toma de decisiones de nuestro Asesor Experto.
En resumen, estos pasos garantizan que podamos recopilar, almacenar y acceder a los datos de nuestros indicadores. Al declarar las matrices, establecer el orden correcto de los datos y utilizar CopyBuffer() para recuperar los datos, tenemos todo lo necesario para comenzar a analizar los valores del indicador y tomar decisiones comerciales basadas en esos valores.
3.2. Datos de velas japonesas
Ahora debemos recopilar los datos correspondientes de las velas japonesas para que coincidan con los valores de los indicadores, tras haber recuperado correctamente los datos de los indicadores. Los datos de las velas japonesas son cruciales para la toma de decisiones en el trading, ya que nos proporcionan el contexto en el que aplicar nuestros indicadores. Las velas japonesas nos muestran la actividad real de los precios del mercado, al igual que los indicadores nos informan sobre las tendencias y el impulso. Podemos encontrar posibles señales de trading, incluyendo el desarrollo de divergencias alcistas o bajistas u otros patrones que podrían indicar un cambio en el estado de ánimo del mercado, integrando datos de indicadores y velas japonesas.
Ejemplos:
double open[]; double close[]; double high[]; double low[]; datetime time[];
Declaramos matrices (open[], close[], high[], low[] y time[]) para almacenar información esencial sobre las velas, como los precios de apertura, cierre, máximo y mínimo, así como sus correspondientes marcas de tiempo. Estas variables nos ayudan a analizar los movimientos de los precios y correlacionarlos con los datos del indicador obtenidos anteriormente.
ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true);Para garantizar que la vela más reciente se encuentre al principio de cada matriz, utilizamos la función ArraySetAsSeries. Esto reorganiza los datos, de modo que la vela más reciente se encuentra en el índice 0, seguida por las velas más antiguas en secuencia. Esta alineación simplifica el análisis y garantiza la coherencia con la estructura de datos del indicador.
CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time);
Utilizando funciones como CopyOpen, CopyClose, CopyHigh, CopyLow y CopyTime, rellenamos las matrices con datos reales del mercado. Recuperamos los datos de las últimas 30 velas a partir de la segunda vela del gráfico, alineando los datos de precios con los valores indicativos correspondientes para tomar decisiones precisas.
4. Identificación de los últimos máximos y mínimos del RSI con los datos correspondientes
Como criterio para comparar el comportamiento del indicador con la evolución del precio, es fundamental localizar los máximos y mínimos más recientes del RSI. El mercado puede seguir subiendo cuando hay una divergencia alcista oculta, que se produce cuando el precio alcanza un mínimo más alto y el RSI alcanza un mínimo más bajo. Sin embargo, existe una divergencia bajista oculta que apunta a una posible continuación a la baja cuando el precio alcanza un máximo más bajo y el RSI alcanza un máximo más alto.
Para determinar si el máximo estaba por debajo de la media móvil en ese momento o si el mínimo estaba por encima, también obtendremos el número de media móvil correspondiente. Esta validación adicional garantiza que la tendencia está en línea con el movimiento previsto y ayuda a validar las señales de divergencia.
4.1. Encontrar divergencias alcistas ocultas
Identificar el último mínimo del RSI es un paso fundamental para detectar divergencias alcistas ocultas. El mínimo del RSI representa un punto en el que el impulso se debilita durante un retroceso del precio, y analizarlo junto con los valores correspondientes del precio y la media móvil (MA) proporciona información valiosa. Una divergencia alcista oculta se produce cuando el RSI forma un mínimo más bajo mientras que el precio crea un mínimo más alto, lo que indica un posible impulso alcista a pesar del retroceso.
Para detectar el mínimo del RSI, definiremos una lógica específica: el mínimo debe formarse después de al menos un movimiento bajista seguido de tres movimientos alcistas consecutivos. Esta condición garantiza que el mínimo refleje un cambio significativo en el impulso. Una vez identificado el mínimo del RSI, recuperaremos su posición, anotaremos el precio correspondiente en el gráfico en ese punto y extraeremos el valor de la media móvil para determinar si el mínimo del precio estaba por encima de la media móvil. Esta validación ayuda a confirmar la alineación de la señal de divergencia con la tendencia predominante, lo que mejora su fiabilidad.
Después de identificar el mínimo del RSI, también definiremos la lógica para buscar un mínimo más bajo que pueda formarse después del mínimo del RSI. Este mínimo inferior debe tener un valor RSI por debajo del mínimo RSI identificado, y el precio en ese momento debe permanecer por encima del mínimo anterior. Esto garantiza que la divergencia permanezca intacta y respalda los criterios de divergencia alcista oculta. Al integrar estos pasos, establecemos un enfoque integral y estructurado para identificar de manera fiable oportunidades ocultas de divergencia alcista.
Ejemplo://DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA double ma_buffer[]; double rsi_buffer[]; double stoch_buffer_k[], stoch_buffer_d[]; //DECLEAR VARIABLES TO STORE CANDLE DATA double open[]; double close[]; double high[]; double low[]; datetime time[]; //DECLEAR VARIBLE ROR RSI LOW AND CORESPONDING CANDLE double rsi_low_value; // Stores the RSI value at the identified low point. double corresponding_low_value; // Stores the price (closing value) corresponding to the RSI low. double corresponding_low_ma; // Stores the Moving Average value at the same point. datetime rsi_low_time; // Stores the timestamp of the RSI low. //DECLEAR VARIABLE FOR THE MINIMUM CORRESPONDING PRICE VALUE int minimum_value_low; //DECLEAR VARIBLE ROR NEW RSI LOW AND CORESPONDING CANDLE double new_rsi_low_value; datetime new_rsi_low_time; double new_corresponding_low_value; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(ma_buffer,true); ArraySetAsSeries(rsi_buffer,true); ArraySetAsSeries(stoch_buffer_k,true); ArraySetAsSeries(stoch_buffer_d,true); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyBuffer(ma_handle,0,1,30,ma_buffer); CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k); CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d); CopyBuffer(rsi_handle,0,1,30,rsi_buffer); //COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time); //LOOP THROUGH THE LAST 30 BARS ON THE CHART for(int i = 0; i < 30; i++) { //PREVENT ARRAY OUT OF RANGE ERROR if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30)) { //LOGIC TO IDENTIFY THE LATEST RSI LOW if(rsi_buffer[i+4] > rsi_buffer[i+3] && rsi_buffer[i+2] > rsi_buffer[i+3] && rsi_buffer[i+1] > rsi_buffer[i+2] && rsi_buffer[i] > rsi_buffer[i+1]) { //GETTING LATEST RSI LOW, CORRESPONDING CANDLE LOW, CORRESPONDING MA VALUE, and RSI TIME rsi_low_value = rsi_buffer[i+3]; corresponding_low_value = close[i+3]; corresponding_low_ma = ma_buffer[i+3]; rsi_low_time = time[i+3]; break; } } } //TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI LOW int total_bars_2 = 0; total_bars_2 = Bars(_Symbol,PERIOD_CURRENT,rsi_low_time, time[0]); //MINIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW minimum_value_low = ArrayMinimum(close,0,total_bars_2); if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value) { //CREATE LINES TO MARK RSI AND CORRESPONDING CANDLE LOW ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value); ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0); ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value); ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value); ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack); //19 LOGIC TO GET THE NEW RSI LOW if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value) { new_rsi_low_value = rsi_buffer[1]; new_rsi_low_time = time[1]; new_corresponding_low_value = close[1]; } //CONDITION FOR HIDDEN BULLISH DIVERGENCE if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value) { ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value); ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value); ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value); ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL"); ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack); //for candle ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value); ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value); ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value); ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL"); ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack); } } //LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT else if((close[0] < ma_buffer[0]) || (close[minimum_value_low] < corresponding_low_value)) { ObjectDelete(ChartID(),"RSI LOW VALUE"); ObjectDelete(ChartID(),"C-CANDLE"); ObjectDelete(ChartID(),"C-CANDLE LINE"); ObjectDelete(ChartID(),"RSI LOW LINE"); ObjectDelete(ChartID(),"C-CANDLE TEXT"); ObjectDelete(ChartID(),"RSI LOW TEXT"); ObjectDelete(ChartID(),"RSI LOW TREND LINE"); ObjectDelete(ChartID(),"L"); ObjectDelete(ChartID(),"LL"); ObjectDelete(ChartID(),"C-CANDLE TREND LINE"); ObjectDelete(ChartID(),"CL"); ObjectDelete(ChartID(),"RSI LOW TEXT"); ObjectDelete(ChartID(),"CLL"); } }
Explicación:
Las posibles divergencias se pueden encontrar utilizando el rsi_low_value, que registra el valor RSI más bajo dentro de un rango determinado. Dado que los gráficos de líneas se basan en los precios de cierre, la compatibilidad con ellos queda garantizada por el corresponding_low_value, que es el precio de cierre de la vela en la que se produce el mínimo del RSI. Al convertirlo en un gráfico de líneas, esta alineación hace que los mínimos del RSI y los puntos de precio asociados parezcan lógicos. Para determinar si el precio está por encima o por debajo de la media móvil (MA), que con frecuencia valida las tendencias, el corresponding_low_ma mantiene el valor de la MA en la misma ubicación. Por último, la función rsi_low_time permite crear objetos como líneas de tendencia o etiquetas asociadas a velas concretas registrando la hora exacta del mínimo del RSI.
El siguiente paso es identificar el último mínimo significativo del RSI. Esto implica buscar patrones específicos en los valores del RSI en los que haya al menos un movimiento bajista seguido de tres movimientos alcistas consecutivos.
- El código analiza las últimas 30 barras en busca de un mínimo significativo del RSI.
- Condición clave:
- El valor del RSI en i+3 debe caer primero (movimiento bajista de i+4 a i+3).
- Después de eso, debe mostrar tres aumentos consecutivos (i+3 → i+2 → i+1 → i).
- Una vez detectado este patrón, se guardan el RSI bajo, el precio de cierre correspondiente, el valor de la media móvil y la marca de tiempo.
Esta lógica garantiza que el mínimo forme parte de un comportamiento significativo del RSI, en el que una caída del RSI va seguida de una recuperación, lo que lo convierte en un punto de inflexión significativo.
¿Por qué usar close[i+3]?
Si utiliza un gráfico de líneas, la línea se traza utilizando los precios de cierre. Al asociar los mínimos del RSI con el precio de cierre, los marcadores que cree (por ejemplo, líneas de tendencia) se alinearán correctamente con el gráfico de líneas. Esto garantiza claridad y coherencia visual.
if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value)
Esta condición comprueba varios factores para confirmar si la situación actual se ajusta a los criterios para detectar una divergencia alcista oculta:
- corresponding_low_value > corresponding_low_ma: Esto comprueba si el precio en el mínimo identificado por el RSI está por encima de la media móvil (MA). Esto sugiere que el precio está por encima de su media móvil, lo que indica que la tendencia sigue siendo relativamente fuerte o alcista.
- close[0] > ma_buffer[0]: Esto comprueba si el precio de cierre más reciente (vela actual) está por encima del valor de la media móvil, lo que refuerza que el precio sigue estando por encima de la media móvil y en una tendencia alcista.
- close[minimum_value_low] >= corresponding_low_value: Esto confirma que el precio en el mínimo significativo más reciente (el encontrado anteriormente) sigue estando igual o por encima del mínimo significativo anterior. Esto ayuda a garantizar que el precio no caiga por debajo del mínimo anterior, lo que indica mínimos más altos en el precio y respalda la interpretación alcista.
Cuando todas estas condiciones se cumplen, esto sugiere que el precio está por encima de su media móvil y ha formado un mínimo más alto, lo cual es una característica clave de una posible divergencia alcista oculta, en la que la acción del precio no confirma el impulso bajista más profundo sugerido por el RSI.
//CREATE LINES TO MARK RSI AND CORRESPONDING PRICE ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value); ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0); ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value); ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value); ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack);
Este código crea elementos visuales en el gráfico para resaltar el mínimo del RSI y su precio correspondiente, que son puntos clave para identificar posibles patrones de divergencia. En primer lugar, se trazan líneas de tendencia para marcar tanto el valor mínimo del RSI como el precio correspondiente (C-PRICE) en el mismo punto. Estas líneas, etiquetadas como «RSI LOW VALUE» y «C-CANDLE», están dibujadas en negro para mantener la claridad visual. El objetivo de estas líneas de tendencia es conectar visualmente el mínimo del RSI con el precio correspondiente, lo que le ayuda a identificar fácilmente la relación entre la acción del precio y el comportamiento del RSI.
A continuación, se crean líneas de tendencia adicionales para conectar el mínimo del RSI con el precio correspondiente en el gráfico. «C-CANDLE LINE» conecta el precio en el mínimo del RSI con el momento actual, mientras que «RSI LOW LINE» extiende el mínimo del RSI verticalmente en el gráfico. Estas líneas sirven como marcadores visuales que ayudan a analizar el movimiento tanto del RSI como del precio, especialmente cuando se busca una posible divergencia alcista o bajista. Todas estas líneas de tendencia se han establecido en color negro para mantener la coherencia y garantizar que sean claramente visibles sobre el fondo del gráfico.
Por último, el código crea etiquetas de texto para anotar el gráfico con los valores del mínimo del RSI y el precio correspondiente (mínimo de la vela). Las etiquetas «RSI LOW» y «C-PRICE» se colocan en los niveles respectivos del RSI y el precio, lo que facilita la identificación de estos puntos clave directamente en el gráfico. Estas etiquetas de texto también se han configurado en negro, lo que garantiza que destaquen y sean legibles. Al crear estos objetos, el código ayuda a los operadores a visualizar rápidamente los niveles de precios importantes y los puntos RSI, lo que facilita el análisis técnico y la identificación de divergencias ocultas.
SALIDA:

Una vez identificado el primer mínimo significativo del RSI, el siguiente paso es encontrar un nuevo mínimo más bajo del RSI. La lógica aquí es más simple que para el primer mínimo.
Lógica para el nuevo mínimo del RSI:
Para el nuevo mínimo del RSI:
- Solo debe haber un movimiento bajista, en el que el RSI disminuya durante una barra.
- Esto debe ir seguido de un movimiento alcista, en el que el RSI comience a aumentar inmediatamente después del mínimo.
if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value) { new_rsi_low_value = rsi_buffer[1]; new_rsi_low_time = time[1]; new_corresponding_low_value = close[1]; }
La primera parte del código declara variables para almacenar información sobre el nuevo mínimo del RSI y su nivel de precio correspondiente. La variable new_rsi_low_value contiene el valor del nuevo mínimo del RSI, necesario para la comparación y el análisis de la divergencia. El new_rsi_low_time almacena el momento en que se produce el nuevo mínimo del RSI, y esta marca de tiempo es importante para marcar este punto en el gráfico. Por último, new_corresponding_low_value almacena el precio asociado al nuevo mínimo del RSI, normalmente el precio de cierre, lo que ayuda a conectar los movimientos del RSI con la evolución real de los precios del mercado.
El requisito para determinar el nuevo mínimo del RSI se define en la segunda sección del código. El razonamiento garantiza que el nuevo mínimo cumple dos requisitos: un movimiento bajista (en el que el RSI cae) y un movimiento alcista (en el que el RSI comienza a subir). Además, el precio coincidente debe producir un mínimo más alto, lo que sugiere una posible divergencia alcista oculta, mientras que el nuevo mínimo del RSI debe ser inferior al mínimo anterior más importante del RSI. Para enfatizar la divergencia en el gráfico más adelante, el algoritmo asigna los nuevos valores para el mínimo del RSI, su marca de tiempo y el precio asociado si se cumplen estos criterios.
if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value) Esta condición busca una divergencia alcista oculta entre el gráfico de precios y el RSI. Cuando el precio alcanza un mínimo más alto y el RSI alcanza un mínimo más bajo, se conoce como divergencia alcista oculta.
//FOR RSI ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value); ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value); ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value); ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL"); ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack); //FOR CORRESPONDING PRICE ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value); ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value); ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value); ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL"); ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack);
Una vez identificada una divergencia alcista oculta, este bloque de código tiene como objetivo marcar gráficamente los aspectos importantes del gráfico. En el primer conjunto de acciones, el RSI es el principal énfasis. El primer mínimo importante del RSI y el mínimo posterior más bajo del RSI se representan mediante líneas de tendencia y etiquetas de texto. El código establece inicialmente una línea de tendencia entre el nuevo mínimo del RSI (new_rsi_low_value) y el mínimo original del RSI (rsi_low_value). Los operadores pueden detectar más fácilmente la tendencia bajista del RSI gracias a la línea de tendencia. En estos casos, también se añaden las etiquetas de texto «L» y «LL» para identificarlos como mínimos notables; «L» indica el mínimo inicial y «LL» indica el mínimo más bajo.
El siguiente conjunto de medidas se centra en los niveles de precios correspondientes. De forma similar al RSI, se crean líneas de tendencia y etiquetas de texto para conectar el primer mínimo de precio (corresponding_low_value) con el nuevo mínimo de precio (new_corresponding_low_value). Se espera que el precio correspondiente forme un mínimo más alto, lo cual es fundamental para la configuración de divergencia alcista oculta. El código crea una línea de tendencia entre estos dos puntos de precio y los etiqueta con «L» y «HL», donde «L» marca el primer mínimo significativo del precio y «HL» denota el mínimo más alto formado por el precio. Estos elementos visuales le ayudan a confirmar que el movimiento del precio muestra el mínimo más alto esperado, mientras que el RSI muestra un mínimo más bajo, un indicador clásico de divergencia alcista oculta.
La función ObjectDelete() se encarga de limpiar los objetos visuales del gráfico cuando las condiciones para la divergencia alcista oculta ya no son válidas. Concretamente, comprueba si el precio de cierre actual está por debajo de la media móvil (ma_buffer[0]) o si el precio de cierre mínimo desde el último mínimo significativo del RSI es inferior al mínimo de precio correspondiente (corresponding_low_value). Si se cumple cualquiera de estas condiciones, se eliminan las líneas de tendencia, las etiquetas de texto y otros objetos que marcan el RSI y los mínimos de precios dibujados anteriormente. Esto garantiza que el gráfico permanezca libre de marcadores visuales obsoletos o irrelevantes, lo que permite a los operadores centrarse en las condiciones de mercado más actuales y válidas.
SALIDA:

4.2. Encontrar divergencias bajistas ocultas
Una divergencia bajista oculta se produce cuando el RSI forma un máximo más alto mientras que el precio crea un máximo más bajo, lo que indica un posible impulso bajista a pesar de la recuperación temporal del precio. Para identificar este patrón, se detecta el último máximo del RSI utilizando una lógica específica: debe producirse después de al menos un movimiento alcista seguido de tres movimientos bajistas consecutivos, lo que indica un pico significativo en el impulso. Una vez identificado el máximo del RSI, se recuperan el precio correspondiente y el valor de la media móvil (MA) para confirmar la alineación con la tendencia bajista predominante. Una divergencia bajista oculta válida requiere que el precio máximo se mantenga por debajo de la media móvil, lo que refuerza la tendencia bajista.
A diferencia de la divergencia alcista oculta, en la que el RSI forma un mínimo más bajo y el precio crea un mínimo más alto (lo que indica un posible impulso alcista), la divergencia bajista oculta funciona en la dirección opuesta. Después de identificar el máximo del RSI, la lógica también incluye comprobar si hay un nuevo máximo del RSI que supere al primero, con el precio correspondiente formando un máximo más bajo. Esta divergencia pone de relieve el debilitamiento del impulso alcista, en contraste con la divergencia alcista oculta, que indica un debilitamiento del impulso bajista. Estas distinciones garantizan un enfoque estructurado para detectar y diferenciar eficazmente ambos tipos de divergencias.
Ejemplo:
//DECLEAR VARIBLE ROR RSI HIGH AND CORESPONDING PRICE double rsi_high_value; double corresponding_high_value; double corresponding_high_ma; datetime rsi_high_time; //DECLEAR VARIABLE FOR THE MAXIMUM CORRESPONDING PRICE VALUE int maximum_value_high; // DECLEAR VARIBLE ROR NEW RSI HIGH AND CORESPONDING PRICE double new_rsi_high_value; datetime new_rsi_high_time; double new_corresponding_high_value;
//LOOP THROUGH THE LAST 30 BARS ON THE CHART for(int i = 0; i < 30; i++) { //PREVENT ARRAY OUT OF RANGE ERROR if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30)) { //LOGIC TO IDENTIFY THE LATEST RSI HIGH if(rsi_buffer[i+4] < rsi_buffer[i+3] && rsi_buffer[i+2] < rsi_buffer[i+3] && rsi_buffer[i+1] < rsi_buffer[i+2] && rsi_buffer[i] < rsi_buffer[i+1]) { //GETTING LATEST RSI HIGH, CORRESPONDING PRICE, CORRESPONDING MA VALUE, and RSI TIME rsi_high_value = rsi_buffer[i+3]; corresponding_high_value = close[i+3]; corresponding_high_ma = ma_buffer[i+3]; rsi_high_time = time[i+3]; break; } } } //TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI HIGH int total_bars_3 = 0; total_bars_3 = Bars(_Symbol,PERIOD_CURRENT,time[0],rsi_high_time); //MAXIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW maximum_value_high = ArrayMaximum(close,0,total_bars_3); if(corresponding_high_value < corresponding_high_ma && close[0] < ma_buffer[0] && close[maximum_value_high] <= corresponding_high_value) { //CREATE LINES TO MARK RSI AND CORRESPONDING PRICE ObjectCreate(ChartID(),"RSI HIGH VALUE",OBJ_TREND,1,rsi_high_time,rsi_high_value,TimeCurrent(),rsi_high_value); ObjectCreate(ChartID(),"C-CANDLE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,TimeCurrent(),corresponding_high_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI HIGH VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE HIGH",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI HIGH AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,rsi_high_time,0); ObjectCreate(ChartID(),"RSI HIGH LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,rsi_high_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI HIGH LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI HIGH AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT HIGH",OBJ_TEXT,0,TimeCurrent(),corresponding_high_value); ObjectSetString(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI HIGH TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_high_value); ObjectSetString(ChartID(),"RSI HIGH TEXT",OBJPROP_TEXT,"RSI HIGH"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI HIGH TEXT",OBJPROP_COLOR,clrBlack); //LOGIC TO GET THE NEW RSI HIGH if(rsi_buffer[0] < rsi_buffer[1] && rsi_buffer[1] > rsi_buffer[2] && rsi_buffer[1] > rsi_high_value && close[1] < corresponding_high_value) { new_rsi_high_value = rsi_buffer[1]; new_rsi_high_time = time[1]; new_corresponding_high_value = close[1]; } if(rsi_high_value < new_rsi_high_value && corresponding_high_value > new_corresponding_high_value) { //for rsi ObjectCreate(ChartID(),"RSI HIGH TREND LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,new_rsi_high_time,new_rsi_high_value); ObjectCreate(ChartID(),"H",OBJ_TEXT,1,rsi_high_time,rsi_high_value); ObjectSetString(ChartID(),"H",OBJPROP_TEXT,"H"); ObjectSetInteger(ChartID(),"H",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI HIGH TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"H",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"HH",OBJ_TEXT,1,new_rsi_high_time,new_rsi_high_value); ObjectSetString(ChartID(),"HH",OBJPROP_TEXT,"HH"); ObjectSetInteger(ChartID(),"HH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"HH",OBJPROP_COLOR,clrBlack); //for candle ObjectCreate(ChartID(),"C-CANDLE TREND LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,new_rsi_high_time,new_corresponding_high_value); ObjectCreate(ChartID(),"CH",OBJ_TEXT,0,rsi_high_time,corresponding_high_value); ObjectSetString(ChartID(),"CH",OBJPROP_TEXT,"H"); ObjectSetInteger(ChartID(),"CH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CH",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLH",OBJ_TEXT,0,new_rsi_high_time,new_corresponding_high_value); ObjectSetString(ChartID(),"CLH",OBJPROP_TEXT,"LH"); ObjectSetInteger(ChartID(),"CLH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLH",OBJPROP_COLOR,clrBlack); } } //LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT else if((close[0] > ma_buffer[0]) || (close[maximum_value_high] > corresponding_high_value)) { ObjectDelete(ChartID(),"RSI HIGH VALUE"); ObjectDelete(ChartID(),"C-CANDLE HIGH"); ObjectDelete(ChartID(),"C-CANDLE LINE HIGH"); ObjectDelete(ChartID(),"RSI HIGH LINE"); ObjectDelete(ChartID(),"C-CANDLE TEXT HIGH"); ObjectDelete(ChartID(),"RSI HIGH TEXT"); ObjectDelete(ChartID(),"RSI HIGH TREND LINE"); ObjectDelete(ChartID(),"H"); ObjectDelete(ChartID(),"HH"); ObjectDelete(ChartID(),"C-CANDLE TREND LINE HIGH"); ObjectDelete(ChartID(),"CH"); ObjectDelete(ChartID(),"CLH"); }
Explicación:
Este segmento de código está diseñado para identificar divergencias bajistas ocultas mediante la detección de máximos del RSI y sus máximos de precio correspondientes, siguiendo una lógica similar a la del punto 4.1. Encontrar divergencias alcistas ocultas. No se añadirá mucho más énfasis aquí, ya que la mayor parte de la lógica ya se ha explicado en el apartado 4.1. Encontrar divergencias alcistas ocultas; la única diferencia es que ahora la atención se centra en el escenario opuesto. Aunque el enfoque subyacente sigue siendo coherente, esta implementación se centra en el impulso bajista, prestando atención a los máximos del RSI en lugar de a los mínimos.
La primera parte del código declara variables como rsi_high_value, corresponding_high_value y corresponding_high_ma para almacenar el valor alto del RSI, su precio correspondiente y el valor de la media móvil (MA), respectivamente. Se utilizan para identificar el último máximo del RSI recorriendo las últimas 30 barras y aplicando la condición de un movimiento alcista seguido de tres movimientos bajistas consecutivos. Una vez que se encuentra un máximo de RSI, se recuperan el precio y el valor de la media móvil correspondientes, y se registra la marca de tiempo (rsi_high_time). Un paso de validación garantiza que el precio máximo correspondiente esté por debajo de la media móvil, lo que refuerza la tendencia bajista.

A continuación, detectamos un nuevo máximo del RSI, donde el RSI forma un máximo más alto y el precio forma un máximo más bajo (LH). Esto cumple los criterios de divergencia bajista oculta, lo que indica un debilitamiento del impulso alcista en la evolución del precio. Si se cumple esta condición, se crean líneas de tendencia y etiquetas para marcar visualmente los máximos del RSI (H y HH) y sus máximos de precio correspondientes (H y LH) en el gráfico. Estos marcadores visuales ayudan a los operadores a identificar rápidamente posibles puntos de divergencia bajista ocultos.
El código también incluye lógica para eliminar objetos cuando las condiciones para la divergencia bajista oculta se invalidan, como cuando el precio actual cierra por encima de la media móvil o el precio máximo desde el máximo identificado del RSI supera el valor máximo correspondiente. Esto garantiza que el gráfico permanezca limpio y refleje solo señales de divergencia válidas.

5. Incorporación del oscilador estocástico y ejecución de operaciones
El oscilador estocástico se presenta como la última herramienta de confirmación ahora que entendemos cómo utilizar la media móvil y el RSI para encontrar divergencias alcistas y bajistas ocultas. Al garantizar que la divergencia se corresponda con los cambios en el impulso del mercado, el oscilador sirve para aumentar la precisión de la entrada en la operación. Con unas estrictas directrices de gestión del tiempo y del riesgo, la justificación de la confirmación de ambos tipos de divergencia depende del rendimiento del oscilador en relación con los niveles de sobrecompra y sobreventa.
5.1. Incorporación del oscilador estocástico
Tras la confirmación de la divergencia alcista oculta por parte del RSI y el movimiento del precio, observaremos si el oscilador estocástico cae por debajo del nivel de sobreventa (20) y luego vuelve a subir por encima de él. Este cruce debe producirse 11 compases después de detectarse la divergencia para validar la señal. Si se confirma, se iniciará una operación de compra, con el stop-loss (SL) en el último mínimo oscilante. El take-profit (TP) se establecerá mediante una relación riesgo-recompensa (RRR) predefinida para garantizar una gestión disciplinada de las operaciones.
Ejemplo:
if(total_bars < 11) { if(stoch_buffer_k[0] < 20 && stoch_buffer_k[1] > 20 && currentBarTime != lastTradeBarTime && totalPositions < 1) { // } }
Explicación:
Este código confirma una configuración de divergencia alcista oculta al validarla con el oscilador estocástico. En primer lugar, calcula el número de barras desde el mínimo del RSI (new_rsi_low_time) utilizando la función Bars y se asegura de que la configuración de la operación siga siendo válida dentro de una ventana de 11 barras «(if(total_bars < 11))». Dentro de este periodo, comprueba si la línea %K del oscilador estocástico ha cruzado por encima del nivel de sobreventa de 20 (stoch_buffer_k[1] < 20 y stoch_buffer_k[0] > 20). Este cruce confirma un posible impulso alcista, lo que refuerza la señal alcista. Si se cumplen ambas condiciones, el código procede a ejecutar la lógica de la operación, asegurándose de que se cumplan todos los criterios para una divergencia alcista oculta.
Del mismo modo, esperaremos a que el oscilador estocástico suba por encima del nivel de sobrecompra (80) y luego vuelva a cruzar por debajo para confirmar la divergencia bajista oculta. Además, este cruce bajista debe producirse dentro de las 11 barras siguientes a la detección de la divergencia. Si se verifica, se realizará una transacción de venta, calculando el TP utilizando el RRR especificado y estableciendo el SL en el máximo más reciente. Al combinar estos procedimientos con el método original de detección de divergencias, se garantiza que las operaciones solo se realicen cuando exista una gestión de riesgos evidente y una buena alineación del impulso.
Ejemplo:
int total_bars = Bars(_Symbol,PERIOD_CURRENT,new_rsi_high_time,TimeCurrent()); if(total_bars < 11) { if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80) { // } }
Explicación:
El número de barras entre la hora actual (TimeCurrent()) y la hora en que se descubrió el nuevo máximo del RSI (new_rsi_high_time) viene determinado por la variable total_bars. Garantiza que la configuración sigue activa y no ha caducado si hay menos de once barras. A continuación, el código determina si la línea %K del oscilador estocástico (stoch_buffer_k) ha pasado por debajo del umbral de sobrecompra (80) en la barra actual (stoch_buffer_k[0] < 80) y por encima de él en la barra anterior (stoch_buffer_k[1] > 80). Se verifica la configuración de la operación y, si se cumplen ambos requisitos, el código comienza a ejecutar la lógica necesaria para introducir la transacción.
5.2. Ejecución de operaciones
5.2.1. Swing bajo y swing alto
Esta sección se centrará en todos los componentes esenciales necesarios para ejecutar operaciones tras confirmar una divergencia válida y una señal del oscilador estocástico. Garantiza que las operaciones se ejecuten de forma sistemática y con una gestión adecuada del riesgo.
Para definir el stop-loss (SL), primero debemos determinar el último máximo o mínimo. El último mínimo será el SL para una divergencia alcista oculta, y el último máximo para una divergencia bajista oculta. Esto garantiza que, dependiendo del comportamiento previo de los precios, las operaciones tengan un umbral de riesgo definido con precisión.
Ejemplo:
double last_high; double last_low; //LOGIC FOR LAST HIGH for(int i = 0; i < 30; i++) { if(i < 30 && i+1 < 30) { if(close[i] < open[i] && close[i+1] > open[i+1]) { last_high = MathMax(high[i],high[i+1]); break; } } } //LOGIC FOR LAST LOW for(int i = 0; i < 30; i++) { if(i < 30 && i+1 < 30) { if(close[i] > open[i] && close[i+1] < open[i+1]) { last_low = MathMin(low[i],low[i+1]); break; } } } }
Explicación:
Para determinar los máximos y mínimos más recientes, este código examina las 30 barras anteriores. Determina el precio más alto (last_high) detectando una vela bajista seguida de una alcista, y el precio más bajo (last_low) detectando una vela alcista seguida de una bajista. A la hora de determinar los niveles de stop-loss, estas cifras son esenciales.
5.2.2. Gestión de la ejecución de operaciones y prevención del exceso de operaciones
El sistema solo permitirá una posición abierta a la vez para desalentar el trading excesivo y reducir riesgos innecesarios. El código se asegurará de que no haya posiciones abiertas antes de realizar una nueva transacción. Esto garantiza un comportamiento comercial mejor controlado al impedir que el sistema complete transacciones con cada señal. Para ejecutar operaciones de forma eficaz, es necesario incluir la biblioteca #include <Trade/Trade.mqh>, que permite acceder a la clase CTrade para la gestión de operaciones.
Ejemplo:
#include <Trade/Trade.mqh> CTrade trade; input int MagicNumber = 9097; //MAGIC NUMBER //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //SET MAGIC NUMBER trade.SetExpertMagicNumber(MagicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Getting total positions int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(ChartID())) { totalPositions++; } } }
Explicación:
El código comienza incluyendo la biblioteca Trade.mqh, que proporciona acceso a las funciones de trading en MetaTrader. A continuación, se instancia el objeto CTradeobject para gestionar las operaciones comerciales. El número mágico se define como una entrada, establecida en 9097 en este caso, que identifica de forma única las operaciones abiertas por este asesor experto (EA) específico para distinguirlas de otras en la misma cuenta. En la función OnInit(), el número mágico se aplica al objeto de operación utilizando SetExpertMagicNumber(MagicNumber). Esto garantiza que el EA reconozca sus operaciones al realizar acciones como abrir, modificar o cerrar posiciones.
En la función OnTick(), el código comprueba el número total de posiciones abiertas para el símbolo en el gráfico actual iterando a través de todas las posiciones utilizando PositionsTotal(). Recupera el número de ticket de cada posición con PositionGetTicket(i) y verifica si el número mágico de la posición coincide con el número mágico del EA utilizando PositionGetInteger(POSITION_MAGIC). Además, el código comprueba si la posición pertenece al símbolo del gráfico actual utilizando PositionGetString(POSITION_SYMBOL). Si ambas condiciones son ciertas, incrementa la variable totalPositions, llevando un registro del número de operaciones abiertas relacionadas con el EA y el símbolo específicos, lo que garantiza que solo haya una posición abierta a la vez.
5.2.3. Gestión de riesgos
El sistema de negociación garantiza un enfoque metódico para la gestión de riesgos y la maximización de la rentabilidad mediante la integración de estos dos elementos esenciales. Esta estrategia favorece el éxito a largo plazo en el mercado y ayuda a proteger la cuenta de operaciones. El objetivo principal de esta sección será la implementación de un sólido sistema de gestión de riesgos que permita a los operadores indicar la cantidad exacta de dinero que desean arriesgar en cada operación. Para asegurarse de que las posibles ganancias superen los posibles riesgos, también describirá cómo calcular la relación riesgo-recompensa (RRR) para cada operación.
Ejemplo:
//INPUTS input double risk_amount = 20; // $ PER TRADE input double rrr = 4; //RRR input int MagicNumber = 9097; //MAGIC NUMBER double take_profit; double points_risk; double lot_size; //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //BULLISH HIDDEN DIVERGENCE if(stoch_buffer_k[0] > 20 && stoch_buffer_k[1] < 20 && currentBarTime != lastTradeBarTime && totalPositions < 1) { take_profit = ((ask_price - last_low) * rrr) + ask_price; points_risk = ask_price - low[0]; lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk); trade.Buy(lot_size,_Symbol,ask_price,last_low,take_profit); lastTradeBarTime = currentBarTime; } //BEARISH HIDDEN DIVERGENCE if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80 && currentBarTime != lastTradeBarTime && totalPositions < 1) { take_profit = MathAbs(((last_high - ask_price) * rrr) - ask_price); points_risk = high[0] - ask_price; lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk); trade.Sell(lot_size,_Symbol,ask_price,last_high,take_profit); lastTradeBarTime = currentBarTime; } } //+-----------------------------------------------------------------------+ //| Function to calculate the lot size based on risk amount and stop loss | //+-----------------------------------------------------------------------+ double CalculateLotSize(string symbol, double riskAmount, double stopLossPips) { // Get symbol information double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); // Calculate pip value per lot double pipValuePerLot = tickValue / point; // Calculate the stop loss value in currency double stopLossValue = stopLossPips * pipValuePerLot; // Calculate the lot size double lotSize = riskAmount / stopLossValue; // Round the lot size to the nearest acceptable lot step double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); lotSize = MathFloor(lotSize / lotStep) * lotStep; // Ensure the lot size is within the allowed range double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); return lotSize; }
Explicación:
Basándose en una cantidad de riesgo determinada (en dólares) y la distancia de stop-loss (en pips), la función CalculateLotSize calcula el tamaño de lote adecuado para una operación. El tamaño del lote se determina dividiendo el importe del riesgo por el valor del stop loss, convirtiendo la distancia del stop loss en un valor monetario y calculando el valor del pip por lote utilizando datos específicos del símbolo. El resultado se modifica para cumplir con los requisitos del bróker en cuanto a los tamaños mínimos y máximos de los lotes, así como los incrementos de los lotes. Para que esta función sea reutilizable en todo el código y para que la gestión de riesgos sea uniforme y eficaz, debe declararse en el área global.
Para una operación de compra, la línea %K del oscilador estocástico debe subir por encima de 20 después de haber caído por debajo de ese nivel. Las operaciones solo se ejecutan si no hay posiciones abiertas (totalPositions < 1) y la barra actual difiere de la última barra negociada. El stop-loss se establece en el último mínimo oscilante, y el take-profit se calcula utilizando la relación riesgo-recompensa (RRR) y la distancia entre el precio de venta y el mínimo oscilante. El tamaño del lote se determina utilizando la función CalculateLotSize, basada en el importe del riesgo y la distancia de stop-loss. Después de la ejecución, lastTradeBarTime se actualiza para evitar operaciones duplicadas en la misma barra.

La línea %K del oscilador debe estar primero por encima del nivel 80 (sobrecompra) antes de cruzar por debajo de él para indicar una operación de venta. Del mismo modo, la transacción solo se ejecutará si no hay posiciones abiertas y la barra es nueva. La relación riesgo-recompensa y la diferencia entre el precio de venta y el último máximo (last_high) se utilizan para determinar la toma de ganancias (take_profit). En el último máximo, se establece el stop-loss. La función CalculateLotSize se utiliza una vez más para determinar el tamaño del lote. Para evitar operaciones duplicadas, la función de venta actualiza lastTradeBarTime y coloca la orden de venta.
Al seguir los límites de transacción, una gestión de riesgos adecuada y las señales de confirmación, este código garantiza una negociación disciplinada y aumenta la fiabilidad de la estrategia de negociación.

Conclusión
Este artículo ofrece una guía paso a paso para identificar y operar con divergencias alcistas y bajistas ocultas utilizando MQL5, integrando indicadores como la media móvil, el RSI y el oscilador estocástico. Se hizo hincapié en la gestión de riesgos, la ejecución de operaciones y la automatización para ayudar a los operadores a detectar estos patrones de manera eficaz en el mercado. Este contenido educativo tiene como objetivo enseñar los fundamentos de la detección de divergencias y el trading sistemático. Se incluye un código fuente muy detallado, con comentarios exhaustivos, que servirá como referencia práctica para implementar los conceptos tratados.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16956
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.
Introducción a MQL5 (Parte 13): Guía para principiantes sobre cómo crear indicadores personalizados (II)
Redes neuronales en el trading: Detección adaptativa de anomalías del mercado (DADA)
Particularidades del trabajo con números del tipo double en MQL4
De principiante a experto: Indicador de fuerza de soporte y resistencia (SRSI)
- 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
Muchas gracias.
De nada, gracias por sus amables palabras.