English Русский 中文 Español Deutsch 日本語
preview
Ciência de Dados e ML (Parte 42): Previsão de Séries Temporais no Mercado Forex com ARIMA em Python

Ciência de Dados e ML (Parte 42): Previsão de Séries Temporais no Mercado Forex com ARIMA em Python

MetaTrader 5 — Sistemas de negociação |
27 1
Omega J Msigwa
Omega J Msigwa

Conteúdo


O que é Previsão de Séries Temporais?

A previsão de séries temporais é o processo de utilizar dados históricos para prever valores futuros em uma sequência de observações. Essa sequência normalmente é organizada cronologicamente, daí o nome série temporal.

Variáveis fundamentais nos dados de séries temporais 

Embora possamos incluir quantas variáveis explicativas desejarmos em nossos dados, qualquer conjunto de dados utilizado na análise ou previsão de séries temporais precisa conter estas duas variáveis.

  1. Tempo

    Esta é uma variável independente que representa os momentos específicos em que os dados foram observados.

  2. Variável-alvo

    É o valor que queremos prever com base em observações anteriores e, possivelmente, em outros fatores. (Por exemplo, o preço de fechamento diário de uma ação, a temperatura registrada a cada hora ou o tráfego de um site por minuto).

O objetivo da previsão de séries temporais é aproveitar os padrões e as tendências históricas presentes nos dados para realizar previsões fundamentadas sobre valores futuros.

Já discutimos anteriormente a previsão de séries temporais utilizando modelos convencionais de IA. Neste artigo, abordaremos a previsão de séries temporais utilizando um modelo concebido para problemas de séries temporais, conhecido como ARIMA.

A previsão de séries temporais pode ser dividida em dois tipos.

  1. Previsão de séries temporais univariadas
    É um problema de previsão de séries temporais no qual uma única variável preditora é utilizada para prever seus próprios valores futuros. Por exemplo, utilizar os preços de fechamento atuais de uma ação para prever seus preços de fechamento futuros.

    Esse é o tipo de previsão que os modelos ARIMA são capazes de realizar.
  2. Previsão de séries temporais multivariadas
    É um problema de previsão de séries temporais no qual múltiplas variáveis preditoras são utilizadas para prever o valor futuro de uma variável-alvo.

    De maneira semelhante ao que fizemos neste artigo.


Introdução aos Modelos ARIMA

ARIMA é a sigla para Modelo Autorregressivo Integrado de Médias Móveis (Autoregressive Integrated Moving Average).

Ele pertence a uma classe de modelos que explicam determinada série temporal com base em seus valores anteriores, ou seja, em suas defasagens e nos erros de previsão defasados.

A equação pode ser utilizada para prever valores futuros. Qualquer série temporal "não sazonal" que apresente padrões e não seja simplesmente um ruído branco aleatório pode ser modelada utilizando modelos ARIMA.

Portanto, o ARIMA, abreviação de Autoregressive Integrated Moving Average, é um algoritmo de previsão baseado na ideia de que as informações contidas apenas nos valores históricos de uma série temporal podem ser utilizadas para prever seus valores futuros.

Os modelos ARIMA são especificados por três parâmetros de ordem: (p, d, q).

Onde:

  • p é a ordem do componente autorregressivo (AR).
  • q é a ordem do componente de médias móveis (MA).
  • d é o número de diferenciações necessárias para tornar a série temporal estacionária.


O significado de p, d e q no modelo ARIMA

O significado de p

p representa a ordem do componente autorregressivo (AR). Refere-se ao número de valores defasados de Y que serão utilizados como variáveis preditoras.

O significado de d

O termo Autorregressivo no ARIMA significa que se trata de um modelo de regressão linear que utiliza seus próprios valores defasados como variáveis preditoras. Como sabemos, os modelos de regressão linear funcionam melhor quando as variáveis preditoras não estão correlacionadas e são independentes entre si. Por isso, precisamos tornar a série temporal estacionária.

A abordagem mais comum para tornar uma série estacionária é aplicar a diferenciação. Ou seja, subtrair o valor anterior do valor atual. Às vezes, dependendo da complexidade da série, pode ser necessário aplicar mais de uma diferenciação.

Portanto, o valor de d representa o número mínimo de diferenciações necessárias para tornar a série estacionária. Se a série temporal já for estacionária, então d = 0.

O significado de q

q representa a ordem do componente de médias móveis (MA). Refere-se ao número de erros de previsão defasados que devem ser incluídos no modelo ARIMA.


Principais Componentes de um Modelo ARIMA

Para compreender o ARIMA, precisamos analisar separadamente os elementos que compõem sua estrutura. Depois de decompor esses componentes, fica mais fácil entender como esse método de previsão de séries temporais funciona como um todo.

O nome ARIMA pode ser dividido em três partes (AR, I, MA), conforme descrito a seguir.

Componente Autorregressivo AR(p)

O componente autorregressivo (AR) modela o valor atual com base em valores passados da própria série. Para esclarecer, a "estrutura autorregressiva" funciona como um modelo de regressão no qual utilizamos os valores históricos defasados da própria série temporal como variáveis regressoras. 

Esse componente é calculado pela seguinte fórmula:

Fórmula AR

Onde:

  •  é o valor atual da série temporal no instante t.
  •  é o termo constante.
  •  até são os parâmetros autorregressivos (coeficientes) que indicam quanto cada valor defasado contribui para o valor atual.
  •  até são os valores anteriores defasados da série temporal.
  •  é o termo de erro no instante t.

Componente Integrado I(d)

O componente Integrado (I) envolve a diferenciação da série temporal, considerando que ela deve ser estacionária, ou seja, sua média e variância devem permanecer constantes ao longo do tempo.

Basicamente, subtraímos uma observação de outra para eliminar tendências e sazonalidades. Ao aplicar a diferenciação, buscamos obter a estacionariedade. Essa etapa é necessária porque ajuda o modelo a se ajustar aos dados, e não ao ruído.

Componente de Médias Móveis MA(q) 

O componente de Médias Móveis (MA) concentra-se na relação entre uma observação e um erro residual. Ao analisar como a observação atual se relaciona com os erros anteriores, podemos extrair informações úteis sobre possíveis tendências presentes nos dados. 

Podemos considerar os resíduos como parte desses erros, e o conceito do modelo de médias móveis permite estimar ou considerar seu impacto sobre a observação mais recente. Isso é particularmente útil para acompanhar e identificar mudanças de curto prazo nos dados ou choques aleatórios. No componente de Médias Móveis (MA) de uma série temporal, podemos obter informações valiosas sobre seu comportamento, o que, por sua vez, permite realizar previsões com maior precisão.

Onde:

  •  é uma constante.
  •  é o parâmetro MA.
  •  é o erro anterior.
  •  é o erro atual.


Modelo ARIMA em Python

Portanto, um modelo ARIMA é simplesmente a combinação dos três componentes descritos anteriormente: AR, I e MA. Sua equação passa a ser a seguinte:

Agora, o desafio dos modelos ARIMA é encontrar os parâmetros adequados (os valores de p, d e q). Como esses valores determinam o funcionamento do modelo, vamos entender como identificá-los.


Determinando a ordem do componente AR (p)

Podemos determinar o número necessário de termos AR analisando o gráfico da Função de Autocorrelação Parcial (PACF).

A autocorrelação parcial pode ser descrita como a correlação entre a série temporal e seus valores defasados, após excluir as contribuições das defasagens intermediárias. Assim, a PACF representa, de certa forma, a correlação direta entre determinada defasagem e a série temporal. Dessa maneira, podemos identificar se essa defasagem precisa ou não ser incluída no componente AR.

Comece instalando todas as dependências pelo Prompt de Comando (CMD). O arquivo requirements.txt está anexado ao final deste artigo.

pip install -r requirements.txt

Importações.

# Importing required libraries
import pandas as pd
import numpy as np
import MetaTrader5 as mt5

# Use auto_arima to automatically select best ARIMA parameters

import seaborn as sns
import matplotlib.pyplot as plt
import warnings
import os

# Suppress warning messages for cleaner output
warnings.filterwarnings("ignore")

# Set seaborn plot style for better visualization
sns.set_style("darkgrid")

Obtendo os dados do MetaTrader 5.

# Getting (EUR/USD OHLC data) from MetaTrader5

mt5_exe_file = r"c:\Users\Omega Joctan\AppData\Roaming\Pepperstone MetaTrader 5\terminal64.exe" # Change this to your MetaTrader5 path
if not mt5.initialize(mt5_exe_file):
    print("Failed to initialize Metatrader5, error = ",mt5.last_error)
    exit()

# select a symbol into the market watch
symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_D1

if not mt5.symbol_select(symbol, True):
    print(f"Failed to select {symbol}, error = {mt5.last_error}")
    mt5.shutdown()
    exit()

rates = mt5.copy_rates_from_pos(symbol, timeframe, 1, 1000) # Get 1000 bars historically
df = pd.DataFrame(rates)

print(df.head(5))
print(df.shape)

Gráfico da PACF.

from statsmodels.graphics.tsaplots import plot_pacf
import matplotlib.pyplot as plt

plt.figure(figsize=(6,4))
plot_pacf(series.diff().dropna(), lags=5)

plt.title("Partial Autocorrelation Plot")
plt.xlabel('Lag')  # X-axis label
plt.ylabel('PACF')  # Y-axis label

plt.savefig("pacf plot.png")
plt.show()

Saídas.

Para determinar o valor adequado de p, procuramos a defasagem a partir da qual a PACF deixa de apresentar valores significativos no gráfico (aproxima-se de zero e permanece estatisticamente insignificante). O valor dessa defasagem é o candidato adequado para p. 

Com base no gráfico acima, o valor adequado de p é 0 (todas as defasagens superiores a 0 são estatisticamente insignificantes).

Determinando a ordem de diferenciação (d) em um modelo ARIMA

Como explicado anteriormente, o objetivo de diferenciar uma série temporal é torná-la estacionária, já que o modelo ARIMA pressupõe estacionariedade. No entanto, devemos tomar cuidado para não aplicar diferenciações insuficientes ou excessivas à série temporal.

A ordem adequada de diferenciação é o número mínimo de diferenciações necessárias para obter uma série aproximadamente estacionária, que oscile em torno de uma média definida e cujo gráfico da Função de Autocorrelação (ACF) se aproxime de zero relativamente rápido.

Se as autocorrelações permanecerem positivas por muitas defasagens (10 ou mais), a série precisará de diferenciações adicionais. Por outro lado, se a autocorrelação na defasagem 1 for excessivamente negativa, provavelmente a série foi diferenciada em excesso.

Se não conseguirmos decidir entre duas ordens de diferenciação, escolheremos aquela que apresentar o menor desvio padrão na série diferenciada.

Utilizando os preços de fechamento do EURUSD, vamos determinar a ordem adequada de diferenciação.

Primeiramente, precisamos verificar se a série fornecida é estacionária (neste caso, os preços de fechamento), utilizando o Teste Aumentado de Dickey-Fuller (Teste ADF), disponível no pacote statsmodels do Python. Verificamos a estacionariedade porque precisamos determinar a ordem de diferenciação apenas quando a série não é estacionária.

A hipótese nula (H₀) do teste ADF estabelece que a série temporal não é estacionária. Portanto, se o valor-p do teste for inferior ao nível de significância (0,05), rejeitamos a hipótese nula e concluímos que a série temporal é estacionária.

Assim, no nosso caso, se o valor-p for maior que 0,05, prosseguiremos com a determinação da ordem adequada de diferenciação.

Mesmo antes de realizar o teste ADF, podemos perceber que os preços de fechamento do EURUSD não são estacionários simplesmente observando o gráfico de linhas.

plt.figure(figsize=(7,5))
sns.lineplot(df, x=df.index, y="Close")
plt.savefig("close prices.png")

Saídas.

Verificando a estacionariedade.

from statsmodels.tsa.stattools import adfuller

series = df["Close"]
result = adfuller(series)

print(f'p-value: {result[1]}')

Saídas.

p-value: 0.3707268514544181

Como podemos observar, o valor-p é muito superior ao nível de significância (0,05). Vamos diferenciar a série uma vez e depois duas vezes, observando como fica o gráfico de autocorrelação em cada caso.

# Original Series
fig, axes = plt.subplots(3, 2, sharex=True, figsize=(9, 9))

axes[0, 0].plot(series); axes[0, 0].set_title('Original Series')
plot_acf(series, ax=axes[0, 1])

# 1st Differencing
axes[1, 0].plot(series.diff().dropna()); axes[1, 0].set_title('1st Order Differencing')
plot_acf(series.diff().dropna(), ax=axes[1, 1])

# 2nd Differencing
axes[2, 0].plot(series.diff().diff()); axes[2, 0].set_title('2nd Order Differencing')
plot_acf(series.diff().diff().dropna(), ax=axes[2, 1])

plt.savefig("acf plots.png")
plt.show()

Saídas.

Como podemos observar nos gráficos, a diferenciação de primeira ordem é suficiente, pois a diferenciação de segunda ordem não apresenta uma melhoria significativa na estacionariedade. Isso pode ser confirmado novamente por meio do teste ADF.

result = adfuller(series.diff().dropna())
print(f'p-value d=1: {result[1]}')

result = adfuller(series.diff().diff().dropna())
print(f'p-value d=2: {result[1]}')

Saídas.

p-value d=1: 0.0
p-value d=2: 0.0

Determinando a ordem do componente MA (q)

Assim como analisamos o gráfico da PACF para determinar o número de termos AR (p), utilizaremos o gráfico da ACF para determinar o número de termos MA. Novamente, o componente MA representa, tecnicamente, o erro de uma previsão defasada.

O gráfico da ACF indica quantos termos MA são necessários para eliminar qualquer autocorrelação presente na série após torná-la estacionária.

plt.figure(figsize=(7,5))
plot_pacf(series.diff().dropna(), lags=20)

plt.title("Partial Autocorrelation Plot")
plt.xlabel('Lag')  # X-axis label
plt.ylabel('PACF')  # Y-axis label

plt.savefig("pacf plot finding q.png")
plt.show()

Saídas.

O melhor valor de q é 0.

Os métodos apresentados anteriormente para determinar os valores de p, d e q são básicos e manuais. Podemos automatizar esse processo e encontrar os parâmetros com muito menos esforço utilizando uma função auxiliar do pacote pmdarima, chamada auto_arima.

from pmdarima.arima import auto_arima

model = auto_arima(series, seasonal=False, trace=True)
print(model.summary())

Saídas.

Executando uma busca passo a passo para minimizar o AIC
 ARIMA(2,1,2)(0,0,0)[0] intercept   : AIC=-35532.282, Time=3.21 sec
 ARIMA(0,1,0)(0,0,0)[0] intercept   : AIC=-35537.068, Time=0.49 sec
 ARIMA(1,1,0)(0,0,0)[0] intercept   : AIC=-35537.492, Time=0.59 sec
 ARIMA(0,1,1)(0,0,0)[0] intercept   : AIC=-35537.511, Time=0.74 sec
 ARIMA(0,1,0)(0,0,0)[0]             : AIC=-35538.731, Time=0.25 sec
 ARIMA(1,1,1)(0,0,0)[0] intercept   : AIC=-35535.683, Time=1.22 sec

Best model:  ARIMA(0,1,0)(0,0,0)[0]          
Total fit time: 6.521 seconds

E pronto! Obtivemos os mesmos parâmetros encontrados anteriormente por meio da análise manual.


Construindo um modelo ARIMA para o EURUSD

Agora que determinamos os valores de p, d e q, temos tudo o que precisamos para ajustar (treinar) o modelo ARIMA.

from statsmodels.tsa.arima.model import ARIMA

arima_model = ARIMA(series, order=(0,1,0))
arima_model = arima_model.fit()
print(arima_model.summary())

Saídas.

                               SARIMAX Results                                
==============================================================================
Dep. Variable:                  Close   No. Observations:                 4007
Model:                 ARIMA(0, 1, 0)   Log Likelihood               13987.647
Date:                Mon, 26 May 2025   AIC                         -27973.293
Time:                        16:59:38   BIC                         -27966.998
Sample:                             0   HQIC                        -27971.062
                               - 4007                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2      5.427e-05   7.78e-07     69.768      0.000    5.27e-05    5.58e-05
===================================================================================
Ljung-Box (L1) (Q):                   1.47   Jarque-Bera (JB):              1370.86
Prob(Q):                              0.22   Prob(JB):                         0.00
Heteroskedasticity (H):               0.49   Skew:                             0.09
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.86
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Agora, vamos treinar esse modelo com alguns dados e usá-lo para fazer previsões em dados fora da amostra, de forma semelhante ao que fazemos com modelos clássicos de aprendizado de máquina.

Começaremos dividindo os dados em conjuntos de treinamento e teste.

series = df["Close"]

train_size = int(len(series) * 0.8)
train, test = series[:train_size], series[train_size:]

Ajustando o modelo aos dados de treinamento.

from statsmodels.tsa.arima.model import ARIMA

arima_model = ARIMA(train, order=(0,1,0))
arima_model = arima_model.fit()
print(arima_model.summary())

Realizando previsões com os dados de treinamento.

predicted = arima_model.predict(start=1, end=len(train))

Visualizando os resultados.

plt.figure(figsize=(7,4))
plt.plot(train.index, train, label='Actual')
plt.plot(train.index, predicted, label='Forecasted mean', linestyle='--')
plt.title('Actual vs Forecast')
plt.legend()
plt.show()

Saídas.

Agora que temos os valores reais e suas respectivas previsões, podemos avaliar o modelo utilizando qualquer técnica de avaliação ou função de perda de nossa preferência.

Mas, antes disso, vamos descobrir como utilizar esse modelo ARIMA para realizar previsões com dados fora da amostra.


Previsões Fora da Amostra Utilizando ARIMA

Diferentemente dos algoritmos clássicos de aprendizado de máquina, esses modelos tradicionais de previsão de séries temporais adotam uma abordagem diferente ao realizar previsões com informações que ainda não foram observadas.

Nas estruturas e bibliotecas clássicas de aprendizado de máquina em Python, um método chamado predict, quando recebe um array de dados, realiza uma previsão ou estimativa dos próximos valores (futuros). Entretanto, a função predict disponibilizada pelo módulo ARIMA desempenha uma função bastante diferente.

Nos modelos ARIMA, esse método não necessariamente prevê o futuro. Ele é especialmente útil para realizar previsões com base nos dados dentro da amostra, ou seja, nas informações que já fazem parte do modelo, correspondentes aos dados de treinamento.

Para compreender melhor essa diferença, vamos distinguir entre ‘prediction’ (estimativa de valores desconhecidos em geral)

A predição consiste em estimar valores desconhecidos (futuros ou não) utilizando qualquer modelo, enquanto a previsão de séries temporais se refere à estimativa de valores futuros a partir dos padrões e das dependências temporais presentes nos dados.

A predição pode ser aplicada a problemas como classificar a direção do mercado ou estimar os próximos preços de fechamento, enquanto a previsão de séries temporais é utilizada para estimar os próximos preços de uma ação com base nos valores atuais.

No modelo ARIMA, o método predict normalmente é utilizado para gerar previsões referentes a valores históricos com os quais o modelo já foi treinado. Por isso, ele recebe um índice inicial e um índice final. Ele também pode receber o número de etapas anteriores para as quais desejamos realizar previsões (avaliações).

predicted = arima_model.predict(start=1, end=len(train))
print(arima_model.predict(steps=10))

Para prever valores futuros, precisamos utilizar um método chamado forecast(). 

Como mencionado anteriormente, os modelos tradicionais de séries temporais, como o ARIMA, dependem dos valores anteriores para prever os próximos valores, conforme podemos observar em sua fórmula na Figura 03.

Isso significa que precisamos atualizar constantemente o modelo com novas informações para que ele continue relevante. Por exemplo, para que um modelo ARIMA faça uma previsão do preço de fechamento do EURUSD de amanhã, ele precisa receber o preço de fechamento de hoje desse mesmo instrumento (símbolo). O mesmo procedimento se aplica ao dia seguinte e aos demais dias.

Essa abordagem é bastante diferente daquela utilizada no aprendizado de máquina clássico.

Agora, vamos realizar previsões com dados fora da amostra.

# Fit initial model

model = ARIMA(train, order=(0, 1, 0)) 
results = model.fit()

# Initialize forecasts
forecasts = [results.forecast(steps=1).iloc[0]]  # First forecast

# Update with test data iteratively
for i in range(len(test)):
    # Append new observation without refitting
    results = results.append(test.iloc[i:i+1], refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])

forecasts = forecasts[:-1] # remove the last element which is the predicted next value 

# Compare forecasts vs actual test data
plt.plot(test.index, test, label="Actual")
plt.plot(test.index, forecasts, label="Forecast", linestyle="--")
plt.legend()

O método append insere novas informações (dados atuais) no modelo. Nesse caso, o preço de fechamento atual do EURUSD é utilizado para prever o próximo preço de fechamento. 

O parâmetro refit=False garante que o modelo não seja treinado novamente. Isso torna o processo uma maneira eficiente de atualizar o modelo ARIMA.

Vamos criar uma função com algumas métricas de avaliação que poderemos utilizar para analisar o desempenho do modelo ARIMA.

import sklearn.metrics as metric
from statsmodels.tsa.stattools import acf
from scipy.stats import pearsonr

def forecast_accuracy(forecast, actual):
    # Convert to numpy arrays if they aren't already
    forecast = np.asarray(forecast)
    actual = np.asarray(actual)
    
    metrics = {
        'mape': metric.mean_absolute_percentage_error(actual, forecast),
        'me': np.mean(forecast - actual),  # Mean Error
        'mae': metric.mean_absolute_error(actual, forecast),
        'mpe': np.mean((forecast - actual) / actual),  # Mean Percentage Error
        'rmse': metric.mean_squared_error(actual, forecast, squared=False),
        'corr': pearsonr(forecast, actual)[0],  # Pearson correlation
        'minmax': 1 - np.mean(np.minimum(forecast, actual) / np.maximum(forecast, actual)),
        'acf1': acf(forecast - actual, nlags=1)[1],  # ACF of residuals at lag 1
        "r2_score": metric.r2_score(forecast, actual)
    }

    return metrics
forecast_accuracy(forecasts, test)

Saídas.

{'mape': 0.0034114761554881936,
 'me': 6.360279441117738e-05,
 'mae': 0.0037872155688622737,
 'mpe': 6.825424905960248e-05,
 'rmse': 0.005018824533752777,
 'corr': 0.99656297100796,
 'minmax': 0.0034008221524469695,
 'acf1': 0.04637470541528736,
 'r2_score': 0.9931220697334551}

O valor de MAPE de 0,003 indica que o modelo apresenta uma precisão aproximada de 99,996%. Um valor aproximadamente equivalente também pode ser observado no r2_score.

Abaixo, apresentamos o gráfico que compara os valores reais e previstos no conjunto de teste.


Gráficos dos Resíduos do Modelo ARIMA

O ARIMA disponibiliza métodos para visualizar os resíduos, permitindo compreender melhor o comportamento do modelo.

results.plot_diagnostics(figsize=(8,8))
plt.show()

Saídas.

Resíduos Padronizados

Os erros residuais parecem oscilar em torno de uma média igual a zero e apresentam variância uniforme.

Histograma

O gráfico de densidade sugere uma distribuição normal, com a média ligeiramente deslocada para a direita.

Quantis Teóricos

A maioria dos pontos está perfeitamente alinhada com a linha vermelha. Desvios significativos indicariam que a distribuição apresenta assimetria.

Correlograma

O correlograma (ou gráfico da ACF) mostra que os erros residuais não apresentam autocorrelação. Caso o gráfico da ACF indicasse a presença de algum padrão nos erros residuais que não fosse explicado pelo modelo, precisaríamos procurar outras variáveis X (preditoras) para incluí-las no modelo.

De modo geral, o modelo parece apresentar um bom ajuste aos dados.


Modelo SARIMA

O modelo ARIMA convencional apresenta uma limitação. Ele não considera a sazonalidade.

A sazonalidade refere-se a padrões recorrentes nos dados financeiros que se repetem em intervalos regulares, como horários, diários, semanais, mensais, trimestrais e anuais.

Frequentemente, observamos determinados instrumentos apresentando padrões repetitivos. Por exemplo, as ações de empresas do setor varejista costumam apresentar altas no quarto trimestre (período de compras de fim de ano), enquanto algumas ações do setor de energia podem acompanhar padrões climáticos sazonais. No mercado Forex, podemos identificar um aumento da volatilidade durante determinadas sessões de negociação, entre outros exemplos.

Se os dados da série temporal apresentarem uma sazonalidade observável ou definida, devemos optar pelo modelo ARIMA Sazonal (SARIMA), pois ele utiliza a diferenciação sazonal.

Componentes do modelo SARIMAX(p, d, q)x(P, D, Q, S)

  • Autorregressão (AR)

    Como descrito anteriormente, a autorregressão analisa os valores históricos da série temporal para prever os valores atuais.

  • Médias Móveis (MA)

    O componente de médias móveis continua modelando os erros de previsões anteriores.

  • Integração (I)

    A integração está sempre presente para tornar a série temporal estacionária.

  • Componente Sazonal (S)

    O componente sazonal identifica variações que se repetem em intervalos regulares.

A diferenciação sazonal é semelhante à diferenciação convencional. Entretanto, em vez de subtrair termos consecutivos, subtraímos o valor correspondente ao período sazonal anterior.

Antes de utilizar o modelo SARIMAX, vamos determinar os parâmetros adequados por meio de auto_arima.

from pmdarima.arima import auto_arima

# Auto-fit SARIMA (automatically detects P, D, Q, S)

auto_model = auto_arima(
    series,
    seasonal=True,          # Enable seasonality
    m=5,                    # Weeky cycle (5 days) for daily data
    trace=True,             # Show search progress
    stepwise=True,          # Faster optimization
    suppress_warnings=True,
    error_action="ignore"
)

print(auto_model.summary())

Saídas.

Executando uma busca passo a passo para minimizar o AIC
 ARIMA(2,1,2)(1,0,1)[5] intercept   : AIC=-35529.092, Time=3.81 sec
 ARIMA(0,1,0)(0,0,0)[5] intercept   : AIC=-35537.068, Time=0.29 sec
 ARIMA(1,1,0)(1,0,0)[5] intercept   : AIC=-35536.573, Time=0.97 sec
 ARIMA(0,1,1)(0,0,1)[5] intercept   : AIC=-35536.570, Time=4.38 sec
 ARIMA(0,1,0)(0,0,0)[5]             : AIC=-35538.731, Time=0.21 sec
 ARIMA(0,1,0)(1,0,0)[5] intercept   : AIC=-35536.048, Time=0.67 sec
 ARIMA(0,1,0)(0,0,1)[5] intercept   : AIC=-35536.024, Time=0.87 sec
 ARIMA(0,1,0)(1,0,1)[5] intercept   : AIC=-35534.248, Time=0.92 sec
 ARIMA(1,1,0)(0,0,0)[5] intercept   : AIC=-35537.492, Time=0.37 sec
 ARIMA(0,1,1)(0,0,0)[5] intercept   : AIC=-35537.511, Time=0.55 sec
 ARIMA(1,1,1)(0,0,0)[5] intercept   : AIC=-35535.683, Time=0.57 sec

Best model:  ARIMA(0,1,0)(0,0,0)[5]          
Total fit time: 13.656 seconds
                               SARIMAX Results                                
==============================================================================
Dep. Variable:                      y   No. Observations:                 5009
Model:               SARIMAX(0, 1, 0)   Log Likelihood               17770.365
Date:                Tue, 27 May 2025   AIC                         -35538.731
Time:                        11:16:40   BIC                         -35532.212
Sample:                             0   HQIC                        -35536.446
                               - 5009                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2      4.846e-05   6.06e-07     80.005      0.000    4.73e-05    4.96e-05
===================================================================================
Ljung-Box (L1) (Q):                   2.42   Jarque-Bera (JB):              2028.68
Prob(Q):                              0.12   Prob(JB):                         0.00
Heteroskedasticity (H):               0.34   Skew:                             0.08
Prob(H) (two-sided):                  0.00   Kurtosis:                         6.11
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Embora não seja necessário ajustar o modelo manualmente mais uma vez, já que auto_arima retorna um modelo SARIMAX, realizar esse ajuste manual nos proporciona maior controle sobre os resultados. Portanto, vamos repetir o procedimento.

from statsmodels.tsa.statespace.sarimax import SARIMAX

model = SARIMAX(
    train,
    order=auto_model.order,                  # Non-seasonal (p,d,q)
    seasonal_order=auto_model.order+(5,),      # Seasonal (P,D,Q,S)
    enforce_stationarity=False
)

results = model.fit()
print(results.summary())

Saídas.

                                     SARIMAX Results                                     
=========================================================================================
Dep. Variable:                             Close   No. Observations:                 4007
Model:             SARIMAX(0, 1, 0)x(0, 1, 0, 5)   Log Likelihood               12613.829
Date:                           Tue, 27 May 2025   AIC                         -25225.658
Time:                                   11:16:41   BIC                         -25219.364
Sample:                                        0   HQIC                        -25223.427
                                          - 4007                                         
Covariance Type:                             opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2         0.0001   1.68e-06     63.423      0.000       0.000       0.000
===================================================================================
Ljung-Box (L1) (Q):                   3.42   Jarque-Bera (JB):               676.61
Prob(Q):                              0.06   Prob(JB):                         0.00
Heteroskedasticity (H):               0.48   Skew:                            -0.01
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.01
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Como a ordem retornada por auto_model corresponde ao ARIMA(p,d,q), mesmo quando definimos o parâmetro seasonal como true. Precisamos acrescentar o valor 5 à tupla ao declarar o modelo SARIMAX, garantindo que a ordem sazonal seja especificada corretamente como (P, D, Q, S), neste caso acrescentando S=5 à tupla sazonal.

Antes de visualizar e analisar os resultados reais e previstos, precisamos remover do nosso array os primeiros elementos correspondentes ao tamanho da janela sazonal. Os valores anteriores a esse ponto estão incompletos.

predicted = results.predict(start=1, end=len(train))

clean_train = train[5:]
clean_predicted = predicted[5:]

plt.figure(figsize=(7,4))
plt.plot(clean_train.index[5:], clean_train[5:], label='Actual')
plt.plot(clean_train.index[5:], clean_predicted[5:], label='Forecasted mean', linestyle='--')
plt.title('Actual vs Forecast')
plt.legend()
plt.savefig("sarimax train actual&forecast plot.png")
plt.show()

Saídas.

Podemos avaliar esse modelo da mesma maneira que avaliamos o modelo ARIMA.

# Initialize forecasts
forecasts = [results.forecast(steps=1).iloc[0]]  # First forecast

# Update with test data iteratively
for i in range(len(test)):
    # Append new observation without refitting
    results = results.append(test.iloc[i:i+1], refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])

clean_test = test[5:]
forecasts = forecasts[5:-1] # remove the last element which is the predicted next value and the first 5 items
forecast_accuracy(forecasts, clean_test)

Saídas.

{'mape': 0.004900183060803821,
 'me': -6.94082142749275e-06,
 'mae': 0.005432456867698095,
 'mpe': -7.226495372320155e-06,
 'rmse': 0.007127465498996785,
 'corr': 0.9931778828074744,
 'minmax': 0.004880027322298863,
 'acf1': 0.10724254539104018,
 'r2_score': 0.9864021833085908}

Uma precisão de 98,6%, de acordo com o r2_score. Um resultado satisfatório.

Por fim, podemos utilizar o modelo ARIMA para realizar previsões em tempo real com base nos dados recebidos do MetaTrader 5.

Primeiramente, precisamos importar a biblioteca schedule para nos ajudar a executar as previsões após o intervalo de um dia, já que treinamos esse modelo utilizando o período gráfico diário.

import schedule
# Make realtime predictions based on the recent data from MetaTrader5

def predict_close():
    
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1) 
    if not rates:
        print(f"Failed to get recent OHLC values, error = {mt5.last_error}")
        time.sleep(60)
    
    rates_df = pd.DataFrame(rates)    
    
    global results # Get the variable globally, outside the function
    global forecasts
    
    # Append new observation to the model without refitting
    
    new_obs_value = rates_df["close"].iloc[-1]
    new_obs_index = results.data.endog.shape[0]  # continue integer index
    
    new_obs = pd.Series([new_obs_value], index=[new_obs_index]) # Its very important to continue making predictions where we ended on the training data
    results = results.append(new_obs, refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])
    print(f"Current Close Price: {new_obs_value} Forecasted next day Close Price: {forecasts[-1]}")
    

Agendando a execução das previsões.

schedule.every(1).days.do(predict_close) # call the predict function after a given time

while True: 
    
    schedule.run_pending()    
    time.sleep(60)

mt5.shutdown()

Saídas.

Current Close Price: 1.1374900000000001 Forecasted next day Close Price: 1.1337899981049262
Current Close Price: 1.1372200000000001 Forecasted next day Close Price: 1.1447100065656721

Com esses preços de fechamento previstos, você pode expandir essa abordagem para desenvolver uma estratégia de negociação e executar operações utilizando a integração MetaTrader5-Python.



Considerações Finais

Tanto o ARIMA quanto o SARIMA são modelos tradicionais sólidos de séries temporais, utilizados em diversas áreas e setores. No entanto, é fundamental compreender suas limitações e desvantagens, incluindo:

  • Pressupõem estacionariedade (após a diferenciação)

    Nem sempre trabalhamos com dados estacionários e, muitas vezes, queremos utilizar os dados em sua forma original. A diferenciação pode distorcer a estrutura natural e as tendências que esperamos identificar.

  • Pressuposto de linearidade

    O ARIMA é essencialmente um modelo linear. Ele pressupõe que os valores futuros dependem linearmente dos valores defasados e dos erros anteriores. Esse pressuposto nem sempre corresponde ao comportamento dos mercados financeiros e do Forex, nos quais padrões complexos são frequentemente observados. Isso significa que esses modelos podem apresentar um desempenho insatisfatório em determinadas situações.

  • Modelos univariados

    Ambos os modelos consideram apenas um recurso por vez. Sabemos que os mercados financeiros são complexos e que precisamos de múltiplos recursos e perspectivas para analisá-los. Esses modelos analisam o mercado sob uma única perspectiva (unidimensional), fazendo com que deixemos de considerar outros recursos que poderiam ser úteis.

    Embora seja possível adicionar variáveis exógenas ao modelo SARIMAX, isso muitas vezes não é suficiente.

Apesar de suas limitações, quando utilizados com os parâmetros, o tipo de problema e as informações adequados, esses modelos podem apresentar resultados surpreendentes. Um modelo ARIMA simples pode superar modelos complexos, como as redes neurais recorrentes (RNNs), na previsão de séries temporais.

Atenciosamente.


Fontes e Referências


Tabela de Anexos

Nome do arquivo Descrição e Utilização
forex_ts_forecasting_using_arima.py Script Python que contém todos os exemplos apresentados, desenvolvidos na linguagem Python.
requirements.txt  Arquivo de texto que contém as dependências do Python e seus respectivos números de versão.


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

Últimos Comentários | Ir para discussão (1)
Retail Trading Realities LTD
Philip Kym Sang Nelson | 22 mar. 2026 em 15:16

Conteúdo incrível.

Exatamente o que eu estava procurando.

Provavelmente não vou usar isso nas negociações, mas é muito interessante.

Ouvi falar pela primeira vez sobre o ARIMA no livro “Trading Systems & Methods”, de Perry J. Kaufman.

Alguém já teve sucesso operando com o ARIMA?

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.
Machine Learning e Data Science (Parte 45): Previsão de séries temporais no Forex com o modelo PROPHET do Facebook Machine Learning e Data Science (Parte 45): Previsão de séries temporais no Forex com o modelo PROPHET do Facebook
O modelo Prophet, desenvolvido pelo Facebook, permite prever séries temporais e identificar tendências, padrões sazonais e efeitos de feriados com o mínimo de ajustes manuais. Esse método é amplamente utilizado na previsão de demanda e no planejamento empresarial. Neste artigo, vamos investigar a eficácia do modelo Prophet na previsão da volatilidade de instrumentos cambiais e avaliar se ele também pode ser aplicado fora do contexto tradicional de negócios.
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.
Desenvolvimento de ferramentas para análise de Price Action (Parte 29): EA "Boom and Crash Interceptor" Desenvolvimento de ferramentas para análise de Price Action (Parte 29): EA "Boom and Crash Interceptor"
Descubra como o EA Boom & Crash Interceptor transforma seus gráficos em um sistema proativo de alertas, capaz de identificar movimentos explosivos dos preços por meio da análise de velocidade, da detecção de picos de volatilidade, da confirmação de tendência e de filtros de zonas de pivô. Com setas verdes para "Boom" e vermelhas para "Crash", o EA facilita a tomada de decisões ao filtrar o ruído do mercado e ajudar a aproveitar melhor os movimentos bruscos dos preços. Vamos entender como ele funciona e por que essa ferramenta pode se tornar um diferencial importante nas suas operações.