English Русский 中文 Deutsch 日本語
preview
Aprendizaje automático y Data Science (Parte 34): Descomposición de series temporales, desglosando el mercado bursátil hasta su núcleo

Aprendizaje automático y Data Science (Parte 34): Descomposición de series temporales, desglosando el mercado bursátil hasta su núcleo

MetaTrader 5Indicadores |
48 0
Omega J Msigwa
Omega J Msigwa

Introducción

La predicción de series temporales nunca ha sido fácil. Los patrones suelen estar ocultos y llenos de ruido e incertidumbres, y los gráficos suelen ser engañosos, ya que contienen una visión general del rendimiento del mercado que no pretende ofrecer una perspectiva detallada de lo que está sucediendo.

En la previsión estadística y el aprendizaje automático, lo que intentamos hacer es desglosar los datos de series temporales, que en este caso son los valores de los precios en el mercado (valores de apertura, máximos, mínimos y cierre), en varios componentes que podrían ser más reveladores que una sola matriz de series temporales.

En este artículo, vamos a analizar la técnica estadística conocida como descomposición estacional. Nuestro objetivo es utilizarlo para analizar los mercados bursátiles con el fin de detectar tendencias, patrones estacionales y mucho más.


¿Qué es la descomposición estacional?

La descomposición estacional es una técnica estadística utilizada para dividir los datos de series temporales en varios componentes: tendencia, estacionalidad y residual. Estos componentes pueden explicarse de la siguiente manera.

Tendencia

El componente de tendencia de los datos de series temporales se refiere a los cambios o patrones a largo plazo que se observan a lo largo del tiempo.

Representa la dirección general en la que se mueven los datos. Por ejemplo, si los datos aumentan con el tiempo, el componente de tendencia tendrá una pendiente ascendente, y si los datos disminuyen con el tiempo, el componente de tendencia tendrá una pendiente descendente.

Esto es algo familiar para casi todos los operadores: la tendencia es lo más fácil de detectar en el mercado con solo mirar el gráfico.

Estacionalidad

El componente estacional de los datos de una serie temporal se refiere a los patrones cíclicos que se observan dentro de un período de tiempo determinado. Por ejemplo, si analizamos los datos de ventas mensuales de un minorista especializado en decoración y regalos, el componente estacional reflejaría el hecho de que las ventas tienden a alcanzar su punto álgido en diciembre debido a las compras navideñas, y se estabilizan una vez finalizada la temporada festiva, en los meses de enero, febrero, etc.

Residual

El componente residual de una serie temporal representa la variación aleatoria que queda después de haber tenido en cuenta los componentes de tendencia y estacionales. Representa el ruido o error en los datos que no puede explicarse por la tendencia o los patrones estacionales.

Para entender esto mejor, mira la imagen a continuación.



¿Por qué realizamos la descomposición estacional?

Antes de entrar en detalles matemáticos e implementar la descomposición estacional en MQL5, veamos primero las razones por las que realizamos la composición estacional en los datos de series temporales.

  1. Detectar patrones y tendencias subyacentes en los datos

    La descomposición estacional nos ayuda a identificar tendencias y patrones en los datos que podrían no ser evidentes a simple vista al examinar los datos sin procesar. Al desglosar los datos en sus componentes (tendencia, estacionalidad y residual), obtenemos una mejor comprensión de cómo estos componentes contribuyen al comportamiento general de los datos.

  2. Para eliminar los efectos de la estacionalidad

    La descomposición estacional se puede utilizar para eliminar los efectos de la estacionalidad de los datos, lo que nos permite centrarnos en la tendencia subyacente o en lo contrario cuando queremos trabajar únicamente con patrones estacionales, por ejemplo: cuando trabajamos con datos meteorológicos que muestran fuertes patrones estacionales.

  3. Para hacer predicciones precisas

    Cuando se desglosan los datos en componentes, resulta útil para filtrar la información innecesaria que podría ser menos necesaria en función del problema. Por ejemplo, cuando se intenta predecir la tendencia, es más sensato disponer solo de la información sobre la tendencia en lugar de los datos estacionales.

  4. Comparar tendencias entre diferentes períodos de tiempo o regiones

    La descomposición estacional se puede utilizar para comparar tendencias entre diferentes períodos de tiempo o regiones, lo que proporciona información sobre cómo diferentes factores pueden estar afectando a los datos. Por ejemplo, si comparamos datos de ventas minoristas entre diferentes regiones, la descomposición estacional puede ayudarnos a identificar diferencias regionales en los patrones estacionales y ajustar nuestros análisis en consecuencia.


Implementación de la descomposición estacional en MQL5

Para implementar este algoritmo analítico, primero generemos algunos datos aleatorios simples con características de tendencia, patrones estacionales y algo de ruido, algo que se puede observar en escenarios de datos de la vida real, especialmente en los mercados de divisas y bursátiles, donde los datos no son sencillos y claros.

Vamos a utilizar el lenguaje de programación Python para esta tarea.

Archivo: seasonal_decomposition_visualization.ipynb

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import os

sns.set_style("darkgrid")

# Create synthetic time-series data
np.random.seed(42)
time = np.arange(0, 365)  # 1 year (daily data)
trend = 0.05 * time  # Linear upward trend
seasonality = 5 * np.sin(2 * np.pi * time / 30)  # 30-day periodic seasonality
noise = np.random.normal(scale=2, size=len(time))  # Random noise

# Combine components to form the time-series
time_series = trend + seasonality + noise

# Plot the original time-series
plt.figure(figsize=(10, 4))
plt.plot(time, time_series, label="Original Time-Series", color="blue")
plt.xlabel("Time (Days)")
plt.ylabel("Value")
plt.title("Synthetic Time-Series with Trend and Seasonality")
plt.legend()
plt.show()

Resultado

Sabemos que el mercado bursátil es más complejo que eso, pero, dados estos datos simples, intentemos descubrir los patrones estacionales de 30 días que hemos añadido a la serie temporal, la tendencia general, y filtrar parte del ruido de los datos.


Extracción de tendencias

Para extraer las características de tendencia, podemos utilizar la media móvil (MA), ya que suaviza una serie temporal al promediar los valores en una ventana fija. Esto ayuda a filtrar las fluctuaciones a corto plazo y resaltar la tendencia subyacente.

En la descomposición aditiva, la tendencia se estima utilizando una media móvil sobre una ventana igual al período estacional p.

Donde:

 = Componente de tendencia en el tiempo t.

 = Tamaño de la ventana o período estacional.

 = La mitad del período estacional.

 = Valores observados en series temporales.

Para la descomposición multiplicativa, tomamos la media geométrica.

Donde:

 = Operador de producto, donde en lugar de sumar, multiplicamos todos los valores dentro de la ventana porque la descomposición multiplicativa asume que los efectos estacionales escalan los datos en lugar de sumarlos.

Esto puede parecer matemáticas complejas, pero se puede desglosar en unas pocas líneas de código MQL5.

vector moving_average(const vector &v, uint k, ENUM_VECTOR_CONVOLVE mode=VECTOR_CONVOLVE_VALID)
 {
   vector kernel = vector::Ones(k) /  k;
   vector ma = v.Convolve(kernel, mode);
    
   return ma;  
 }

Calculamos la media móvil basándonos en el método convolve porque es flexible, eficiente y maneja los valores perdidos en los extremos de la matriz, a diferencia del cálculo de la media móvil utilizando el enfoque de ventana deslizante.

Podemos finalizar la fórmula de la siguiente manera.

//--- compute the trend

   int n = (int)timeseries.Size();  
   res.trend = moving_average(timeseries, period);
   
   // We align trend array with the original series length
   
   int pad = (int)MathFloor((n - res.trend.Size()) / 2.0);
   int pad_array[] = {pad, n-(int)res.trend.Size()-pad};
   
   res.trend = Pad(res.trend, pad_array, edge);


Extracción de componentes estacionales

La extracción de componentes estacionales se refiere al aislamiento de los patrones repetitivos en una serie temporal que se producen a intervalos determinados, por ejemplo, diarios, mensuales, anuales, etc.

Este componente se calcula tras eliminar la tendencia de los datos originales de la serie temporal, y puede extraerse de forma diferente dependiendo de si el modelo es aditivo o multiplicativo.

Modelo aditivo

Tras estimar la tendencia , la serie sin tendencia se calcula de la siguiente manera.

Donde:

 = Un valor sin tendencia en el tiempo t.

 = Valor de la serie temporal en el tiempo t.

 = Componente de tendencia en el tiempo t.

Para calcular el componente estacional, tomamos la media de los valores sin tendencia en todos los ciclos completos del período estacional p.

Donde:

 = Número de ciclos estacionales completos en el conjunto de datos.

 = El período estacional (por ejemplo, 30 para datos diarios en un ciclo mensual).

 = Componente estacional extraído.


Modelo multiplicativo

En lo que respecta al modelo multiplicativo, dividimos en lugar de restar para eliminar la tendencia de una serie temporal.

El componente estacional se extrae tomando la media geométrica en lugar de la media aritmética.

Esto ayuda a evitar sesgos debido a la naturaleza multiplicativa del modelo.

Podemos implementar esta función en MQL5 de la siguiente manera.

//--- compute the seasonal component
   
   if (model == multiplicative)
     {
       for (ulong i=0; i<timeseries.Size(); i++)
         if (timeseries[i]<=0)
            {
               printf("Error, Multiplicative seasonality is not appropriate for zero and negative values");
               return res;
            }
     }
   
   vector detrended = {};
   vector seasonal = {};
   
   switch(model)
     {
      case  additive:
        {
          detrended = timeseries - res.trend;
          seasonal = vector::Zeros(period);
         
          for (uint i = 0; i < period; i++)
            seasonal[i] = SliceStep(detrended, i, period).Mean(); //Arithmetic mean over cycles
        }    
        break;
      case  multiplicative:
        {
          detrended = timeseries / res.trend;
          seasonal = vector::Zeros(period);
         
          for (uint i = 0; i < period; i++)
            seasonal[i] = MathExp(MathLog(SliceStep(detrended, i, period)).Mean()); //Geometric mean
        }    
        break;
      default:
        printf("Unknown model for seasonal component calculations");
        break;
     }

    
    vector seasonal_repeated = Tile(seasonal, (int)MathFloor(n/period)+1);
    res.seasonal = Slice(seasonal_repeated, 0, n);

La función Pad añade relleno (valores adicionales) alrededor de un vector, de forma similar a Numpy.pad. En este caso, ayuda a garantizar que los valores de la media móvil estén centrados.

Debido a la presencia de una función de transformación logarítmica MathLog que no se puede utilizar para valores cero y negativos, el modelo multiplicativo debe restringirse únicamente a series temporales positivas, por lo que era necesario comprobar y reforzar esta condición.
   if (model == multiplicative)
     {
       for (ulong i=0; i<timeseries.Size(); i++)
         if (timeseries[i]<=0)
            {
               printf("Error, Multiplicative seasonality is not appropriate for zero and negative values");
               return res;
            }
     }

La función Tile construye un vector grande repitiendo varias veces el vector estacional. Este proceso es crucial para capturar los patrones estacionales repetidos en una serie temporal.


Cálculo residual

Por último, calculamos los residuos restando la tendencia y la estacionalidad.

Para el modelo aditivo:

Para el modelo multiplicativo:

Donde:

 = Valor original de la serie temporal en el tiempo t.

 = Valor de la tendencia en el tiempo t.

 = Valor estacional en el tiempo t.


Poniéndolo todo en una sola función

De manera similar a la función seasonal_decompose en la que me inspiré, tuve que agrupar todos los cálculos en una función llamada seasonal_decompose que devuelve una estructura que contiene vectores de tendencia, estacionales y residuales.

enum seasonal_model
 {
   additive,
   multiplicative
 };

struct seasonal_decompose_results
 {
   vector trend;
   vector seasonal;
   vector residuals;
 };
 
seasonal_decompose_results seasonal_decompose(const vector &timeseries, uint period, seasonal_model model=additive)
 {
   seasonal_decompose_results res;
   
   if (timeseries.Size() < period)
    {
      printf("%s Error: Time series length is smaller than the period. Cannot compute seasonal decomposition.",__FUNCTION__);
      return res;
    }
   
//--- compute the trend

   int n = (int)timeseries.Size();  
   res.trend = moving_average(timeseries, period);
   
   // We align trend array with the original series length
   
   int pad = (int)MathFloor((n - res.trend.Size()) / 2.0);
   int pad_array[] = {pad, n-(int)res.trend.Size()-pad};
   
   res.trend = Pad(res.trend, pad_array, edge);

//--- compute the seasonal component
   
   if (model == multiplicative)
     {
       for (ulong i=0; i<timeseries.Size(); i++)
         if (timeseries[i]<=0)
            {
               printf("Error, Multiplicative seasonality is not appropriate for zero and negative values");
               return res;
            }
     }
   
   vector detrended = {};
   vector seasonal = {};
   
   switch(model)
     {
      case  additive:
        {
          detrended = timeseries - res.trend;
          seasonal = vector::Zeros(period);
         
          for (uint i = 0; i < period; i++)
            seasonal[i] = SliceStep(detrended, i, period).Mean(); //Arithmetic mean over cycles
        }    
        break;
      case  multiplicative:
        {
          detrended = timeseries / res.trend;
          seasonal = vector::Zeros(period);
         
          for (uint i = 0; i < period; i++)
            seasonal[i] = MathExp(MathLog(SliceStep(detrended, i, period)).Mean()); //Geometric mean
        }    
        break;
      default:
        printf("Unknown model for seasonal component calculations");
        break;
     }


    
    vector seasonal_repeated = Tile(seasonal, (int)MathFloor(n/period)+1);
    res.seasonal = Slice(seasonal_repeated, 0, n);

//--- Compute Residuals

    if (model == additive)
        res.residuals = timeseries - res.trend - res.seasonal;
    else  // Multiplicative
        res.residuals = timeseries / (res.trend * res.seasonal);
        
   return res;
 }

Por fin podemos probar esta función.

También tuve que generar valores positivos y guardarlos en un archivo CSV para probar la descomposición estacional utilizando el modelo multiplicativo.

Archivo: seasonal_decomposition_visualization.ipynb

# Create synthetic time-series data
np.random.seed(42)
time = np.arange(0, 365)  # 1 year (daily data)
trend = 0.05 * time  # Linear upward trend
seasonality = 5 * np.sin(2 * np.pi * time / 30)  # 30-day periodic seasonality
noise = np.random.normal(scale=2, size=len(time))  # Random noise

# Combine components to form the time-series
time_series = trend + seasonality + noise

# Fix for multiplicative decomposition: Shift the series to make all values positive
min_value = np.min(time_series)
if min_value <= 0:
    shift_value = abs(min_value) + 1  # Ensure strictly positive values
    time_series_shifted = time_series + shift_value
else:
    time_series_shifted = time_series

ts_pos_df = pd.DataFrame({
    "timeseries": time_series_shifted
})

ts_pos_df.to_csv(os.path.join(files_path,"pos_ts_df.csv"), index=False)

Dentro de nuestro script MQL5, cargamos ambos conjuntos de datos de series temporales utilizando la biblioteca dataframe que hemos comentado en este artículo. Tras cargar los datos, ejecutamos los algoritmos de descomposición estacional..

Archivo: seasonal_decompose test.mq5

#include <MALE5\Stats Models\Tsa\Seasonal Decompose.mqh>
#include <MALE5\pandas.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Additive model
   
   CDataFrame df;
   df.FromCSV("ts_df.csv");
   
   vector time_series = df["timeseries"];
   
//---
    
    seasonal_decompose_results res_ad = seasonal_decompose(time_series, 30, additive);
    
    df.Insert("original", time_series);
    df.Insert("trend",res_ad.trend);
    df.Insert("seasonal",res_ad.seasonal);
    df.Insert("residuals",res_ad.residuals);
    
    df.ToCSV("seasonal_decomposed_additive.csv");

//--- Multiplicative model

   CDataFrame pos_df;
   pos_df.FromCSV("pos_ts_df.csv");
   
   time_series = pos_df["timeseries"];
   
//---
    
    seasonal_decompose_results res_mp = seasonal_decompose(time_series, 30, multiplicative);
    
    pos_df.Insert("original", time_series);
    pos_df.Insert("trend",res_mp.trend);
    pos_df.Insert("seasonal",res_mp.seasonal);
    pos_df.Insert("residuals",res_mp.residuals);
    
    pos_df.ToCSV("seasonal_decomposed_multiplicative.csv");
  }

Tuve que guardar el resultado en nuevos archivos CSV para visualizarlo con Python.

Descomposición estacional utilizando un gráfico de resultados del modelo aditivo

Descomposición estacional utilizando un gráfico de resultados del modelo multiplicativo.

Las gráficas parecen casi similares cuando se trazan debido a la escala, pero los resultados difieren cuando se observan los datos. Este es el mismo resultado que obtendrías utilizando tsa.seasonal.seasonal_decompose proporcionado por statsmodels.org en Python.

Ahora que tenemos la función de descomposición estacional, utilicémosla para analizar los mercados bursátiles.


Observación de patrones en el mercado bursátil

Al analizar el mercado bursátil, identificar la tendencia suele ser sencillo, especialmente en el caso de empresas bien establecidas y con fundamentos sólidos. Muchas grandes empresas financieramente estables tienden a seguir una trayectoria ascendente a lo largo del tiempo debido a su crecimiento constante, innovación y demanda del mercado.

Sin embargo, detectar patrones estacionales en los precios de las acciones puede ser mucho más difícil. A diferencia de las tendencias, la estacionalidad se refiere a movimientos recurrentes de los precios a intervalos fijos que no siempre son evidentes, ya que estos patrones pueden producirse en diferentes marcos temporales.

Estacionalidad intradía

En determinadas horas de una jornada bursátil puede observarse un comportamiento repetitivo de los precios (por ejemplo, una mayor volatilidad al inicio o al cierre del mercado).

Estacionalidad mensual o trimestral

Los precios de las acciones pueden seguir ciclos basados en los informes de resultados, las condiciones económicas o la confianza de los inversores.

Estacionalidad a largo plazo

Algunas acciones muestran tendencias repetitivas a lo largo de los años debido a los ciclos económicos o a factores específicos de la empresa.

Caso práctico: las acciones de Apple (AAPL)

Tomando como ejemplo las acciones de Apple, podemos plantear la hipótesis de que los patrones estacionales surgen cada 22 días hábiles, lo que corresponde aproximadamente a un mes hábil en un conjunto de datos anual. Esta suposición se basa en el hecho de que hay aproximadamente 22 días hábiles al mes (excluyendo fines de semana y días festivos).

Mediante la aplicación de técnicas de descomposición estacional, podemos analizar si las acciones de Apple muestran movimientos recurrentes en su precio cada 22 días. Si existe un fuerte componente estacional, esto sugiere que las fluctuaciones de precios pueden seguir ciclos predecibles, lo que puede resultar útil para los operadores y analistas; sin embargo, si no se detecta una estacionalidad significativa, los movimientos de precios pueden estar impulsados en gran medida por factores externos, ruido o una tendencia dominante.

Recopilemos los precios de cierre diarios de 1000 barras y realicemos una descomposición estacional multiplicativa en un período de 22 días.

#include <MALE5\Stats Models\Tsa\Seasonal Decompose.mqh>
#include <MALE5\pandas.mqh>

input uint bars_total = 1000;
input uint period_ = 22;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   
    vector close, time;
    close.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_CLOSE, 1, bars_total); //closing prices
    time.CopyRates(Symbol(), PERIOD_D1, COPY_RATES_TIME, 1, bars_total); //time
   
    seasonal_decompose_results res_ad = seasonal_decompose(close, period_, multiplicative);
    
    CDataFrame df; //A dataframe object for storing the seasonal decomposition outcome
    
    df.Insert("time", time);
    df.Insert("close", close);
    df.Insert("trend",res_ad.trend);
    df.Insert("seasonal",res_ad.seasonal);
    df.Insert("residuals",res_ad.residuals);
    
    df.ToCSV(StringFormat("%s.%s.period=%d.seasonal_dec.csv",Symbol(), EnumToString(PERIOD_D1), period_));
  }

El resultado se visualizó en un cuaderno Jupyter llamado stock_market seasonal dec.ipynb. A continuación se muestra el resultado.

De acuerdo, podemos observar algunos patrones estacionales en el gráfico anterior, pero no podemos estar 100 % seguros de ellos, ya que siempre hay algunos errores asociados. El reto consistiría en interpretar los valores de error y realizar el análisis en consecuencia.

Basándonos en el gráfico de residuos, podemos observar que hay picos en los valores residuales entre los años 2020 y 2022. Todos sabemos que este fue el periodo en el que se produjo una pandemia mundial, por lo que es posible que los patrones estacionales se vieran alterados y fueran inconsistentes, lo que indica que no podemos confiar en los patrones estacionales que observamos en ese periodo.

Regla general.

Buena descomposición: los residuos deben parecer ruido aleatorio (ruido blanco).

Mala descomposición: los residuos aún muestran una estructura visible (tendencias o efectos estacionales que no se eliminaron).

Podemos utilizar diferentes técnicas matemáticas para visualizar los residuos, tales como:

El gráfico de distribución

Tenemos un gráfico de residuos con distribución normal, lo que podría ser una buena señal de que podría haber algunos patrones mensuales en las acciones de Apple.

La media y la desviación estándar

En el modelo aditivo, la media de los residuos debe ser cercana a 0, lo que significa que la mayoría de las variaciones se explican por la tendencia y la estacionalidad.

En el modelo multiplicativo, la media residual se aproxima idealmente a 1, lo que significa que la serie original se explica bien por la tendencia y la estacionalidad.

El valor de la desviación estándar debe ser pequeño

print("Residual Mean:", residuals.mean())  # Should be close to 0
print("Residual Std Dev:", residuals.std())  # Should be small

Resultados

Residual Mean: 1.0002367590572043
Residual Std Dev: 0.021749969975933727

Por último, podemos incluirlo todo en un indicador.

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots 1

#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_type1 DRAW_LINE
#property indicator_width1 2

//+------------------------------------------------------------------+

double trend_buff[];
double seasonal_buff[];

#include <MALE5\Stats Models\Tsa\Seasonal Decompose.mqh>
#include <MALE5\pandas.mqh>

input uint bars_total = 10000;
input uint period_ = 22;
input ENUM_COPY_RATES price = COPY_RATES_CLOSE;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   
   SetIndexBuffer(0, seasonal_buff, INDICATOR_DATA);
   SetIndexBuffer(1, trend_buff, INDICATOR_CALCULATIONS);
   
//---
   
   IndicatorSetString(INDICATOR_SHORTNAME, "Seasonal decomposition("+string(period_)+")");
   PlotIndexSetString(1, PLOT_LABEL, "seasonal ("+string(period_)+")");
   
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   
   ArrayInitialize(seasonal_buff, EMPTY_VALUE);
   ArrayInitialize(trend_buff, EMPTY_VALUE);
    
   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==rates_total) //not on a new bar, calculate the indicator on the opening of a new bar  
      return rates_total;
    
    ArrayInitialize(seasonal_buff, EMPTY_VALUE);
    ArrayInitialize(trend_buff, EMPTY_VALUE);

//---

    Comment("rates total: ",rates_total," bars total: ",bars_total);
    
    //if (rates_total<(int)bars_total)
    //  return rates_total;
          
    vector close_v;
    close_v.CopyRates(Symbol(), Period(), price, 0, bars_total); //closing prices
      
    seasonal_decompose_results res = seasonal_decompose(close_v, period_, multiplicative);
    
    for (int i=MathAbs(rates_total-(int)bars_total), count=0; i<rates_total; i++, count++) //calculate only the chosen number of bars
      {
         trend_buff[i] = res.trend[count];
         seasonal_buff[i] = res.seasonal[count];
      }
    
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Debido a la naturaleza del cálculo de la descomposición estacional, puede resultar muy costoso desde el punto de vista computacional calcular cuando utilizamos todas las tasas disponibles en el gráfico para realizar el cálculo tan pronto como llegan nuevas tasas al gráfico, tenemos que restringir el número de barras que se utilizan para el cálculo y la representación gráfica.

Tuve que crear dos indicadores separados, uno para trazar patrones estacionales y otro para trazar los residuos utilizando la misma lógica.

A continuación se muestran los gráficos de indicadores sobre el símbolo de Apple. 

Los patrones estacionales del indicador también pueden interpretarse como señales de trading o condiciones de sobreventa y sobrecompra, tal y como se puede observar en el gráfico anterior. Por ahora, es difícil saberlo, ya que aún no lo he explorado desde el punto de vista del trading. Te animo a que lo hagas como ejercicio.


Reflexiones finales

La descomposición estacional es una técnica útil que conviene tener en la caja de herramientas de trading algorítmico. Algunos científicos de datos la utilizan para crear nuevas características basadas en los datos de series temporales de que disponen, mientras que otros la utilizan para analizar la naturaleza de los datos y luego decidir las técnicas de aprendizaje automático adecuadas para cada problema concreto. Esto plantea ahora una pregunta importante: ¿cuándo utilizar la descomposición estacional?

Algunos científicos de datos comienzan realizando una descomposición estacional para separar la serie temporal en sus componentes subyacentes: tendencia, estacionalidad y residuos. Si hay un patrón estacional claro en los datos, se utiliza el suavizado exponencial estacional (también conocido como método estacional de Holt-Winters) para intentar pronosticar los datos; sin embargo, si no hay un patrón estacional claro, o si el patrón estacional es débil o irregular, se utilizan modelos ARIMA y otros modelos estándar de aprendizaje automático para intentar descubrir los patrones en los datos de series temporales y hacer predicciones.


Tabla de archivos adjuntos

Nombre del archivo y ruta
Descripción y uso
Include\pandas.mqh Consiste en la clase Dataframe para el almacenamiento y la manipulación de datos en formato similar a Pandas.
Include\Seasonal Decompose.mqh     Contiene todas las funciones y líneas de código que hacen posible la descomposición estacional en MQL5.
Indicators\Seasonal Decomposition.mq5 Este indicador traza el componente estacional.
Indicators\Seasonal Decomposition residuals.mq5 Este indicador traza el componente residual.
Scripts\seasonal_decompose test.mq5
Un script sencillo utilizado para implementar y depurar la función de descomposición estacional y sus componentes.
Scripts\stock market seasonal dec.mq5 Un script para analizar el precio de cierre de un símbolo y guardar el resultado en un archivo CSV con fines analíticos.
Python\seasonal_decomposition_visualization.ipynb Cuaderno Jupyter para visualizar los resultados de la descomposición estacional en archivos CSV.
Python\stock_market seasonal dec.ipynb Cuaderno Jupyter para visualizar los resultados de la descomposición estacional de una acción.
Files\*.csv Archivos CSV que contienen los resultados de la descomposición estacional y los datos tanto del código Python como del MQL5.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17361

Archivos adjuntos |
Attachments.zip (403.72 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
El componente View para tablas en el paradigma MQL5 MVC: Contenedores El componente View para tablas en el paradigma MQL5 MVC: Contenedores
En este artículo, hablaremos sobre cómo crear un control «Contenedor» que permita desplazarse por su contenido. Dentro del proceso, se mejorarán las clases ya implementadas de controles de la biblioteca gráfica.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
De novato a experto: Noticias animadas utilizando MQL5 (III) — Información sobre indicadores De novato a experto: Noticias animadas utilizando MQL5 (III) — Información sobre indicadores
En este artículo, mejoraremos el EA News Headline introduciendo una línea dedicada a la información de los indicadores: una visualización compacta en el gráfico de las señales técnicas clave generadas a partir de indicadores populares como el RSI, el MACD, el estocástico y el CCI. Este enfoque elimina la necesidad de múltiples subventanas de indicadores en la terminal MetaTrader 5, lo que mantiene su espacio de trabajo limpio y eficiente. Al aprovechar la API MQL5 para acceder a los datos de los indicadores en segundo plano, podemos procesar y visualizar información del mercado en tiempo real utilizando una lógica personalizada. Únase a nosotros para explorar cómo manipular los datos de los indicadores en MQL5 para crear un sistema de información inteligente y que ahorra espacio, todo ello en una sola línea horizontal en su gráfico de operaciones.