English Русский 中文 Español Deutsch 日本語
preview
Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo

Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo

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

Introdução

A previsão de dados de séries temporais nunca foi fácil. Os padrões geralmente estão ocultos e repletos de ruído e incertezas, os gráficos muitas vezes são enganosos, pois apresentam uma visão geral do desempenho do mercado que não tem como objetivo fornecer insights aprofundados sobre o que está acontecendo.

Na previsão estatística e no aprendizado de máquina, o que tentamos fazer é decompor os dados de séries temporais, que neste caso são valores de preço no mercado (Open, High, Low e Close), em vários componentes que podem ser mais informativos do que um único array de série temporal.

Neste artigo, vamos analisar a técnica estatística conhecida como decomposição sazonal. Nosso objetivo é utilizá-la para dissecar os mercados financeiros e detectar tendências, padrões sazonais e muito mais.


O que é Decomposição Sazonal

A decomposição sazonal é uma técnica estatística usada para decompor dados de séries temporais em vários componentes: Tendência, Sazonalidade e Resíduo. Esses componentes podem ser explicados da seguinte forma.

Tendência

O componente de tendência dos dados de séries temporais refere-se às mudanças ou padrões de longo prazo que são observados ao longo do tempo.

Ele representa a direção geral na qual os dados estão se movendo. Por exemplo, se os dados estão aumentando ao longo do tempo, o componente de tendência será ascendente, e se os dados estão diminuindo ao longo do tempo, o componente de tendência será descendente.

Isso é familiar para quase todos os traders, a tendência é a coisa mais fácil de identificar no mercado apenas observando o gráfico.

Sazonalidade

O componente sazonal de dados de séries temporais refere-se aos padrões cíclicos que são observados dentro de um determinado período de tempo. Por exemplo, se estivermos analisando dados mensais de vendas de um varejista especializado em decoração e presentes, o componente sazonal capturaria o fato de que as vendas tendem a atingir o pico em dezembro devido às compras de Natal, e se estabilizam após o término do período de festas, nos meses de janeiro, fevereiro, etc.

Residual

O componente residual de dados de séries temporais representa a variação aleatória que permanece após os componentes de tendência e sazonalidade terem sido considerados. Ele representa o ruído ou erro nos dados que não pode ser explicado pela tendência ou pelos padrões sazonais.

Para entender melhor, observe a imagem abaixo.



Por que realizamos a Decomposição Sazonal?

Antes de entrarmos nos detalhes matemáticos e implementarmos a decomposição sazonal em MQL5, vamos primeiro entender os motivos pelos quais realizamos a decomposição sazonal em dados de séries temporais.

  1. Detectar padrões e tendências subjacentes nos dados

    A decomposição sazonal pode nos ajudar a identificar tendências e padrões nos dados que podem não ser imediatamente aparentes ao examinar os dados brutos. Ao decompor os dados em seus componentes constituintes (tendência, sazonal e residual), obtemos uma melhor compreensão de como esses componentes contribuem para o comportamento geral dos dados.

  2. Remover os efeitos da sazonalidade

    A decomposição sazonal pode ser usada para remover os efeitos da sazonalidade dos dados, permitindo que nos concentremos na tendência subjacente ou, ao contrário, quando desejamos trabalhar apenas com padrões sazonais, por exemplo: ao trabalhar com dados climáticos que apresentam fortes padrões sazonais.
  3. Fazer previsões precisas

    Quando você tem os dados decompostos em componentes, isso ajuda a filtrar informações desnecessárias que podem ser menos relevantes dependendo do problema. Por exemplo, ao tentar prever a tendência, é mais adequado utilizar apenas as informações de tendência em vez dos dados sazonais.

  4. Comparar tendências em diferentes períodos de tempo ou regiões

    A decomposição sazonal pode ser usada para comparar tendências em diferentes períodos de tempo ou regiões, fornecendo insights sobre como diferentes fatores podem estar afetando os dados. Por exemplo, ao comparar dados de vendas no varejo entre diferentes regiões, a decomposição sazonal pode nos ajudar a identificar diferenças regionais nos padrões sazonais e ajustar nossas análises de acordo.


Implementando a Decomposição Sazonal em MQL5

Para implementar esse algoritmo analítico, primeiro vamos gerar alguns dados aleatórios simples com características de tendência, padrões sazonais e algum ruído. Isso é algo que pode ser observado em cenários de dados reais, especialmente em mercados Forex e de ações, onde os dados não são diretos nem limpos.

Vamos utilizar a linguagem de programação Python para essa tarefa.

Arquivo: 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 o mercado de ações é mais complexo do que isso, mas, considerando esses dados simples, vamos tentar identificar os padrões sazonais de 30 dias que adicionamos à série temporal, a tendência geral e filtrar parte do ruído dos dados.


Extração de Tendência

Para extrair as características de tendência, podemos usar a média móvel (MA), pois ela suaviza uma série temporal ao calcular a média dos valores em uma janela fixa. Isso ajuda a filtrar flutuações de curto prazo e destacar a tendência subjacente.

Na decomposição aditiva, a tendência é estimada usando uma média móvel em uma janela igual ao período sazonal p.

Onde:

 = Componente de tendência no tempo t.

 = Tamanho da janela ou período sazonal.

 = Metade do período sazonal.

 = Valores observados da série temporal.

Para decomposição multiplicativa, utilizamos a média geométrica.

Onde:

 = Operador de produto, onde, em vez de somar, multiplicamos todos os valores dentro da janela, pois a decomposição multiplicativa assume que os efeitos sazonais escalam os dados em vez de simplesmente serem somados.

Isso pode parecer matemática complexa, mas pode ser reduzido a algumas linhas de código em 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 a média móvel com base no método de convolução porque ele é flexível, eficiente e lida com valores ausentes nas extremidades da matriz, ao contrário do cálculo da média móvel usando a abordagem de janela deslizante.

Podemos finalizar a fórmula da seguinte forma.

//--- 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);


Extração do Componente Sazonal

A extração do componente sazonal refere-se ao isolamento dos padrões repetitivos em uma série temporal que ocorrem em um determinado intervalo, por exemplo: diário, mensal, anual, etc.

Esse componente é calculado após remover a tendência da série temporal original e pode ser extraído de maneira diferente dependendo se o modelo é aditivo ou multiplicativo.

Modelo aditivo

Após estimar a tendência , a série sem tendência é calculada da seguinte forma.

Onde:

 = valor sem tendência no tempo t.

 = valor da série temporal no tempo t.

 = componente de tendência no tempo t.

Para calcular o componente sazonal, tiramos a média dos valores sem tendência ao longo de todos os ciclos completos do período sazonal p.

Onde:

 = número de ciclos sazonais completos no conjunto de dados.

 = o período sazonal (por exemplo, 30 para dados diários em um ciclo mensal).

 = componente sazonal extraído.


Modelo multiplicativo

Quando se trata do modelo multiplicativo, dividimos em vez de subtrair para remover a tendência de uma série temporal.

O componente sazonal é extraído utilizando a média geométrica em vez da média aritmética.

Isso ajuda a evitar viés devido à natureza multiplicativa do modelo.

Podemos implementar essa função em MQL5 da seguinte forma.

//--- 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);

A função Pad adiciona preenchimento (valores extras) ao redor de um vetor, de forma semelhante ao Numpy.pad; neste cenário, isso ajuda a garantir que os valores da média móvel estejam centralizados.

Devido à presença da função de transformação logarítmica MathLog, que não pode ser utilizada com valores zero ou negativos, o modelo multiplicativo deve ser restrito apenas a séries temporais positivas, condição que deve ser verificada e reforçada.
   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;
            }
     }

A função Tile constrói um vetor grande repetindo o vetor sazonal várias vezes. Esse processo é crucial para capturar os padrões sazonais repetitivos em uma série temporal.


Cálculo Residual

Por fim, calculamos os resíduos subtraindo a tendência e a sazonalidade.

Para o modelo aditivo:

Para o modelo aditivo:

Onde:

 = valor original da série temporal no tempo t.

 = valor da tendência no tempo t.

 = valor sazonal no tempo t.


Unificando Tudo em Uma Função

De forma semelhante à função seasonal_decompose, da qual me inspirei, foi necessário encapsular todos os cálculos em uma única função chamada seasonal_decompose, que retorna uma estrutura contendo os vetores de tendência, sazonalidade e resíduo.

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 fim, podemos testar essa função.

Foi necessário também gerar valores positivos e salvá-los em um arquivo CSV para testar a decomposição sazonal usando o modelo multiplicativo.

Arquivo: 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 do nosso script em MQL5, carregamos ambos os conjuntos de dados de séries temporais utilizando a biblioteca dataframe que discutimos em este artigo. Após carregar os dados, executamos os algoritmos de decomposição sazonal.

Arquivo: 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");
  }

Foi necessário salvar o resultado novamente em novos arquivos CSV para visualização utilizando Python.

Gráfico de resultado da decomposição sazonal usando o modelo aditivo

Gráfico de resultado da decomposição sazonal usando o modelo multiplicativo

Os gráficos parecem quase semelhantes quando plotados devido à escala, mas os resultados diferem quando observamos os dados. Esse é o mesmo resultado que você obteria usando tsa.seasonal.seasonal_decompose, fornecido por stats models em Python.

Agora que temos a função de decomposição sazonal, vamos utilizá-la para analisar o mercado de ações.


Observando Padrões no Mercado de Ações

Ao analisar o mercado de ações, identificar a tendência geralmente é algo direto, especialmente para empresas bem estabelecidas e financeiramente sólidas. Muitas grandes corporações tendem a seguir uma trajetória ascendente ao longo do tempo devido ao crescimento consistente, inovação e demanda de mercado.

No entanto, detectar padrões sazonais nos preços das ações pode ser muito mais desafiador. Diferentemente da tendência, a sazonalidade refere-se a movimentos recorrentes de preço em intervalos fixos que nem sempre são evidentes; esses padrões podem ocorrer em diferentes escalas de tempo.

Sazonalidade intradiária

Certas horas durante um dia de negociação podem apresentar comportamentos repetitivos de preço (por exemplo, aumento da volatilidade na abertura ou no fechamento do mercado).

Sazonalidade mensal ou trimestral

Os preços das ações podem seguir ciclos com base em relatórios de resultados, condições econômicas ou sentimento dos investidores.

Sazonalidade de Longo prazo

Algumas ações apresentam tendências repetitivas ao longo dos anos devido a ciclos econômicos ou fatores específicos da empresa.

Estudo de Caso: A ação da Apple (AAPL)

Usando a ação da Apple como exemplo, podemos hipotetizar que padrões sazonais surgem a cada 22 dias de negociação, o que corresponde aproximadamente a um mês de negociação em um conjunto de dados anual. Essa suposição baseia-se no fato de que há aproximadamente 22 dias de negociação por mês (excluindo fins de semana e feriados).

Ao aplicar técnicas de decomposição sazonal, podemos analisar se a ação da Apple apresenta movimentos recorrentes de preço a cada 22 dias. Se um componente sazonal forte estiver presente, isso sugere que as flutuações de preço podem seguir ciclos previsíveis, o que pode ser útil para traders e analistas; no entanto, se nenhuma sazonalidade significativa for detectada, os movimentos de preço podem ser amplamente impulsionados por fatores externos, ruído ou uma tendência dominante.

Vamos coletar os preços de fechamento diários para 1000 barras e realizar a decomposição sazonal multiplicativa em um período de 22 dias.

#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_));
  }

O resultado foi então visualizado em um notebook Jupyter chamado stock_market seasonal dec.ipynb, abaixo está o resultado.

Certo, podemos ver alguns padrões sazonais no gráfico acima, mas não podemos ter 100% de certeza sobre esses padrões, pois sempre há erros associados; o desafio é interpretar os valores de erro e realizar a análise de acordo.

Com base no gráfico de resíduos, podemos observar que há picos nos valores residuais nos anos de 2020 a 2022. Todos sabemos que esse foi o período em que houve uma pandemia global, então talvez os padrões sazonais tenham sido interrompidos e inconsistentes, indicando que não podemos confiar totalmente nos padrões sazonais observados nesse período.

Regra prática.

Boa decomposição: os resíduos devem se comportar como ruído aleatório (ruído branco).

Má decomposição: os resíduos ainda apresentam estrutura visível (tendências ou efeitos sazonais que não foram removidos).

Podemos utilizar diferentes técnicas matemáticas para visualizar os resíduos, como:

O gráfico de distribuição

Temos um gráfico de resíduos com distribuição normal, o que pode ser um bom indicativo de que existem padrões mensais na ação da Apple.

A Média e o Desvio Padrão

No modelo aditivo, a média dos resíduos deve ser próxima de 0, o que significa que a maior parte das variações é explicada pela tendência e pela sazonalidade.

No modelo multiplicativo, a média dos resíduos idealmente deve ser próxima de 1, o que significa que a série original é bem explicada pela tendência e pela sazonalidade.

O valor do desvio padrão deve ser pequeno

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

Saídas

Residual Mean: 1.0002367590572043
Residual Std Dev: 0.021749969975933727

Por fim, podemos colocar tudo dentro de um 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);
  }

Devido à natureza do cálculo da decomposição sazonal, ele pode ser computacionalmente caro quando utilizamos todos os dados disponíveis no gráfico para calcular assim que novos dados chegam; portanto, precisamos restringir o número de barras utilizadas para cálculo e plotagem.

Foi necessário criar dois indicadores separados, um para plotar os padrões sazonais e outro para plotar os resíduos utilizando a mesma lógica.

Abaixo estão os gráficos dos indicadores no símbolo da Apple. 

Os padrões sazonais do indicador também podem ser interpretados como sinais de trading ou condições de sobrecompra e sobrevenda, como pode ser visto no gráfico acima; no momento, é difícil afirmar com certeza, pois ainda não foi explorado do ponto de vista operacional. Eu encorajo você a explorar isso como exercício.


Considerações Finais

A decomposição sazonal é uma técnica útil para se ter no arsenal de trading algorítmico. Alguns cientistas de dados a utilizam para criar novas features com base nos dados de séries temporais disponíveis, enquanto outros a utilizam para analisar a natureza dos dados e então decidir quais técnicas de aprendizado de máquina são mais adequadas para o problema em questão. Isso levanta uma questão importante: quando utilizar a decomposição sazonal?

Alguns cientistas de dados começam realizando a decomposição sazonal para separar a série temporal em seus componentes fundamentais: tendência, sazonalidade e resíduos. Se houver um padrão sazonal claro nos dados, então a suavização exponencial sazonal (também conhecida como método sazonal de Holt-Winters) é utilizada para tentar prever os dados; no entanto, se não houver um padrão sazonal claro, ou se o padrão sazonal for fraco ou irregular, então modelos ARIMA e outros modelos padrão de aprendizado de máquina são utilizados para tentar revelar padrões nos dados de séries temporais e realizar previsões.


Tabela de anexos

Nome do arquivo & caminho
Descrição & uso
Include\pandas.mqh Consiste na classe Dataframe para armazenamento e manipulação de dados em formato semelhante ao Pandas.
Include\Seasonal Decompose.mqh     Contém todas as funções e linhas de código que tornam possível a decomposição sazonal em MQL5.
Indicators\Seasonal Decomposition.mq5 Este indicador plota o componente sazonal.
Indicators\Seasonal Decomposition residuals.mq5 Este indicador plota o componente residual.
Scripts\seasonal_decompose test.mq5
Um script simples utilizado para implementar e depurar a função de decomposição sazonal e seus componentes.
Scripts\stock market seasonal dec.mq5 Um script para analisar o preço de fechamento de um símbolo e salvar o resultado em um arquivo CSV para fins analíticos.
Python\seasonal_decomposition_visualization.ipynb Notebook Jupyter para visualização dos resultados da decomposição sazonal em arquivos CSV.
Python\stock_market seasonal dec.ipynb Notebook Jupyter para visualização dos resultados da decomposição sazonal de uma ação.
Files\*.csv Arquivos CSV contendo os resultados da decomposição sazonal e dados provenientes tanto do código em Python quanto do MQL5.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17361

Arquivos anexados |
Attachments.zip (403.72 KB)
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização de Código (III): Módulo de Comunicação Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização de Código (III): Módulo de Comunicação
Junte-se a nós para uma discussão aprofundada sobre os mais recentes avanços no design de interfaces em MQL5 enquanto apresentamos o Painel de Comunicações redesenhado e continuamos nossa série sobre a construção do Novo Painel de Administração utilizando princípios de modularização. Desenvolveremos a classe CommunicationsDialog passo a passo, explicando detalhadamente como herdá-la da classe Dialog. Além disso, utilizaremos arrays e a classe ListView em nosso desenvolvimento. Obtenha insights práticos para elevar suas habilidades em desenvolvimento MQL5 — leia o artigo e participe da discussão na seção de comentários!
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Do básico ao intermediário: Objetos e sub janelas (II) Do básico ao intermediário: Objetos e sub janelas (II)
Este artigo explica como capturar e tratar a remoção de objetos do gráfico em MQL5 usando eventos do MetaTrader 5. Ao detectar a exclusão de um objeto criado pelo indicador, o código remove a instância correspondente para evitar inconsistências e sub janelas remanescentes. A abordagem adota nomes únicos e um arquivo de cabeçalho, priorizando simplicidade e previsibilidade do comportamento.