Indicador de estacionalidad por horas, días de la semana y meses
Los precios reproducen una melodía que se repite ciertos días del mes, días de la semana o incluso horas del día. Estos ritmos recurrentes, o patrones estacionales, pueden ofrecer a los tráders pistas sobre cuándo es probable que el mercado suba y cuándo es probable que baje. La estacionalidad en los mercados financieros no es solo un fenómeno curioso, sino una herramienta que ayuda a identificar momentos predecibles en el caos de los precios. Por ejemplo, ¿ha notado que algunos pares de divisas suelen subir los lunes o bajar a finales de mes? Esto se conoce como estacionalidad, y estudiarla puede ofrecer a los tráders una ventaja.
En este tutorial, crearemos un indicador de estacionalidad en MQL5 para la plataforma MetaTrader 5. Nuestro indicador analizará datos históricos de precios para identificar la rentabilidad media para los días del mes (del 1 al 31), los días de la semana (de lunes a domingo) o las horas del día (de 0:00 a 23:00). Los resultados se mostrarán como un histograma en una ventana de gráfico aparte, con una línea de pronóstico que conecta los valores de estacionalidad y puntos que resaltan los valores esperados en las barras siguientes. Además, el indicador mostrará estadísticas de texto con pronósticos, además de los mejores y peores periodos. Le guiaremos paso a paso por el proceso de desarrollo, explicándole cada parte del código para que no solo pueda usar el indicador, sino también adaptarlo a sus propias ideas. Juntos haremos un viaje al mundo de la programación y el análisis financiero, donde el código se convierte en el puente entre los datos y las decisiones de trading.
¿Por qué estudiar la estacionalidad?
La estacionalidad es como el pulso oculto del mercado. Esta se origina a partir de muchos factores: el comportamiento de los tráders, los acontecimientos económicos e incluso la psicología humana. Por ejemplo, a finales de mes, los grandes fondos pueden cerrar posiciones, lo cual provoca movimientos de precios predecibles. O bien, en determinados momentos del día, la actividad del mercado aumenta debido a la apertura de las sesiones bursátiles en Londres o Nueva York. Nuestro indicador le ayudará a identificar estos patrones convirtiendo datos caóticos en gráficos y cifras comprensibles. No se trata de magia, es ciencia de datos al servicio del tráder.
El indicador que crearemos será universal, y permitirá seleccionar el tipo de estacionalidad, configurar el número de barras analizadas, mostrar la rentabilidad como porcentaje o valor absoluto, y activar o desactivar la previsión en el gráfico. La flexibilidad resulta clave para que el indicador sea una herramienta útil en distintos mercados y marcos temporales, ya sea el gráfico diario de EUR/USD o el gráfico horario del oro.
Definiendo la estructura del indicador
La creación de un indicador comienza por comprender cómo se verá y funcionará. Para no saturar el espacio principal, queremos que se muestre en una ventana aparte debajo del gráfico de precios. En cuanto a la visualización, vamos a utilizar tres elementos: un histograma que muestra el rendimiento promedio para cada periodo, una línea de pronóstico que conecta los valores de estacionalidad y los datos previstos, y los puntos de pronóstico que resaltan los valores esperados en las dos barras siguientes. Cada elemento estará asociado a un búfer de datos, un array que almacena los valores que se van a dibujar. También necesitaremos variables globales para almacenar los datos de estacionalidad y las etiquetas de periodo, así como parámetros de entrada para que el usuario pueda personalizar el indicador.
//+------------------------------------------------------------------+ //| SeasonalityIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 //--- plot Seasonality #property indicator_label1 "Seasonality" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- plot Forecast Line #property indicator_label2 "Forecast" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_DASH #property indicator_width2 2 //--- plot Forecast Points #property indicator_label3 "Forecast Points" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrOrange #property indicator_width3 3
En este código, configuramos información básica sobre el indicador: los derechos de autor, el enlace al sitio web de MetaQuotes y la versión. La directiva #property indicator_separate_window especifica que el indicador estará en una ventana separada. Luego definimos tres búferes y tres gráficos. El histograma (DRAW_HISTOGRAM) está coloreado de azul, la línea de pronóstico (DRAW_LINE) está coloreada de rojo y los puntos de pronóstico (DRAW_ARROW) están coloreados de naranja con una anchura de tres píxeles. Estos ajustes definen el estilo visual que hará que el indicador resulte claro y atractivo.
Configuración de los parámetros de entrada
Para que el indicador sea flexible, vamos a añadir parámetros de entrada que el usuario pueda modificar en la ventana de configuración de MetaTrader 5. El parámetro para seleccionar el tipo de estacionalidad permite analizar los días del mes, los días de la semana o las horas del día. El número de barras a analizar determina hasta qué profundidad podemos adentrarnos en la historia. Es posible mostrar la rentabilidad como un porcentaje o un valor absoluto, así como mostrar únicamente los valores de estacionalidad positivos. La opción de activar la previsión en el gráfico y personalizar los colores de la línea y los puntos de previsión hace que el indicador sea aún más personalizable.
//--- Входные параметры enum ENUM_SEASONALITY_TYPE { SEASONALITY_DAYS_OF_MONTH = 0, // Дни месяца (1-31) SEASONALITY_DAYS_OF_WEEK = 1, // Дни недели (Пн-Вс) SEASONALITY_HOURS = 2 // Часы (0-23) }; input ENUM_SEASONALITY_TYPE SeasonalityType = SEASONALITY_DAYS_OF_MONTH; // Тип сезонности input int BarsToAnalyze = 1000; // Количество баров для анализа input bool ShowPercentage = true; // Показывать в процентах input bool ShowPositiveOnly = false; // Показывать только положительные значения input bool ShowForecastOnChart = true; // Показывать прогноз на графике input color ForecastColor = clrRed; // Цвет линии прогноза input color ForecastPointsColor = clrOrange; // Цвет точек прогноза
La enumeración ENUM_SEASONALITY_TYPE define tres opciones de estacionalidad. Por defecto, está seleccionado el análisis por días del mes. El parámetro BarsToAnalyze está configurado en 1000 barras, lo cual se corresponde con aproximadamente cuatro años de datos en un gráfico diario. La opción ShowPercentage está habilitada de forma predeterminada para mostrar los rendimientos como un porcentaje, lo que resulta más intuitivo. Los parámetros ShowPositiveOnly y ShowForecastOnChart ofrecen mayor flexibilidad, y podemos cambiar los colores a través de la interfaz de MetaTrader.
Definición de búferes y variables
Para almacenar datos, el indicador usa tres búferes: uno para el histograma, el segundo para la línea de pronóstico y el tercero para los puntos de pronóstico. También se necesitan variables globales: un array para almacenar el rendimiento promedio por periodo, un array para las etiquetas de los periodos (por ejemplo, "Lun" o "1") y una variable para el número de periodos, que depende del tipo de estacionalidad (31 para los días del mes, 7 para los días de la semana, 24 para las horas).
//--- Буферы индикатора double SeasonalityBuffer[]; double ForecastLineBuffer[]; double ForecastPointsBuffer[]; //--- Глобальные переменные double seasonality_data[]; string period_labels[]; int periods_count;
Los búferes contendrán los datos para la representación gráfica, y las variables seasonality_data y period_labels ayudarán a almacenar y mostrar los resultados del análisis. La variable periods_count determina cuántos periodos se analizan y se establece según el tipo de estacionalidad.
Inicialización del indicador
La función OnInit se ejecuta cuando se inicia el indicador y se encarga de la configuración inicial. Vincula los búferes con los gráficos, establece el estilo de flecha para los puntos de pronóstico y define los colores a partir de parámetros del usuario. Según el tipo de estacionalidad, se crean etiquetas para los periodos y el array para almacenar los datos de estacionalidad se inicializa a cero. La precisión de la visualización se ajusta dependiendo de si se seleccionan porcentajes o valores absolutos.
int OnInit() { SetIndexBuffer(0, SeasonalityBuffer, INDICATOR_DATA); SetIndexBuffer(1, ForecastLineBuffer, INDICATOR_DATA); SetIndexBuffer(2, ForecastPointsBuffer, INDICATOR_DATA); PlotIndexSetInteger(2, PLOT_ARROW, 159); PlotIndexSetInteger(1, PLOT_LINE_COLOR, ForecastColor); PlotIndexSetInteger(2, PLOT_LINE_COLOR, ForecastPointsColor); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: periods_count = 31; ArrayResize(period_labels, periods_count); for(int i = 0; i < periods_count; i++) period_labels[i] = IntegerToString(i + 1); IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по дням месяца"); break; case SEASONALITY_DAYS_OF_WEEK: periods_count = 7; ArrayResize(period_labels, periods_count); period_labels[0] = "Пн"; period_labels[1] = "Вт"; period_labels[2] = "Ср"; period_labels[3] = "Чт"; period_labels[4] = "Пт"; period_labels[5] = "Сб"; period_labels[6] = "Вс"; IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по дням недели"); break; case SEASONALITY_HOURS: periods_count = 24; ArrayResize(period_labels, periods_count); for(int i = 0; i < periods_count; i++) period_labels[i] = IntegerToString(i) + ":00"; IndicatorSetString(INDICATOR_SHORTNAME, "Сезонность по часам"); break; } ArrayResize(seasonality_data, periods_count); ArrayInitialize(seasonality_data, 0.0); IndicatorSetInteger(INDICATOR_DIGITS, ShowPercentage ? 2 : _Digits); return(INIT_SUCCEEDED); }
Esta función establece las bases para el funcionamiento del indicador. Por ejemplo, para los días de la semana se crea un array de etiquetas desde "Lun" hasta "Dom", y para las horas, desde "0:00" hasta "23:00". El nombre del indicador cambia para que el usuario comprenda de inmediato qué tipo de estacionalidad se ha seleccionado. Esto hace que el indicador resulte intuitivo.
Cálculo de la estacionalidad
El funcionamiento principal del indicador se produce en la función OnCalculate. Esta analiza los datos históricos de precios, calcula la rentabilidad media de cada periodo y rellena los búferes para su renderizado. En primer lugar, se comprueba si hay suficientes barras en el gráfico. A continuación, se crean arrays auxiliares para sumar los rendimientos y contar las barras en cada periodo.
Para cada barra, la rentabilidad se calcula como la relación entre la diferencia de los precios de cierre y el precio de cierre actual. La función GetPeriodIndex determina a qué periodo pertenece la barra y los datos se escriben en los arrays. Posteriormente, se calcula el rendimiento medio, que, si es necesario, se convierte en porcentaje o se restablece a cero para valores negativos. Luego los búferes se rellenan con datos y se llama a una función independiente para realizar la previsión.
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < BarsToAnalyze + 1) return(0); ArrayInitialize(seasonality_data, 0.0); double period_returns[]; int period_counts[]; ArrayResize(period_returns, periods_count); ArrayResize(period_counts, periods_count); ArrayInitialize(period_returns, 0.0); ArrayInitialize(period_counts, 0); int start_pos = MathMax(0, rates_total - BarsToAnalyze - 1); for(int i = start_pos; i < rates_total - 1; i++) { double return_value = 0.0; if(close[i] != 0) return_value = (close[i+1] - close[i]) / close[i]; int period_index = GetPeriodIndex(time[i]); if(period_index >= 0 && period_index < periods_count) { period_returns[period_index] += return_value; period_counts[period_index]++; } } for(int i = 0; i < periods_count; i++) { if(period_counts[i] > 0) { seasonality_data[i] = period_returns[i] / period_counts[i]; if(ShowPercentage) seasonality_data[i] *= 100.0; if(ShowPositiveOnly && seasonality_data[i] < 0) seasonality_data[i] = 0.0; } } for(int i = 0; i < rates_total; i++) { int period_index = GetPeriodIndex(time[i]); if(period_index >= 0 && period_index < periods_count) { SeasonalityBuffer[i] = seasonality_data[period_index]; ForecastLineBuffer[i] = seasonality_data[period_index]; } else { SeasonalityBuffer[i] = 0.0; ForecastLineBuffer[i] = 0.0; } ForecastPointsBuffer[i] = EMPTY_VALUE; } if(ShowForecastOnChart) DrawForecastOnChart(rates_total, time); return(rates_total); }
Este código es el núcleo del indicador y convierte los datos de precios brutos en patrones significativos. Imagine que está analizando un gráfico diario de un par de divisas. Para cada día de la semana, el indicador calcula cuánto ha variado el precio y encuentra el valor medio. Si observa que el mercado cae un promedio del 0,05% los viernes, esto podría ser una señal para una estrategia de inversión.
Definición del periodo
Para saber a qué periodo pertenece una barra, vamos a crear la función GetPeriodIndex. Esta convierte el intervalo de tiempo de la barra en un índice de periodo dependiendo del tipo de estacionalidad. Para los días del mes, tomamos el número del día y le restamos uno para obtener un índice del 0 al 30. Para los días de la semana, convertimos el valor del día de la semana (donde el domingo es 0 y el lunes es 1) a un orden de día de la semana donde el lunes es 0 y el domingo es 6. Para las horas, simplemente retorna el valor de la hora de 0 a 23.
int GetPeriodIndex(datetime bar_time) { MqlDateTime dt; TimeToStruct(bar_time, dt); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: return(dt.day - 1); case SEASONALITY_DAYS_OF_WEEK: return(dt.day_of_week == 0 ? 6 : dt.day_of_week - 1); case SEASONALITY_HOURS: return(dt.hour); } return(-1); }
Esta función es como una brújula que ayuda al indicador a entender a qué "casilla" asignar los datos de la barra. Sin ella, no podríamos asociar el precio a un día u hora específicos.
Elaboramos el pronóstico
La previsión es lo que hace que el indicador no solo sea analítico, sino también prácticamente útil. La función DrawForecastOnChart dibuja los valores de estacionalidad esperados en las dos últimas barras del gráfico. Asimismo, determina el periodo actual y los dos periodos siguientes utilizando la función GetNextPeriod. Los valores de estacionalidad para estos periodos se escriben en la línea de pronóstico y en los búferes de puntos. Para que la línea de previsión tenga un aspecto uniforme, el valor del periodo actual se escribe en el búfer de la penúltima barra.
void DrawForecastOnChart(int rates_total, const datetime &time[]) { if(rates_total < 3) return; datetime current_time = time[rates_total - 1]; int current_period = GetPeriodIndex(current_time); int next_period1 = GetNextPeriod(current_period); int next_period2 = GetNextPeriod(next_period1); int forecast_pos1 = rates_total - 2; int forecast_pos2 = rates_total - 1; if(next_period1 >= 0 && next_period1 < periods_count) { ForecastLineBuffer[forecast_pos1] = seasonality_data[next_period1]; ForecastPointsBuffer[forecast_pos1] = seasonality_data[next_period1]; } if(next_period2 >= 0 && next_period2 < periods_count) { ForecastLineBuffer[forecast_pos2] = seasonality_data[next_period2]; ForecastPointsBuffer[forecast_pos2] = seasonality_data[next_period2]; } if(rates_total >= 3) { int current_pos = rates_total - 3; int current_period_idx = GetPeriodIndex(time[current_pos]); if(current_period_idx >= 0 && current_period_idx < periods_count) { double current_value = seasonality_data[current_period_idx]; double next_value = (next_period1 >= 0) ? seasonality_data[next_period1] : current_value; ForecastLineBuffer[current_pos] = current_value; if(forecast_pos1 < rates_total) ForecastLineBuffer[forecast_pos1] = next_value; } } }
El pronóstico es como una mirada al futuro: el indicador nos dice qué esperar basándose en datos históricos. Si, por ejemplo, los martes suelen generar una rentabilidad del +0,07%, el indicador lo mostrará en el gráfico, ayudando a los tráders a prepararse.
Calculando el siguiente periodo
La función GetNextPeriod determina qué periodo sigue al actual. Usa aritmética modular para ofrecer una transición cíclica: después del día 31 del mes viene el día 1, después del domingo viene el lunes, después de las 23:00 viene la hora de las 0:00.
int GetNextPeriod(int current_period) { if(current_period < 0) return(-1); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: return((current_period + 1) % 31); case SEASONALITY_DAYS_OF_WEEK: return((current_period + 1) % 7); case SEASONALITY_HOURS: return((current_period + 1)rate_total % 24); } return(-1); }
Esta función garantiza que la previsión siempre considere los periodos futuros correctos, preservando la lógica temporal cíclica. Opera como un calendario de mercado preciso, asegurando que el indicador se mueva correctamente de un periodo a otro, ya sea por el cambio de los días del mes, los días de la semana o las horas. Piense en el mercado como un reloj gigante donde cada tic cuenta no solo el tiempo, sino también los posibles movimientos de precios. Después del día 31 del mes, la función nos retorna sin problemas al primer día, después del domingo pasa al lunes y después de las 23:00, a las 0:00. Este enfoque cíclico refleja la estructura en tiempo real de los mercados financieros, lo cual permite al indicador predecir qué periodos pueden ser significativos para un tráder.
Mostramos las estadísticas

Para que los tráders puedan comprender al instante los resultados del análisis, el indicador muestra estadísticas detalladas directamente en el gráfico. La función ShowStatistics crea un mensaje de texto que, como un narrador, convierte datos áridos en una fascinante historia sobre las tendencias del mercado.
La función comienza con un título que cambia según el tipo de estacionalidad que elijamos, ya sea análisis por día del mes, día de la semana u hora del día. A continuación, se indica el número de barras analizadas, lo cual permite al tráder hacerse una idea de la profundidad de los datos históricos en los que se basan los cálculos. A continuación, se añade una previsión obtenida con la ayuda de la función GetForecast, que predice cómo es probable que se comporte el mercado en los próximos dos periodos basándose en las tendencias históricas. Después de ello, la función examina cuidadosamente el array seasonality_data, encontrando los periodos con los rendimientos más altos y más bajos. Estos periodos se clasifican como mejores y peores, y sus valores se muestran con tres decimales para que el tráder pueda evaluar su importancia.
El mensaje concluye con estadísticas completas, en las que, para cada periodo se indica su etiqueta —por ejemplo, “Lun” o “15”— y el rendimiento promedio. Este texto aparece en la esquina superior derecha del gráfico, como el informe de un científico, revelando los secretos de los ritmos del mercado y haciéndolos comprensibles y útiles para la toma de decisiones de trading.
Conclusión
La estacionalidad en los mercados financieros no es un fenómeno místico, sino un patrón matemáticamente fundamentado que puede convertirse en una poderosa herramienta para los tráders. El indicador que hemos creado transforma los movimientos caóticos de los precios en patrones comprensibles, revelando ritmos ocultos del mercado según el día del mes, de la semana o la hora de la sesión de negociación.
Gracias a su configuración flexible de parámetros, la visualización de los resultados en forma de histograma con una previsión y las estadísticas detalladas, esta herramienta permite no solo analizar datos históricos, sino también formular hipótesis fundamentadas sobre los movimientos futuros de los precios. La integración de elementos de pronóstico hace que el indicador sea aplicable en la práctica para el desarrollo de estrategias de trading.
El código del indicador se adapta fácilmente a diversos activos y marcos temporales, y una explicación paso a paso de cada función permite modificar el algoritmo para adaptarlo a nuestras propias ideas de inversión. En definitiva, este indicador es el puente entre los cálculos estadísticos complejos y las decisiones de trading concretas, transformando los datos en conocimiento y el conocimiento en beneficio potencial.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/18672
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.
Análisis de las brechas temporales de precios en MQL5 (Parte II): Creamos un mapa de calor de la distribución de liquidez a lo largo del tiempo
Redes neuronales en el trading: Segmentación periódica adaptativa (Generación de tokens)
Particularidades del trabajo con números del tipo double en MQL4
- 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