Redefiniendo los indicadores de MQL5 y MetaTrader 5
Introducción
MQL5 ya no es un lenguaje de programación sencillo como solía ser para crear robots de trading simples. El lenguaje ha madurado hoy en día; ahora se pueden crear programas de negociación de mucha complejidad, implementarlos y probarlos de una manera mucho más robusta y conveniente.
Todos hemos utilizado indicadores "nativos" o "personalizados" en algún momento de nuestra trayectoria como operadores. MetaTrader5 tiene una interfaz sencilla para cargar y adjuntar el indicador al gráfico, lo que permite a los operadores (principalmente operadores manuales) analizar los mercados cómodamente utilizando indicadores; sin embargo, cuando se trata de trading algorítmico, lo que importa no es lo que se ve en el gráfico, sino los cálculos del indicador.
En el trading manual, por ejemplo, uno podría necesitar observar la línea del indicador de media móvil para detectar una tendencia si eso es lo que busca, pero en el trading algorítmico, podríamos buscar una tendencia positiva basándonos en si el precio de cierre está por encima o por debajo del valor de la media móvil calculada sobre un cierto número de barras.
Cuando se trata de trading algorítmico para utilizar un indicador determinado, por ejemplo una media móvil simple (Simple Moving Average, SMA) de período 20, hay que:
int sma_handle; //Declare an indicator handle //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { sma_handle = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE); //Initialize the indicator inside the OnInit function //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- double sma_buffer[]; CopyBuffer(sma_handle, 0, 0, 1, sma_buffer); //Copy the indicator buffers inside the Ontick function }
Este enfoque es práctico y funciona bien, por decir lo menos, pero resulta rudimentario y no nos ofrece la flexibilidad y el control necesarios para decidir qué información se puede transmitir al indicador para realizar cálculos inmediatos.
Basándose en este enfoque, se afirma que el indicador de media móvil (Moving Average, MA) solo debe aplicarse a una de las constantes de precio proporcionadas en la documentación.
| ID | Descripción |
|---|---|
| PRICE_CLOSE | Precio de cierre |
| PRICE_OPEN | Precio de apertura |
| PRICE_HIGH | Precio máximo para el período |
| PRICE_LOW | Precio mínimo para el período |
| PRICE_MEDIAN | Precio medio, (máximo + mínimo)/2 |
| PRICE_TYPICAL | Precio típico, (máximo + mínimo + cierre)/3 |
| PRICE_WEIGHTED | Precio promedio, (máximo + mínimo + cierre + cierre)/4 |
Por no mencionar que estás limitado a los métodos/técnicas de suavizado descritos en la documentación.
| ID | Descripción |
|---|---|
| MODE_SMA | Promedio simple |
| MODE_EMA | Promedio exponencial |
| MODE_SMMA | Promedio suavizado |
| MODE_LWMA | Promedio ponderado lineal |
¿Qué ocurre cuando se desea probar algo fuera de lo común? Por ejemplo, si se desea calcular la media móvil de la diferencia entre los precios máximos y mínimos, esto resulta imposible con el enfoque actual, ya que estamos restringidos y limitados a lo que podemos hacer con los indicadores.
Entiendo que la mayoría de los indicadores son lo que son en función de cómo se definieron y derivaron matemáticamente, pero ¿no sería fantástico poder introducir diferentes datos y parámetros en los indicadores con el fin de observar nuevos patrones en el mercado?
Inspirado en la biblioteca Python Technical Analysis (TA-Lib), este innovador enfoque tiene como objetivo proporcionar a los operadores y desarrolladores de sistemas algorítmicos un mayor control sobre la información que se introduce en el indicador para realizar cálculos inmediatos.
En este artículo, implementaremos algunos indicadores (los más utilizados) utilizando este enfoque plug-and-play.
Agrupados por categorías.
Indicadores de tendencia
- Indicador de media móvil simple
- Indicador de media móvil exponencial
- Bandas de Bollinger
- El SAR parabólico
- Desviación estándar
Osciladores
- La convergencia/divergencia de la media móvil (Moving Average Convergence Divergence, MACD)
- Índice de fuerza relativa (Relative Streng Index, RSI)
- Osciladores estocásticos
- Rango verdadero promedio (Average True Range, ATR)
- Indicador de impulso (Momentum)
Bill Williams
- Oscilador acelerador (Accelerator Oscillator, AC)
- Oscilador Awesome (Awesome Oscillator, AO)
Indicador de media móvil simple (Simple Moving Average, SMA)
Este es uno de los indicadores técnicos más utilizados. Calcula el promedio de una serie de precios durante un período de tiempo determinado.

Dado por la fórmula.
![]()
Donde:
-
son los precios (por ejemplo, los precios de cierre) durante
periodos. -
es el período de la SMA.
Podemos implementar fácilmente esta fórmula en una función vectorizada.
Archivo: ta-lib.mqh
vector CTrendIndicators::SMA(const vector &price, uint period, uint shift = 0) { uint size = (uint)price.Size(); if(!CheckShiftPeriod(size, period, shift)) return price; //--- vector ma(size); ma.Fill(NaN); for(uint i = shift + period - 1; i < size; i++) //Loop through all the prices considering the period and shift { double sum = 0; for(uint j = i - period + 1; j <= i; j++) sum += price[j]; //sum of the prices ma[i] = sum / period; //divided by period to find the mean of that period } return ma; }
Así es como se calcula un indicador de media móvil simple, sin buffers desplegados, sin comprobación de errores MQL5 al cargar el indicador para un símbolo concreto, etc., lo cual puede resultar tedioso.
Este enfoque minimalista solo comprueba si los valores shift y period son correctos; si lo son, la función continúa con los cálculos del indicador y devuelve los valores calculados en forma de vector. Decidí utilizar el formato vectorial para obtener un mayor control y flexibilidad sobre el resultado final.
Dado que todas las funciones de nuestras clases Indicadores son estáticas, podemos acceder fácilmente a los valores de los indicadores en nuestros programas MQL5. Por ejemplo, en un script sencillo.
Archivo: Custom Indicators test script.mq5
#include <ta-lib.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; int period = 3; Print("SMA values: ", CTrendIndicators::SMA(prices, period)); }
Resultados:
2025.01.21 09:57:35.727 Custom Indicators test script (EURUSD,H1) SMA values: [nan,nan,2,3,4,5,6,7,8,9]
De esta manera, pudimos calcular el indicador de media móvil simple a partir de un vector de valores. Más adelante, analizaremos cómo representar gráficamente estos valores para obtener un indicador visual.
Indicador de media móvil exponencial (Exponential Moving Average, EMA)
La EMA se calcula como el promedio de una serie de precios durante un número específico de períodos, al igual que la SMA, pero otorga más peso a los precios recientes, lo que la hace más sensible a los cambios de precios en comparación con la media móvil simple (Simple Moving Average, SMA).

Dado por la fórmula.
Donde:
-
es la EMA en el momento t. - Pt es el precio en el momento t.
-
es la media móvil exponencial (EMA) del período anterior. -
es el factor de suavizado (donde n es el periodo)
Podemos implementarlo en MQL5 de la siguiente manera:
Archivo: ta-lib.mqh
vector CTrendIndicators::EMA(const vector &price, uint period, uint shift = 0) { uint size = (uint)price.Size(); if(!CheckShiftPeriod(size, period, shift)) return price; //--- double alpha = 2.0 / (period + 1.0); // Smoothing factor vector res(size); res.Fill(NaN); // Initialize the EMA with the SMA of the first period vector sma = SMA(price, period, shift); res[period - 1 + shift] = sma[period - 1 + shift]; // Calculate EMA for the rest of the prices for(ulong i = period + shift; i < size; i++) res[i] = alpha * price[i] + (1 - alpha) * res[i - 1]; return res; }
Podemos obtener los valores calculados del indicador de forma similar a como lo hicimos para la SMA.
Archivo: Custom Indicators test script.mq5
void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; int period = 3; Print("EMA values: ", CTrendIndicators::EMA(prices, period)); }
Resultados:
2025.01.21 10:19:54.291 Custom Indicators test script (EURUSD,H1) EMA values: [nan,nan,2,3,4,5,6,7,8,9]
Hemos calculado los indicadores de media móvil simple (SMA) y media móvil exponencial (EMA) por separado, a diferencia de cómo se calculan dentro del indicador iMA integrado, que ofrece la opción de elegir el método de suavizado que prefiera.
Este enfoque separado requiere escribir líneas de código adicionales para implementar todos los métodos de suavizado de los indicadores de media móvil.
Las funciones para métodos de suavizado como MODE_SMMA (promedio suavizado) y MODE_LWMA (promedio ponderado lineal) aún no se han implementado en la biblioteca, ya que creo que no se utilizan tan comúnmente como sus homólogos (los métodos de suavizado SMA y EMA).
El indicador de Bandas de Bollinger
Se trata de un indicador de volatilidad compuesto por tres bandas.

- La banda media es una media móvil simple (SMA) de los precios de cierre durante un período específico.

- La banda superior es la banda media más un múltiplo de la desviación estándar de los precios de cierre durante el mismo período.

Donde:
-
es normalmente 2 (el ajuste estándar para las Bandas de Bollinger), -
es el período para los cálculos de la media móvil simple (SMA) y la desviación estándar.
Dado que este indicador tiene que devolver tres (3) valores unidimensionales (1D), conocidos como buffers cuando se utiliza el indicador integrado de Bandas de Bollinger, hagamos que esta función devuelva una estructura de 3 vectores.
Archivo: ta-lib.mqh
struct BB_res_struct { vector upper_band; vector lower_band; vector middle_band; };
BB_res_struct CTrendIndicators::BollingerBands(const vector &price, uint period, uint shift = 0, double k = 2.0) { uint size = (uint)price.Size(); BB_res_struct res; //--- Check for valid parameters if(!CheckShiftPeriod(size, period, shift)) return res; //--- Initialize vectors res.upper_band.Resize(size); res.lower_band.Resize(size); res.upper_band.Fill(NaN); res.lower_band.Fill(NaN); //--- Calculate the middle band (SMA) res.middle_band = SMA(price, period, shift); //--- Calculate the upper and lower bands for(uint i = shift + period - 1; i < size; i++) { double sum_squared_diff = 0; for(uint j = i - period + 1; j <= i; j++) { sum_squared_diff += MathPow(price[j] - res.middle_band[i], 2); } double std_dev = MathSqrt(sum_squared_diff / period); res.upper_band[i] = res.middle_band[i] + (k * std_dev); res.lower_band[i] = res.middle_band[i] - (k * std_dev); } return res; }
A continuación, se explica cómo implementar fácilmente el indicador de Bandas de Bollinger y obtener sus valores.
Archivo: Custom Indicators test script.mq5
void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; int period = 3; BB_res_struct bb; bb = CTrendIndicators::BollingerBands(prices,period,0,2); Print("BB upper: ",bb.upper_band); Print("BB middle: ",bb.middle_band); Print("BB lower: ",bb.lower_band); }
Resultados:
RL 0 11:39:21.000 Custom Indicators test script (EURUSD,H1) BB upper: [nan,nan,3.632993161855452,4.632993161855453,5.632993161855453,6.632993161855453,7.632993161855453,8.632993161855453,9.632993161855453,10.63299316185545] RO 0 11:39:21.000 Custom Indicators test script (EURUSD,H1) BB middle: [nan,nan,2,3,4,5,6,7,8,9] FF 0 11:39:21.000 Custom Indicators test script (EURUSD,H1) BB lower: [nan,nan,0.3670068381445479,1.367006838144548,2.367006838144548,3.367006838144548,4.367006838144547,5.367006838144547,6.367006838144547,7.367006838144547]
El SAR parabólico (Stop and Reverse)
Se trata de un indicador que sigue las tendencias y se utiliza para identificar posibles reversiones en el mercado. Coloca puntos por encima o por debajo del precio en función de la dirección de la tendencia.

Dado por la fórmula.
Para tendencia alcista
![]()
Para la tendencia bajista
![]()
Donde:
-
= El valor SAR del siguiente periodo -
= Factor de aceleración (comienza con un valor predeterminado, por ejemplo, 0,02, y aumenta en 0,02 cada vez que se alcanza un nuevo máximo/mínimo). Hasta un valor máximo de 0,2) -
= Punto extremo (máximo más alto en una tendencia alcista, mínimo más bajo en una tendencia bajista)
Este indicador se puede implementar de la siguiente manera en MQL5.
Archivo: ta-lib.mqh
vector CTrendIndicators::ParabolicSAR(const vector &high, const vector &low, const vector &close, double step = 0.02, double max = 0.2) { uint size = (uint)close.Size(); vector psar(size); psar.Fill(NaN); // Initialize variables double AF = step; // Acceleration Factor double EP = high[0]; // Extreme Point double SAR = low[0]; // Initial SAR bool isUptrend = true; // Assume uptrend at the start // Calculate Parabolic SAR for(uint i = 0; i < size; i++) { // Update SAR if(isUptrend) SAR = SAR + AF * (EP - SAR); else SAR = SAR + AF * (SAR - EP); // Determine if trend changes if(isUptrend && SAR > low[i]) { // Switch to downtrend isUptrend = false; SAR = EP; // Reset SAR to the most recent EP EP = low[i]; // Reset EP AF = step; // Reset AF } else if(!isUptrend && SAR < high[i]) { // Switch to uptrend isUptrend = true; SAR = EP; // Reset SAR to the most recent EP EP = high[i]; // Reset EP AF = step; // Reset AF } // Update EP and AF if(isUptrend) { if(high[i] > EP) { EP = high[i]; AF = MathMin(AF + step, max); } } else { if(low[i] < EP) { EP = low[i]; AF = MathMin(AF + step, max); } } // Store the SAR value psar[i] = SAR; } return psar; }
A diferencia de los dos indicadores de seguimiento de tendencias anteriores, que pueden recibir un único vector como entrada de precio, el SAR parabólico tiene en cuenta tres (3) valores de precio (valores máximos, mínimos y de cierre).
Archivo: Custom Indicators test script.mq5
void OnStart() { vector close = {1,2,3,4,5,6,7,8,9,10}; vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5}; vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5}; Print("Parabolic SAR values: ", CTrendIndicators::ParabolicSAR(high,low,close,0.02,0.2)); }
Resultados:
2025.01.21 11:11:03.525 Custom Indicators test script (EURUSD,H1) Parabolic SAR values: [1.5,0.5,0.54,0.6584000000000001,0.8888960000000001,1.25778432,1.782005888,2.46816518144,3.3126220560384,4.302602527072256]
El indicador de desviación estándar
Este indicador mide la cantidad de variación o dispersión del precio con respecto a su media. Este indicador se utiliza a menudo para evaluar la volatilidad del mercado.

Un valor alto de la desviación estándar indica una alta volatilidad, mientras que un valor bajo de la desviación estándar indica una baja volatilidad.
Dado por la fórmula.
![]()
Donde:
-
= Desviación estándar - n = Período
-
= Precio -
= Media de los puntos de datos 
Podemos implementar este indicador en MQL5 de la siguiente manera:
Archivo: ta-lib.mqh
vector CTrendIndicators::StandardDeviation(const vector &price, uint period, uint shift = 0) { uint size = (uint)price.Size(); // Check if the period and shift are valid if(!CheckShiftPeriod(size, period, shift)) return price; // Initialize standard deviation vector vector std_dev(size); std_dev.Fill(NaN); // Loop through the price data for(uint i = shift + period - 1; i < size; i++) { double sum = 0.0; double sum_sq_diff = 0.0; // Calculate mean for(uint j = i - period + 1; j <= i; j++) sum += price[j]; double mean = sum / period; // Calculate squared differences for(uint j = i - period + 1; j <= i; j++) sum_sq_diff += MathPow(price[j] - mean, 2); // Calculate standard deviation std_dev[i] = MathSqrt(sum_sq_diff / period); } return std_dev; }
Este es otro indicador sencillo que se puede llamar de la siguiente manera.
Archivo: Custom Indicators test script.mq5
void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; int period = 3; Print("Stddev values: ", CTrendIndicators::StandardDeviation(prices, period)); }
Resultados:
2025.01.21 11:55:11.657 Custom Indicators test script (EURUSD,H1) Stddev values: [nan,nan,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726]
La convergencia/divergencia de la media móvil (Moving Average Convergence Divergence, MACD)
Este oscilador muestra las relaciones entre dos medias móviles del precio de un valor. Se utiliza ampliamente como herramienta de análisis técnico.

Dado por la fórmula.
Línea principal del MACD
![]()
Donde:
-
= Media móvil exponencial con un período más corto (por ejemplo, 12) -
= Media móvil exponencial con un período más largo (por ejemplo, 26)
Línea de señal
![]()
Se trata de una EMA suavizada de la línea MACD, normalmente con un valor de período de 9.
Histograma MACD
Esta es la diferencia entre la línea MACD y la línea de señal.
![]()
De forma similar a cómo devolvimos los valores calculados de múltiples Bandas de Bollinger, definimos una estructura para devolver los valores del histograma MACD, la línea principal y la línea de señal.
Archivo: ta-lib.mqh
struct MACD_res_struct { vector main; // The MACD Line vector signal; // The Signal Line vector histogram; // The MACD Histogram };
MACD_res_struct COscillatorIndicators::MACD(const vector &price, uint fast_ema = 12, uint slow_ema = 26, uint macd_sma = 9, uint shift = 0) { uint size = (uint)price.Size(); MACD_res_struct res; if(!CheckShiftPeriod(size, slow_ema, shift)) return res; //--- Calculate EMA(short), EMA(long), and MACD Line vector fast_ema_vector = CTrendIndicators::EMA(price, fast_ema, shift); vector slow_ema_vector = CTrendIndicators::EMA(price, slow_ema, shift); res.main.Resize(size); res.main.Fill(NaN); for(uint i = 0; i < size; i++) res.main[i] = fast_ema_vector[i] - slow_ema_vector[i]; //--- Calculate Signal Line (SMA of MACD Line) res.signal = CTrendIndicators::SMA(price, macd_sma, shift); //--- Calculate MACD Histogram res.histogram.Resize(size); res.histogram.Fill(NaN); for(uint i = 0; i < size; i++) res.histogram[i] = res.main[i] - res.signal[i]; return res; }
Podemos obtener fácilmente los valores calculados del oscilador MACD dentro de un script de prueba de la siguiente manera.
Archivo: Custom Indicators test script.mq5
void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; MACD_res_struct macd; macd = COscillatorIndicators::MACD(prices,2,3,4); Print("MACD main: ", macd.main); Print("MACD signal: ", macd.signal); }
Resultados:
RD 0 12:28:51.368 Custom Indicators test script (EURUSD,H1) MACD main: [nan,nan,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5] HO 0 12:28:51.368 Custom Indicators test script (EURUSD,H1) MACD signal: [nan,nan,nan,2.5,3.5,4.5,5.5,6.5,7.5,8.5]
El indicador de fuerza relativa (Relative Strength Indicator, RSI)
Se trata de un oscilador de impulso (momentum) que mide el gasto y la magnitud de los movimientos de precios. Oscila entre 0 y 100, y los operadores lo utilizan para identificar condiciones de sobrecompra y sobreventa en el mercado.

Los cálculos del indicador RSI implican los siguientes pasos:
01: Calcular las ganancias y pérdidas medias durante un periodo determinado.
![]()
![]()
02: Cálculo de la fuerza relativa (Relative Strength, RS).
![]()
03: Cálculo del RSI.
![]()
Implementación en MQL5
vector COscillatorIndicators::RSI(const vector &price, uint period, uint shift = 0) { uint size = (uint)price.Size(); //--- Check for valid parameters if(!CheckShiftPeriod(size, period, shift)) return price; //--- Initialize vectors vector rsi(size), gains(size), losses(size); rsi.Fill(NaN); gains.Fill(0.0); losses.Fill(0.0); //--- Calculate gains and losses for(uint i = shift + 1; i < size; i++) { double change = price[i] - price[i - 1]; gains[i] = (change > 0) ? change : 0; losses[i] = (change < 0) ? -change : 0; } //--- Initialize first average gain and loss (simple average for the first period) double avg_gain = 0, avg_loss = 0; for(uint i = shift + 1; i < shift + 1 + period; i++) { avg_gain += gains[i]; avg_loss += losses[i]; } avg_gain /= period; avg_loss /= period; //--- Compute RSI for the rest of the periods for(uint i = shift + period; i < size; i++) { // Apply smoothing for average gain and loss avg_gain = ((avg_gain * (period - 1)) + gains[i]) / period; avg_loss = ((avg_loss * (period - 1)) + losses[i]) / period; // Calculate RSI double rs = (avg_loss == 0) ? 0 : avg_gain / avg_loss; rsi[i] = (avg_loss == 0) ? 100 : (100 - (100 / (1 + rs))); } return rsi; }
A continuación se muestra cómo se pueden obtener los valores del indicador RSI.
void OnStart() { vector prices = {1,2,3,4,5,6,7,8,9,10}; int period = 3; Print("RSI values: ", COscillatorIndicators::RSI(prices,period)); }
Resultados:
2025.01.21 12:51:29.640 Custom Indicators test script (EURUSD,H1) RSI values: [nan,nan,nan,100,100,100,100,100,100,100]
El indicador oscilador estocástico
Se trata de un indicador de impulso (momentum) que compara el precio de cierre de un valor con su rango de precios durante un periodo determinado.
La fórmula para calcular este indicador consiste en:
Línea %K
![]()
Donde:
-
es el precio de cierre actual. -
es el precio más bajo durante el período de referencia. -
es el precio más alto durante el período de referencia.
Línea %D
Esta es la media móvil de la línea %K, que suele utilizar una media móvil simple (SMA) de 3 períodos.
A continuación se muestra cómo implementar este indicador en MQL5.
Stochastic_struct COscillatorIndicators::StochasticOscillator(const vector &high, const vector &low, const vector &close, uint k_period = 5, uint d_period = 3, uint period = 3, uint shift = 0) { uint size = (uint)close.Size(); Stochastic_struct res; // Check for valid parameters if(!CheckShiftPeriod(size, period, shift)) return res; // Initialize vectors for %K and %D vector K(size), D(size); K.Fill(NaN); D.Fill(NaN); // Calculate %K for(uint i = shift + period - 1; i < size; i++) { double H_max = -DBL_MAX, L_min = DBL_MAX; // Find the highest high and the lowest low over the lookback period for(uint j = i - period + 1; j <= i; j++) { H_max = MathMax(H_max, high[j]); L_min = MathMin(L_min, low[j]); } // Calculate %K double K_value = (H_max - L_min != 0) ? ((close[i] - L_min) / (H_max - L_min)) * 100 : 0; K[i] = K_value; } // Smooth %K with a simple moving average (k_period) vector smoothedK(size); smoothedK.Fill(NaN); for(uint i = shift + k_period - 1; i < size; i++) { double sum = 0; for(uint j = i - k_period + 1; j <= i; j++) { sum += K[j]; } smoothedK[i] = sum / k_period; } // Calculate %D (3-period moving average of %K) D = CTrendIndicators::SMA(smoothedK, period, shift); res.main = K; res.signal = D; return res; }
Devolvemos la señal y la línea principal en una estructura.
void OnStart() { vector close = {1,2,3,4,5,6,7,8,9,10}; vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5}; vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5}; Stochastic_struct stoch; stoch = COscillatorIndicators::StochasticOscillator(high,low,close,5,3,3); Print("Stoch main: ", stoch.main); Print("Stoch signal: ", stoch.signal); }
El indicador del rango verdadero promedio (Average True Range, ATR)
Este es un indicador útil para comprender la volatilidad del mercado. Mide el rango promedio entre los precios altos y bajos durante un período específico.

Dado por la fórmula.
01: Cálculo del rango real (True Range, TR)
![]()
Donde:
- H es el máximo del período actual.
-
es el mínimo del período actual. -
es el cierre del período anterior.
02: Cálculo del rango verdadero promedio (Average True Range, ATR).
El ATR es la media móvil del rango real durante un período determinado.
SMA es la media móvil simple.
Podemos implementar este indicador en MQL5 de la siguiente manera.
vector COscillatorIndicators::ATR(const vector &high, const vector &low, const vector &close, uint period = 14, uint shift = 0) { uint size = (uint)close.Size(); // Check for valid parameters if(!CheckShiftPeriod(size, period, shift)) return close; // Initialize the True Range (TR) and ATR vectors vector TR(size); TR.Fill(NaN); // Calculate the True Range for each period for(uint i = shift + 1; i < size; i++) { double H = high[i]; double L = low[i]; double C_prev = close[i - 1]; // Calculate the three possible True Range values double TR1 = H - L; double TR2 = MathAbs(H - C_prev); double TR3 = MathAbs(L - C_prev); // True Range is the maximum of the three TR[i] = MathMax(TR1, MathMax(TR2, TR3)); } //--- Smooth the True Range using a simple moving average (SMA) over the specified period return CTrendIndicators::SMA(TR, period, shift); }
A continuación se muestra cómo podemos obtener los valores para este indicador.
void OnStart() { vector close = {1,2,3,4,5,6,7,8,9,10}; vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5}; vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5}; Print("ATR values: ", COscillatorIndicators::ATR(high,low,close,3)); }
Resultados:
2025.01.21 13:57:59.943 Custom Indicators test script (EURUSD,H1) ATR values: [nan,nan,nan,1.5,1.5,1.5,1.5,1.5,1.5,1.5]
El indicador de impulso (Momentum)
Se trata de un indicador sencillo que se utiliza para medir la velocidad a la que se mueve el precio de un activo durante un periodo determinado. Ayuda a identificar tendencias y posibles cambios de tendencia.

Dado por una fórmula simple.
![]()
Donde:
- Momentum(t) = Valor del impulso en el momento t
- Price(t) = El precio actual
- Price(t - n) = Precio hace n períodos
- n = El período durante el cual se calcula el impulso
Algunas variaciones para calcular el momento utilizan una relación.
![]()
Vamos a implementar el indicador de impulso utilizando esta variación.
Implementación en MQL5
vector COscillatorIndicators::MomentumIndicator(const vector &price, uint period, uint shift = 0) { uint size = (uint)price.Size(); // Check for valid input if(!CheckShiftPeriod(size, period, shift)) return price; // Initialize the momentum vector vector momentum(size); momentum.Fill(NaN); // Calculate Momentum for(uint i = shift + period; i < size; i++) { //momentum[i] = price[i] - price[i - period]; // Momentum difference formula // using the ratio formula: momentum[i] = (price[i] / price[i - period]) * 100; } return momentum; }
Indicador oscilador impresionante (Awesome Oscillator, AO)
Este es un indicador de impulso (momentum) que calcula la diferencia entre dos medias móviles simples (Simple Moving Averages, SMA) del precio medio.

Dado por la fórmula:
![]()
Donde:
-
= SMA con un período más corto (normalmente 5) -
= SMA con un período más largo (normalmente 34)
A continuación se muestra la implementación en MQL5.
vector CBillWilliamsIndicators::AwesomeOscillator(const vector &high, const vector &low, uint fast_period = 5, uint slow_period = 34, uint shift = 0) { uint size = (uint)high.Size(); if(size != low.Size()) return vector::Zeros(0); // Ensure high and low vectors are of the same size if(!CheckShiftPeriod(size, slow_period, shift)) return vector::Zeros(0); // Validate shift and slow period // Initialize vectors vector ao(size), median_price(size); ao.Fill(NaN); median_price.Fill(NaN); // Calculate Median Price for(uint i = 0; i < size; i++) median_price[i] = (high[i] + low[i]) / 2; // Calculate Fast and Slow SMAs of the Median Price vector sma_fast = CTrendIndicators::SMA(median_price, fast_period, shift); vector sma_slow = CTrendIndicators::SMA(median_price, slow_period, shift); // Calculate AO for(uint i = 0; i < size; i++) ao[i] = sma_fast[i] - sma_slow[i]; return ao; }
Dado que tenemos vectores muy simples para precios altos y bajos, podemos establecer el período rápido en 3 y el período lento en 5.
void OnStart()
{
vector close = {1,2,3,4,5,6,7,8,9,10};
vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
Print("AO values: ", CBillWilliamsIndicators::AwesomeOscillator(high,low,3,5));
} Resultados:
2025.01.21 14:25:50.590 Custom Indicators test script (EURUSD,H1) AO values: [nan,nan,nan,nan,1,1,1,1,1,1]
Indicador oscilador acelerador (Accelerator Oscillator, AC)
El oscilador acelerador (Accelerator Oscillator, AC) es una herramienta de análisis técnico que rastrea la velocidad de los cambios en el impulso (momentum) del precio. Muestra si la fuerza motriz del mercado se acelera o desacelera, lo que ayuda a los operadores a anticipar posibles reversiones.

Se calcula como la diferencia entre el oscilador Awesome y la media móvil simple (SMA) de 5 períodos del AO (Awesome Oscillator).
Dado por la siguiente fórmula.
Podemos implementar este indicador en MQL5 de la siguiente manera.
vector CBillWilliamsIndicators::AcceleratorOscillator(const vector &high, const vector &low, uint ao_period_fast = 5, // Fast period for AO uint ao_period_slow = 34, // Slow period for AO uint ac_period = 5 // Period for AC SMA ) { uint size = (uint)high.Size(); if(size != low.Size()) return vector::Zeros(0); // Ensure high and low vectors are of the same size // Validate shift and period if(!CheckShiftPeriod(size, ao_period_slow, 0)) return vector::Zeros(0); // Calculate AO (Awesome Oscillator) vector ao = AwesomeOscillator(high, low, ao_period_fast, ao_period_slow); // Calculate AC (Accelerator Oscillator) vector ac(size); ac.Fill(NaN); vector ao_sma_ac = CTrendIndicators::SMA(ao, ac_period); for(uint i = 0; i < size; i++) ac[i] = ao[i] - ao_sma_ac[i]; return ac; }
Dado que el oscilador acelerador (Accelerator Oscillator, AC) es muy similar al oscilador impresionante (Awesome Oscillator, AO), podemos llamar a su función con argumentos similares.
void OnStart() { vector close = {1,2,3,4,5,6,7,8,9,10}; vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5}; vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5}; //--- Bill williams indicator Print("AO values: ", CBillWilliamsIndicators::AwesomeOscillator(high,low,3,5)); Print("AC values: ", CBillWilliamsIndicators::AcceleratorOscillator(high,low,3,5)); }
Resultados:
MQ 0 14:40:36.296 Custom Indicators test script (EURUSD,H1) AO values: [nan,nan,nan,nan,1,1,1,1,1,1] EL 0 14:40:36.296 Custom Indicators test script (EURUSD,H1) AC values: [nan,nan,nan,nan,nan,nan,nan,nan,0,0]
Recopilación simplificada de datos de indicadores
Ahora que hemos visto cómo se han implementado algunos de los indicadores en la biblioteca, podemos intentar recopilar toda la información que queramos obtener del mercado y crear un indicador a partir de ella. Por ejemplo, voy a calcular la media móvil de los precios de apertura, máximo, mínimo y cierre.
Comenzamos obteniendo los valores OHLC.
int size = 1000; vector open, high, low, close; open.CopyRates(Symbol(), Period(), COPY_RATES_OPEN, 1, size); high.CopyRates(Symbol(), Period(), COPY_RATES_HIGH, 1, size); low.CopyRates(Symbol(), Period(), COPY_RATES_LOW, 1, size); close.CopyRates(Symbol(), Period(), COPY_RATES_CLOSE, 1, size);
Comenzando con la media móvil simple del periodo 20 aplicada a los precios de apertura.
vector sma_open = CTrendIndicators::SMA(open, 20);
Podemos recopilar sin esfuerzo diversos indicadores SMA para diferentes periodos y precios.
vector sma_open = CTrendIndicators::SMA(open, 20); vector sma_high = CTrendIndicators::SMA(high, 50); vector sma_low = CTrendIndicators::SMA(low, 100); vector sma_close = CTrendIndicators::SMA(close, 20);
En lugar de eso, introduzcámoslos en una matriz y observémoslos todos juntos.
matrix Indicators(size, 4); Indicators.Col(CTrendIndicators::SMA(open, 20), 0); Indicators.Col(CTrendIndicators::SMA(high, 50), 1); Indicators.Col(CTrendIndicators::SMA(low, 100), 2); Indicators.Col(CTrendIndicators::SMA(close, 20), 3); Print("Indicators matrix\n",Indicators);
Resultados:
NK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0485715,1.0484514,nan,1.048488] LL 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0484425,1.0485544,nan,1.0484265] RP 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048381,1.0486754,nan,1.048299] QG 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0483055,1.0488004,nan,1.048152] KK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0481585,1.0489198,nan,1.048296] HL 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048303,1.049033,nan,1.0485255] DS 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048533,1.0491756,nan,1.0487015] OK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048658,1.0493158,1.0475226,1.0488295] KD 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0487895,1.0494628,1.047473,1.0488985] JR 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0488575,1.0494916,1.0474256,1.0489465] FR 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0489055,1.049475,1.0473723,1.0490045] LL 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048964,1.0494814,1.0473137,1.049052] HK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0490075,1.0494728,1.0472494,1.0491065] CF 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.049062,1.0494618,1.0471845,1.049044] RE 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0489995,1.0494452,1.047114,1.048892] FQ 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048848,1.0494558,1.047044,1.0487065] CL 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0486625,1.0495002,1.0469762,1.0486305] DK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.048586,1.0496014,1.0469234,1.048582] EE 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0485375,1.0496714,1.0468866,1.048646] RE 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0486015,1.0497982,1.046857200000001,1.04877] IP 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0487245,1.0498646,1.0468378,1.0490025] DO 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0489565,1.0499466,1.046833500000001,1.0492415] GN 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0491975,1.050113,1.046846700000001,1.0497525] CK 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0497085,1.0502838,1.046881000000001,1.0502025] IJ 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.050159,1.0503826,1.046898100000001,1.0506915] PF 0 18:30:25.100 Custom Indicators test script (EURUSD,H1) [1.0506475,1.0504916,1.046935200000001,1.051158…]
Supongamos que quiero medir el impulso de estas variables que tengo, creando un indicador de impulso basado en ellas.
Indicators.Col(COscillatorIndicators::MomentumIndicator(open, 20), 4); Indicators.Col(COscillatorIndicators::MomentumIndicator(high, 50), 5); Indicators.Col(COscillatorIndicators::MomentumIndicator(low, 100), 6); Indicators.Col(COscillatorIndicators::MomentumIndicator(close, 20), 7);
Resultados:
IN 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0441885,nan,nan,1.0438975,99.43679181343492,nan,nan,99.44502817843157] LN 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0438975,1.0495116,nan,1.043593,99.44407828753189,nan,nan,99.41864350150351] QS 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0435925,1.0492638,nan,1.0433225,99.4176888931316,98.82473464044848,nan,99.4833645288208] HG 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0436275,1.049148199999999,nan,1.043363,100.0668474731655,99.45161810609009,nan,100.0773424743863] EH 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0436685,1.0490618,nan,1.043385,100.0782973197491,99.59030385797199,nan,100.0420047732697] KO 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0436925,1.0489454,nan,1.0434175,100.0458251389074,99.4479854313681,nan,100.0620536907626] JQ 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0437225,1.048834,nan,1.043484,100.0572803299347,99.47183265534476,nan,100.1270939444036] JE 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.043788,1.048718,nan,1.0435725,100.1251839535195,99.45010144680205,nan,100.1690625149243] LE 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.043877,1.048595199999999,nan,1.0435315,100.1700192943244,99.41799844546816,nan,99.92180198737388] MI 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0438365,1.048457,nan,1.0437085,99.92275488503829,99.34503611305946,nan,100.3389538390831] HN 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.044013,1.0483164,nan,1.044095,100.3379931060896,99.33386396801032,nan,100.7431263218612] KS 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0443985,1.048186,nan,1.044364,100.7411964891704,99.38227742565063,nan,100.5158345877638] EE 0 18:39:34.172 Custom Indicators test script (EURUSD,H1) [1.0446665,1.0480502,nan,1.044532,100.5139219145509,99.35649569733499,nan,100.3225558712848…]
Trazado de indicadores creados con funciones personalizadas
Intentemos hacer visibles al ojo humano los cálculos de los indicadores obtenidos de esta biblioteca.
A pesar de que este enfoque está orientado a la recopilación de datos más que a la visualización, visualicemos los cálculos del indicador en un indicador personalizado de MetaTrader 5.
Visualización del indicador SMA (20).
#property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_color1 clrDodgerBlue #property indicator_type1 DRAW_LINE #property indicator_style1 STYLE_SOLID #include <ta-lib.mqh> input int period_ = 20; //SMA period double buffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, buffer, INDICATOR_DATA); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0); PlotIndexSetString(0,PLOT_LABEL,"SMA("+string(period_)+")"); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period_+1); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if (prev_calculated==0) //Initial indicator calculation, For Plotting the entire past { vector buffer_vector = CTrendIndicators::SMA(close, period_); VectorToArray(buffer_vector, buffer); //We assign the calculations into an array format } else //For plotting the current value { for (int i=prev_calculated-1; i<rates_total; i++) //Loop from the prev_calculated bars -1 to the total bars present { double temp_close[]; //For storing temporary close values int size = period_*2; //We are going to copy an equivalent of period x 2 to leave a room for NaN values if (ArrayCopy(temp_close, close, 0, i - size, size) < 0) { printf("Failed to copy closing price values to a temporary array, err = %d",GetLastError()); continue; } vector indicator_values = CTrendIndicators::SMA(temp_close, period_); ulong last_index = indicator_values.Size() - 1; //The last value in the vector is the recent calculated indicator value buffer[i] = indicator_values[last_index]; //Assing the last indicator value to the last value in the buffer } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| A crude way to convert a vector into Array | //+------------------------------------------------------------------+ template <typename T> void VectorToArray(const vector<T> &v, T &arr[]) { ArrayResize(arr, (uint)v.Size()); for (uint i=0; i<(uint)v.Size(); i++) arr[i] = v[i]; }
Resultados:

Reflexiones finales
Este enfoque de colección personalizada de valores indicadores resulta más útil para la recopilación de datos que para cualquier otra tarea concreta, y es ideal para el análisis de datos y para los entusiastas que buscan formas innovadoras de recopilar datos indicadores con fines analíticos.
A menudo me he visto en la necesidad de obtener rápidamente varios valores de indicadores sin tener que inicializar el indicador y realizar todas esas tareas tediosas que a menudo provocan errores en MetaTrader5. Con la creciente demanda de datos en MQL5, debido a su nueva capacidad para manejar sofisticados modelos de IA y ML, necesitamos formas diferentes y eficaces de extraer y visualizar los datos de los indicadores con fines de análisis y entrenamiento de ML.
Nos vemos.
Tabla de archivos adjuntos
| Nombre del archivo | Descripción/Uso |
|---|---|
| Include\ta-lib.mqh | Librería MQL5 con todo el código de indicadores analizado en este artículo. |
| Indicators\Trend Indicators Redefined.mq5 | Un indicador que contiene un indicador de media móvil simple implementado con fines demostrativos. |
| Scripts\Custom Indicators test script.mq5 | Un script sencillo creado para probar y depurar código de indicadores. |
Fuentes y referencias
- Explicación del indicador del índice de fuerza relativa (Relative Strength Index, RSI) con fórmula
- Media móvil (MA): finalidad, usos, fórmula y ejemplos
- Cómo utilizar las Bandas de Bollinger para medir tendencias
- Indicador SAR parabólico: definición, fórmula, estrategias de trading
- Fórmula y usos de la desviación estándar frente a la varianza
- ¿Qué es el MACD?
- Oscilador estocástico: qué es, cómo funciona, cómo se calcula
- Fórmula del rango verdadero promedio (Average True Range, ATR), qué significa y cómo utilizarlo
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16931
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.
Simulación de mercado (Parte 09): Sockets (III)
Cómo funciones centenarias pueden actualizar nuestras estrategias comerciales
Análisis de todas las variantes del movimiento de precios en una computadora cuántica IBM
Redes neuronales en el trading: Modelos bidimensionales del espacio de enlaces (Final)
- 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
