1. Introdução¶

O mercado financeiro é um ambiente dinâmico e altamente influenciado por uma ampla gama de fatores, incluindo indicadores econômicos, políticas governamentais, eventos globais e o comportamento dos investidores. Dentro desse contexto, prever o movimento futuro da bolsa de valores, especificamente do índice Ibovespa, é um desafio complexo que vai muito além da simples análise dos dados históricos. Se fosse possível prever com 100% de precisão o comportamento do mercado, aqueles que dominassem essa habilidade certamente estariam entre as pessoas mais ricas do mundo.

Neste trabalho, utilizaremos uma base de dados do Ibovespa com um período de 10 anos para construir modelos de previsão utilizando técnicas de séries temporais. Os modelos escolhidos para essa tarefa são o ARIMA (Autoregressive Integrated Moving Average) e o SARIMA (Seasonal Autoregressive Integrated Moving Average), que são amplamente utilizados para analisar padrões e tendências em dados temporais. Nosso objetivo é desenvolver um modelo que possa atingir uma taxa de acerto acima de 70%, oferecendo previsões que, embora não sejam infalíveis, possam auxiliar na tomada de decisões estratégicas no mercado de ações.

2. Importação de Bibliotecas e Carregamento dos Dados¶

  • pandas Manipulação de dados tabulares (DataFrames).
  • numpy Cálculos numéricos e manipulação de arrays/matrizes.
  • matplotlib Gráficos básicos (linha, barras, dispersão).
  • seaborn Visualizações estatísticas avançadas e estilizadas.
  • scikit-learn Machine Learning (classificação, regressão, clustering).
  • statsmodels Modelos estatísticos e séries temporais (ARIMA, regressão).
In [1]:
# instalando os pacotes necessários
#pip install pandas numpy matplotlib seaborn scikit-learn statsmodels
In [76]:
from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
from sklearn.metrics import mean_absolute_error, mean_squared_error, mean_absolute_percentage_error
from statsmodels.stats.diagnostic import acorr_ljungbox
from statsmodels.graphics.tsaplots import plot_acf

import warnings
warnings.filterwarnings("ignore")
In [77]:
df = pd.read_csv('data.csv')
df.head()
Out[77]:
Data Último Abertura Máxima Mínima Vol. Var%
0 30.12.2024 120.283 120.267 121.050 120.158 8,90M 0,01%
1 27.12.2024 120.269 121.078 121.609 120.252 8,94M -0,67%
2 26.12.2024 121.078 120.767 121.612 120.428 8,34M 0,26%
3 23.12.2024 120.767 122.105 122.105 120.617 9,95M -1,09%
4 20.12.2024 122.102 121.183 122.209 120.700 18,13M 0,75%
  • Invertemos o DataFrame para que os dados fiquem organizados em ordem crescente, garantindo que as datas sejam apresentadas do mais antigo para o mais recente.
  • Definimos a variável data como o índice de nosso data set.
In [78]:
df['Data'] = pd.to_datetime(df['Data'])
df.set_index('Data', inplace=True)  # definindo a data como índice
df.head()   
Out[78]:
Último Abertura Máxima Mínima Vol. Var%
Data
2024-12-30 120.283 120.267 121.050 120.158 8,90M 0,01%
2024-12-27 120.269 121.078 121.609 120.252 8,94M -0,67%
2024-12-26 121.078 120.767 121.612 120.428 8,34M 0,26%
2024-12-23 120.767 122.105 122.105 120.617 9,95M -1,09%
2024-12-20 122.102 121.183 122.209 120.700 18,13M 0,75%
In [79]:
df = df.iloc[::-1]
In [80]:
df.head()
Out[80]:
Último Abertura Máxima Mínima Vol. Var%
Data
2015-01-02 48.512 50.004 50.004 48.345 2,88M -2,99%
2015-01-05 47.517 48.512 48.512 47.264 3,87M -2,05%
2015-01-06 48.001 47.517 48.061 47.338 4,56M 1,02%
2015-01-07 49.463 48.006 49.882 48.006 4,41M 3,05%
2015-01-08 49.943 49.463 50.261 49.017 3,62M 0,97%
In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2479 entries, 2015-01-02 to 2024-12-30
Data columns (total 6 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Último    2479 non-null   float64
 1   Abertura  2479 non-null   float64
 2   Máxima    2479 non-null   float64
 3   Mínima    2479 non-null   float64
 4   Vol.      2478 non-null   object 
 5   Var%      2479 non-null   object 
dtypes: float64(4), object(2)
memory usage: 135.6+ KB
In [ ]:
# Plot dos dados originais
plt.figure(figsize=(14, 7))
plt.plot(df.index, df["Último"], label="Valor de Fechamento")
plt.title("Fechamento IBOVESPA (2015-2025)")
plt.xlabel("Data")
plt.ylabel("Fechamento(R$)")
plt.legend()
plt.show()
No description has been provided for this image

3. Decomposição da Série Temporal¶

  • Tendência: Representa o movimento de longo prazo nos dados.
  • Sazonalidade: Padrões que se repetem em intervalos regulares.
  • Resíduos: Variações aleatórias ou ruído nos dados.

Primeiro vamos análisar com o modelo "additive" e um periodo de um ano (253 dias úteis)

In [86]:
# Decomposição aditiva
resultado = seasonal_decompose(df["Último"], model="additive", period=253)

# Plot da decomposição
ax = resultado.plot()
ax.get_figure().set_size_inches(15, 7)
No description has been provided for this image

Observamos uma tendência de alta, uma sazonalidade bem definida e um ruído contínuo, conforme evidenciado em nosso gráfico.

Nossa decomposição utilizando o modelo "additive" apresentou um ruído menos disperso do que o esperado. Agora, vamos analisá-la com o modelo "multiplicative" para verificar possíveis diferenças

In [90]:
# Decomposição aditiva
resultado = seasonal_decompose(df["Último"], model="multiple", period=253)

# Plot da decomposição
ax = resultado.plot()
ax.get_figure().set_size_inches(15, 7)
No description has been provided for this image

Observamos que, ao utilizar o modelo "multiplicative", os resíduos deixam de oscilar em torno de zero, indicando uma possível mudança na estrutura da decomposição.

Vamos retornar ao modelo "additive", agora considerando um período de 1 mês (20 dias úteis) para ver como os dados de comportam

In [91]:
# Decomposição aditiva
resultado = seasonal_decompose(df["Último"], model="additive", period=20)

# Plot da decomposição
ax = resultado.plot()
ax.get_figure().set_size_inches(15, 7)
No description has been provided for this image

Com o período de um mês, os resíduos se aproximam mais de uma distribuição normal, e a sazonalidade apresenta mudanças significativas, refletindo melhor a estrutura dos dados.

4. Teste de Estacionariedade¶

Vamos realizar um teste de estacionariedade em nossos dados para verificar se podemos utilizá-los diretamente no modelo ARIMA ou se será necessário aplicar diferenciação.

In [ ]:
# Teste ADF
resultado_adf = adfuller(df["Último"].dropna())
print(f"Estatística ADF: {resultado_adf[0]}")
print(f"Valor-p: {100*resultado_adf[1]:.2f}%")
# for key, value in resultado_adf[4].items():
#     print(f"Valor Crítico {key}: {value}")

# Interpretação
if resultado_adf[1] < 0.05:
    print("A série é estacionária.")
else:
    print("A série não é estacionária.")
Estatística ADF: -1.6164309707909765
Valor-p: 47.46%
A série não é estacionária.

Como podemos observar, será necessário aplicar uma diferenciação antes de utilizar o modelo ARIMA

5. Estacionarização da Série Temporal¶

Vamos aplicar dois tipos de diferenciação: a de primeira ordem e a sazonal, para analisar como cada uma impacta a série e seu comportamento.

  • Diferenciação de Primeira Ordem
In [ ]:
# Diferenciação de primeira ordem
df["ultimo_diff"] = df["Último"] - df["Último"].shift(1)
df["ultimo_diff"] = df["ultimo_diff"].dropna()
# df["tavg_diff"] = df["tavg"].diff().dropna()

# Plot da série diferenciada
plt.figure(figsize=(14, 7))
plt.plot(df.index, df["ultimo_diff"], label="Série Diferenciada (1ª Ordem)")
plt.title("Série Diferenciada (1ª Ordem)")
plt.xlabel("Data")
plt.ylabel("Diferença de Temperatura (°C)")
plt.legend()
plt.show()
No description has been provided for this image
  • Diferenciação Sazonal
In [ ]:
# Diferenciação sazonal (período de 365 dias)
df["ultimo_seasonal_diff"] = df["Último"] - df["Último"].shift(365)
df["ultimo_seasonal_diff"] = df["ultimo_seasonal_diff"].dropna()

# Plot da série sazonalmente diferenciada
plt.figure(figsize=(14, 7))
plt.plot(
    df.index, df["ultimo_seasonal_diff"], label="Série Diferenciada Sazonalmente"
)
plt.title("Série Diferenciada Sazonalmente")
plt.xlabel("Data")
plt.ylabel("Diferença de Temperatura (°C)")
plt.legend()
Out[ ]:
<matplotlib.legend.Legend at 0x237991032c0>
No description has been provided for this image

Agora, vamos aplicar o teste de estacionariedade ADF em ambas as séries para análise.

  • Diferenciação de Primeira Ordem
In [ ]:
# Teste ADF na série diferenciada

resultado_adf_diff = adfuller(df["ultimo_diff"].dropna())
print(f"Estatística ADF: {resultado_adf_diff[0]}")
print(f"Valor-p: {100*resultado_adf_diff[1]:.4}%")

# Interpretação
if resultado_adf_diff[1] < 0.05:
    print("A série diferenciada é estacionária.")
else:
    print("A série diferenciada não é estacionária.")
Estatística ADF: -15.923310753216212
Valor-p: 7.879e-27%
A série diferenciada é estacionária.
  • Diferenciação Sazonal
In [ ]:
# Teste ADF na série diferenciada

resultado_adf_diff = adfuller(df["ultimo_seasonal_diff"].dropna())
print(f"Estatística ADF: {resultado_adf_diff[0]}")
print(f"Valor-p: {100*resultado_adf_diff[1]:.4}%")

# Interpretação
if resultado_adf_diff[1] < 0.05:
    print("A série diferenciada é estacionária.")
else:
    print("A série diferenciada não é estacionária.")
Estatística ADF: -3.639258929767526
Valor-p: 0.5053%
A série diferenciada é estacionária.

6. Identificação dos Parâmetros do Modelo¶

Para utilizar o modelo ARIMA, precisamos identificar os parâmetros "q" e "p". Para isso, usaremos a Função de Autocorrelação (ACF) para determinar "q" e a Função de Autocorrelação Parcial (PACF) para identificar "p".

In [ ]:
# Plot da ACF e PACF da série original

plt.figure(figsize=(14, 6))
plt.subplot(121)
plot_acf(df["Último"].dropna(), ax=plt.gca(), lags=253)
plt.title("Função de Autocorrelação (ACF)")
plt.subplot(122)
plot_pacf(df["Último"].dropna(), ax=plt.gca(), lags=50)

plt.title("Função de Autocorrelação Parcial (PACF)")
plt.show()
No description has been provided for this image

Com esses gráficos, podemos observar que os momentos de normalidade para "q" e "p" ocorrem, respectivamente, entre os lags 210 e 4. Vamos ampliar os gráficos para visualizar com mais precisão em quais lags esses momentos se encontram.

In [ ]:
import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import pacf

# Criar a figura
plt.figure(figsize=(14, 6))

# Plot ACF
plt.subplot(121)
plot_acf(df["Último"].dropna(), ax=plt.gca(), lags=253)
plt.title("Função de Autocorrelação (ACF)")

# Plot PACF com rótulos nos pontos
plt.subplot(122)
ax_pacf = plt.gca()
plot_pacf(df["Último"].dropna(), ax=ax_pacf, lags=5)

# Obtendo valores PACF para rotulagem
pacf_values = pacf(df["Último"].dropna(), nlags=5)
lags = range(51)  # Inclui o lag 0

# Adicionando rótulos nos pontos do PACF
for i, value in enumerate(pacf_values):
    ax_pacf.annotate(f'{value:.2f}', (i, value), textcoords="offset points", xytext=(0, 5), ha='center')

plt.title("Função de Autocorrelação Parcial (PACF)")
plt.show()
No description has been provided for this image

Podemos observar, por meio da equação PACF, que o momento de normalidade do parâmetro "p" ocorre no lag 2. Agora, vamos ampliar o gráfico de ACF para identificar com mais precisão o lag correspondente ao momento de normalidade do parâmetro "q".

In [ ]:
import matplotlib.pyplot as plt
import numpy as np
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import pacf

# Criar a figura
plt.figure(figsize=(28, 6))

# Plot ACF com grid no eixo x e rótulos de 10 em 10
plt.subplot(121)
ax_acf = plt.gca()
plot_acf(df["Último"].dropna(), ax=ax_acf, lags=253)
ax_acf.set_title("Função de Autocorrelação (ACF)")
ax_acf.grid(axis='x', linestyle='--', alpha=0.7)  # Adicionando grid no eixo x
ax_acf.set_xticks(np.arange(0, 254, 10))  # Define os rótulos do eixo x de 10 em 10


plt.show()
No description has been provided for this image

Definindo os parametos: Utilizamos $d = 1$ porque é necessário diferenciar os dados uma vez para torná-los estacionários.

In [ ]:
p = 2
q = 210
d = 1

7. Aplicando modelo ARIMA¶

In [ ]:
# Ajuste do modelo ARIMA(p, d, q)
# Suposição: p=9, d=0, q=20 (com base na análise dos gráficos ACF e PACF)
model_arima = ARIMA(df["Último"], order=(p, d, q))
resultado_arima = model_arima.fit()

# Resumo do modelo
print(resultado_arima.summary())
                               SARIMAX Results                                
==============================================================================
Dep. Variable:                 Último   No. Observations:                 2479
Model:               ARIMA(2, 1, 210)   Log Likelihood               -4040.006
Date:                Fri, 21 Mar 2025   AIC                           8506.012
Time:                        05:46:32   BIC                           9744.651
Sample:                             0   HQIC                          8955.898
                               - 2479                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
ar.L1         -0.1908      1.544     -0.124      0.902      -3.216       2.835
ar.L2         -0.1834      0.379     -0.484      0.628      -0.926       0.559
ma.L1          0.1041      1.544      0.067      0.946      -2.922       3.130
ma.L2          0.2029      0.438      0.464      0.643      -0.655       1.061
ma.L3          0.0181      0.077      0.236      0.813      -0.132       0.169
ma.L4         -0.0217      0.046     -0.475      0.635      -0.111       0.068
ma.L5          0.0133      0.059      0.226      0.821      -0.102       0.128
ma.L6         -0.0373      0.035     -1.071      0.284      -0.106       0.031
ma.L7          0.0973      0.057      1.703      0.089      -0.015       0.209
ma.L8          0.0183      0.162      0.113      0.910      -0.299       0.336
ma.L9          0.0229      0.040      0.567      0.570      -0.056       0.102
ma.L10         0.0312      0.021      1.457      0.145      -0.011       0.073
ma.L11         0.0281      0.050      0.566      0.572      -0.069       0.125
ma.L12         0.0230      0.038      0.603      0.546      -0.052       0.098
ma.L13        -0.0553      0.028     -1.995      0.046      -0.110      -0.001
ma.L14         0.0027      0.099      0.027      0.978      -0.191       0.196
ma.L15        -0.0246      0.041     -0.604      0.546      -0.105       0.055
ma.L16         0.0189      0.033      0.580      0.562      -0.045       0.083
ma.L17         0.0151      0.039      0.389      0.697      -0.061       0.091
ma.L18         0.0175      0.030      0.582      0.561      -0.042       0.077
ma.L19        -0.0158      0.027     -0.581      0.561      -0.069       0.037
ma.L20        -0.0311      0.039     -0.789      0.430      -0.108       0.046
ma.L21         0.0098      0.048      0.202      0.840      -0.085       0.105
ma.L22        -0.0248      0.042     -0.594      0.552      -0.107       0.057
ma.L23        -0.0330      0.044     -0.754      0.451      -0.119       0.053
ma.L24        -0.0023      0.053     -0.044      0.965      -0.106       0.101
ma.L25        -0.0380      0.032     -1.193      0.233      -0.100       0.024
ma.L26         0.0043      0.058      0.075      0.940      -0.109       0.117
ma.L27         0.0118      0.032      0.365      0.715      -0.052       0.075
ma.L28         0.0115      0.035      0.332      0.740      -0.056       0.079
ma.L29         0.0587      0.024      2.467      0.014       0.012       0.105
ma.L30        -0.0319      0.086     -0.371      0.711      -0.201       0.137
ma.L31        -0.0325      0.078     -0.418      0.676      -0.185       0.120
ma.L32        -0.0152      0.054     -0.283      0.777      -0.120       0.090
ma.L33         0.0073      0.026      0.277      0.782      -0.044       0.059
ma.L34         0.0622      0.031      1.993      0.046       0.001       0.123
ma.L35         0.0071      0.095      0.075      0.940      -0.178       0.193
ma.L36         0.0218      0.035      0.618      0.537      -0.047       0.091
ma.L37         0.0161      0.031      0.515      0.606      -0.045       0.077
ma.L38         0.0078      0.033      0.238      0.812      -0.057       0.072
ma.L39         0.0464      0.026      1.800      0.072      -0.004       0.097
ma.L40        -0.0208      0.070     -0.296      0.767      -0.159       0.117
ma.L41        -0.0072      0.059     -0.123      0.902      -0.122       0.108
ma.L42        -0.0056      0.029     -0.195      0.845      -0.061       0.050
ma.L43        -0.0174      0.024     -0.721      0.471      -0.065       0.030
ma.L44         0.0010      0.035      0.029      0.977      -0.069       0.071
ma.L45        -0.0245      0.026     -0.955      0.340      -0.075       0.026
ma.L46         0.0889      0.043      2.056      0.040       0.004       0.174
ma.L47        -0.0208      0.150     -0.138      0.890      -0.315       0.274
ma.L48        -0.0145      0.076     -0.190      0.849      -0.164       0.135
ma.L49        -0.0470      0.045     -1.043      0.297      -0.135       0.041
ma.L50         0.0237      0.058      0.407      0.684      -0.090       0.138
ma.L51        -0.0126      0.064     -0.196      0.845      -0.139       0.114
ma.L52        -0.0270      0.036     -0.745      0.456      -0.098       0.044
ma.L53        -0.0366      0.051     -0.717      0.473      -0.137       0.064
ma.L54        -0.0264      0.048     -0.548      0.584      -0.121       0.068
ma.L55         0.0104      0.032      0.323      0.747      -0.053       0.074
ma.L56        -0.0197      0.041     -0.483      0.629      -0.100       0.060
ma.L57        -0.0010      0.041     -0.025      0.980      -0.081       0.079
ma.L58        -0.0019      0.025     -0.076      0.939      -0.051       0.047
ma.L59        -0.0189      0.024     -0.789      0.430      -0.066       0.028
ma.L60        -0.0315      0.037     -0.841      0.400      -0.105       0.042
ma.L61        -0.0123      0.049     -0.253      0.800      -0.108       0.083
ma.L62        -0.0447      0.026     -1.733      0.083      -0.095       0.006
ma.L63        -0.0168      0.065     -0.258      0.796      -0.144       0.111
ma.L64        -0.0321      0.029     -1.118      0.263      -0.088       0.024
ma.L65         0.0333      0.042      0.788      0.431      -0.050       0.116
ma.L66         0.0076      0.069      0.109      0.913      -0.128       0.143
ma.L67        -0.0074      0.029     -0.256      0.798      -0.064       0.049
ma.L68        -0.0210      0.034     -0.624      0.533      -0.087       0.045
ma.L69        -0.0070      0.037     -0.190      0.849      -0.079       0.065
ma.L70        -0.0119      0.024     -0.500      0.617      -0.059       0.035
ma.L71        -0.0423      0.028     -1.518      0.129      -0.097       0.012
ma.L72        -0.0056      0.065     -0.087      0.931      -0.132       0.121
ma.L73         0.0290      0.030      0.966      0.334      -0.030       0.088
ma.L74        -0.0047      0.063     -0.074      0.941      -0.129       0.119
ma.L75         0.0155      0.036      0.426      0.670      -0.056       0.087
ma.L76        -0.0506      0.032     -1.576      0.115      -0.113       0.012
ma.L77         0.0120      0.084      0.143      0.886      -0.152       0.176
ma.L78        -0.0516      0.049     -1.062      0.288      -0.147       0.044
ma.L79         0.0150      0.078      0.193      0.847      -0.138       0.168
ma.L80        -0.0606      0.047     -1.301      0.193      -0.152       0.031
ma.L81         0.0072      0.092      0.078      0.938      -0.172       0.187
ma.L82        -0.0404      0.043     -0.937      0.349      -0.125       0.044
ma.L83        -0.0219      0.058     -0.380      0.704      -0.135       0.091
ma.L84        -0.0538      0.039     -1.394      0.163      -0.130       0.022
ma.L85        -0.0389      0.070     -0.558      0.577      -0.176       0.098
ma.L86         0.0016      0.049      0.032      0.974      -0.094       0.097
ma.L87        -0.0126      0.039     -0.324      0.746      -0.089       0.064
ma.L88        -0.0516      0.031     -1.668      0.095      -0.112       0.009
ma.L89        -0.0254      0.084     -0.301      0.763      -0.191       0.140
ma.L90        -0.0350      0.033     -1.060      0.289      -0.100       0.030
ma.L91        -0.0159      0.043     -0.373      0.709      -0.099       0.068
ma.L92        -0.0194      0.028     -0.690      0.491      -0.075       0.036
ma.L93        -0.0152      0.032     -0.469      0.639      -0.079       0.048
ma.L94         0.0006      0.029      0.021      0.984      -0.057       0.058
ma.L95        -0.0082      0.027     -0.298      0.766      -0.062       0.045
ma.L96        -0.0383      0.026     -1.458      0.145      -0.090       0.013
ma.L97         0.0052      0.062      0.083      0.934      -0.117       0.127
ma.L98        -0.0653      0.037     -1.755      0.079      -0.138       0.008
ma.L99        -0.0270      0.098     -0.276      0.783      -0.219       0.165
ma.L100       -0.0080      0.037     -0.213      0.831      -0.081       0.065
ma.L101       -0.0029      0.027     -0.109      0.913      -0.055       0.049
ma.L102       -0.0122      0.025     -0.482      0.630      -0.062       0.038
ma.L103       -0.0273      0.031     -0.883      0.377      -0.088       0.033
ma.L104        0.0319      0.044      0.729      0.466      -0.054       0.118
ma.L105       -0.0064      0.067     -0.096      0.924      -0.138       0.125
ma.L106        0.0020      0.034      0.058      0.953      -0.065       0.069
ma.L107        0.0242      0.023      1.037      0.300      -0.021       0.070
ma.L108        0.0081      0.047      0.171      0.864      -0.085       0.101
ma.L109       -0.0043      0.026     -0.169      0.866      -0.055       0.046
ma.L110       -0.0022      0.028     -0.078      0.938      -0.057       0.053
ma.L111        0.0197      0.025      0.786      0.432      -0.029       0.069
ma.L112        0.0097      0.041      0.237      0.812      -0.071       0.090
ma.L113        0.0258      0.026      1.010      0.312      -0.024       0.076
ma.L114        0.0373      0.040      0.932      0.351      -0.041       0.116
ma.L115       -0.0006      0.054     -0.011      0.991      -0.106       0.105
ma.L116       -0.0016      0.034     -0.048      0.962      -0.068       0.065
ma.L117        0.0218      0.026      0.850      0.395      -0.028       0.072
ma.L118        0.0304      0.045      0.673      0.501      -0.058       0.119
ma.L119        0.0540      0.046      1.165      0.244      -0.037       0.145
ma.L120       -0.0213      0.071     -0.301      0.763      -0.160       0.117
ma.L121        0.0703      0.065      1.079      0.281      -0.057       0.198
ma.L122       -0.0352      0.113     -0.312      0.755      -0.256       0.186
ma.L123       -0.0030      0.080     -0.037      0.970      -0.160       0.154
ma.L124       -0.0185      0.030     -0.612      0.541      -0.078       0.041
ma.L125        0.0100      0.028      0.353      0.724      -0.046       0.066
ma.L126       -0.0041      0.033     -0.122      0.903      -0.070       0.061
ma.L127       -0.0243      0.026     -0.938      0.348      -0.075       0.027
ma.L128        0.0505      0.046      1.100      0.271      -0.040       0.141
ma.L129        0.0503      0.093      0.539      0.590      -0.132       0.233
ma.L130        0.0073      0.070      0.105      0.917      -0.129       0.144
ma.L131        0.0148      0.037      0.399      0.690      -0.058       0.088
ma.L132        0.0309      0.030      1.040      0.298      -0.027       0.089
ma.L133        0.0341      0.053      0.642      0.521      -0.070       0.138
ma.L134        0.0302      0.046      0.656      0.512      -0.060       0.121
ma.L135        0.0697      0.036      1.916      0.055      -0.002       0.141
ma.L136        0.0181      0.097      0.188      0.851      -0.171       0.207
ma.L137        0.0315      0.032      0.986      0.324      -0.031       0.094
ma.L138        0.0110      0.038      0.289      0.773      -0.063       0.085
ma.L139        0.0576      0.025      2.273      0.023       0.008       0.107
ma.L140       -0.0170      0.083     -0.203      0.839      -0.181       0.147
ma.L141        0.0282      0.059      0.475      0.635      -0.088       0.145
ma.L142        0.0444      0.047      0.946      0.344      -0.048       0.136
ma.L143        0.0177      0.072      0.244      0.807      -0.124       0.160
ma.L144        0.0214      0.030      0.721      0.471      -0.037       0.080
ma.L145        0.0372      0.029      1.291      0.197      -0.019       0.094
ma.L146        0.0181      0.057      0.319      0.749      -0.093       0.129
ma.L147        0.0267      0.029      0.920      0.357      -0.030       0.084
ma.L148        0.0071      0.037      0.193      0.847      -0.065       0.079
ma.L149        0.0146      0.026      0.563      0.574      -0.036       0.065
ma.L150        0.0009      0.029      0.030      0.976      -0.056       0.058
ma.L151       -0.0287      0.025     -1.129      0.259      -0.078       0.021
ma.L152       -0.0144      0.053     -0.273      0.785      -0.118       0.089
ma.L153        0.0131      0.028      0.462      0.644      -0.043       0.069
ma.L154        0.0070      0.041      0.169      0.866      -0.074       0.088
ma.L155        0.0006      0.026      0.024      0.981      -0.050       0.051
ma.L156        0.0475      0.025      1.889      0.059      -0.002       0.097
ma.L157        0.0497      0.078      0.634      0.526      -0.104       0.203
ma.L158        0.0720      0.065      1.108      0.268      -0.055       0.199
ma.L159       -0.0444      0.087     -0.513      0.608      -0.214       0.125
ma.L160        0.0156      0.108      0.145      0.885      -0.196       0.227
ma.L161        0.0133      0.047      0.283      0.777      -0.079       0.106
ma.L162        0.0202      0.039      0.521      0.602      -0.056       0.096
ma.L163        0.0363      0.030      1.227      0.220      -0.022       0.094
ma.L164        0.0022      0.051      0.044      0.965      -0.097       0.101
ma.L165       -0.0116      0.028     -0.421      0.674      -0.065       0.042
ma.L166        0.0042      0.035      0.120      0.905      -0.064       0.072
ma.L167        0.0089      0.028      0.313      0.754      -0.047       0.064
ma.L168       -0.0174      0.028     -0.626      0.532      -0.072       0.037
ma.L169       -0.0182      0.041     -0.441      0.659      -0.099       0.063
ma.L170        0.0088      0.034      0.259      0.796      -0.058       0.076
ma.L171       -0.0111      0.035     -0.315      0.753      -0.080       0.058
ma.L172        0.0155      0.031      0.498      0.619      -0.046       0.077
ma.L173       -0.0062      0.032     -0.193      0.847      -0.069       0.056
ma.L174       -0.0274      0.027     -1.021      0.307      -0.080       0.025
ma.L175       -0.0126      0.051     -0.249      0.804      -0.112       0.086
ma.L176       -0.0259      0.027     -0.976      0.329      -0.078       0.026
ma.L177       -0.0368      0.039     -0.955      0.339      -0.112       0.039
ma.L178       -0.0140      0.053     -0.264      0.792      -0.118       0.090
ma.L179       -0.0409      0.028     -1.460      0.144      -0.096       0.014
ma.L180       -0.0153      0.056     -0.272      0.786      -0.126       0.095
ma.L181       -0.0804      0.028     -2.833      0.005      -0.136      -0.025
ma.L182        0.0131      0.113      0.116      0.907      -0.208       0.234
ma.L183        0.0097      0.062      0.158      0.875      -0.111       0.130
ma.L184        0.0173      0.037      0.464      0.643      -0.056       0.091
ma.L185       -0.0476      0.029     -1.639      0.101      -0.104       0.009
ma.L186       -0.0155      0.085     -0.183      0.855      -0.181       0.150
ma.L187       -0.0580      0.032     -1.805      0.071      -0.121       0.005
ma.L188       -0.0107      0.077     -0.140      0.889      -0.161       0.139
ma.L189       -0.0099      0.031     -0.326      0.745      -0.070       0.050
ma.L190        0.0397      0.025      1.619      0.105      -0.008       0.088
ma.L191       -0.0183      0.066     -0.277      0.782      -0.148       0.111
ma.L192       -0.0326      0.052     -0.631      0.528      -0.134       0.069
ma.L193       -0.0206      0.056     -0.366      0.714      -0.131       0.090
ma.L194       -0.0174      0.030     -0.576      0.564      -0.076       0.042
ma.L195        0.0004      0.028      0.016      0.987      -0.054       0.055
ma.L196       -0.0003      0.025     -0.013      0.990      -0.050       0.049
ma.L197       -0.0274      0.025     -1.112      0.266      -0.076       0.021
ma.L198       -0.0263      0.052     -0.505      0.613      -0.128       0.076
ma.L199       -0.0157      0.039     -0.403      0.687      -0.092       0.061
ma.L200        0.0005      0.026      0.021      0.983      -0.050       0.051
ma.L201       -0.0504      0.027     -1.881      0.060      -0.103       0.002
ma.L202       -0.0217      0.082     -0.266      0.790      -0.182       0.138
ma.L203        0.0103      0.033      0.317      0.751      -0.054       0.074
ma.L204        0.0323      0.043      0.753      0.451      -0.052       0.116
ma.L205       -0.0635      0.049     -1.289      0.198      -0.160       0.033
ma.L206        0.0555      0.119      0.468      0.640      -0.177       0.288
ma.L207       -0.0185      0.112     -0.165      0.869      -0.238       0.201
ma.L208       -0.0088      0.047     -0.187      0.852      -0.101       0.084
ma.L209       -0.0700      0.034     -2.035      0.042      -0.137      -0.003
ma.L210       -0.0218      0.102     -0.213      0.832      -0.223       0.179
sigma2         1.5128      0.037     40.650      0.000       1.440       1.586
===================================================================================
Ljung-Box (L1) (Q):                   0.01   Jarque-Bera (JB):             10205.17
Prob(Q):                              0.93   Prob(JB):                         0.00
Heteroskedasticity (H):               2.12   Skew:                            -0.96
Prob(H) (two-sided):                  0.00   Kurtosis:                        12.75
===================================================================================

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

É importante notar que, com um parâmetro de $q = 210$, o treinamento do modelo levou 11 horas. Nesse caso, seria mais eficiente diferenciar os dados antes de inseri-los no modelo, o que reduziria o valor de $q$ e, consequentemente, diminuiria o tempo de treinamento.

8. Diagnóstico dos Resíduos¶

A análise dos resíduos é uma etapa crítica na modelagem de séries temporais, pois nos ajuda a avaliar a qualidade do modelo.

Os resíduos ideais devem ser:

  • Não correlacionados.
    • resíduos correlacionados indicam que o modelo não capturou toda a informação dos dados.
  • Distribuição normal.
    • Resíduos com distribuição não normal indicam a presença de padrões que o modelo não conseguiu capturar.
  • Variância constante.
    • Se a variância dos resíduos não for constante, temos não linearidade nos dados que precisam ser corrigidas.
  • Média zero.
    • Se a média dos resíduos não for zero, o modelo precisa ser ajustado para capturar o viés (constante $c$).
  • Não sazonais.
    • Se houver padrões sazonais nos resíduos, o modelo precisa ser ajustado com diferenciação sazonal.

Teste de Ljung-Box: Verifica se os resíduos são aleatórios.

In [ ]:
# Resíduos do modelo ARIMA
resid_arima = resultado_arima.resid

# Plot dos resíduos
plt.figure(figsize=(14, 7))
plt.plot(resid_arima)
plt.title("Resíduos do Modelo ARIMA")
plt.xlabel("Data")
plt.ylabel("Resíduos")
plt.show()

# Teste de Ljung-Box
lb_test = acorr_ljungbox(resid_arima, lags=[10], return_df=True)
lb_test
No description has been provided for this image
Out[ ]:
lb_stat lb_pvalue
10 1.094523 0.99974

Nossos resíduos não estão correlacionados, seguem uma distribuição normal, apresentam variação constante e possuem média zero. Portanto, podemos considerá-los normais.

9. Previsão com o Modelo ARIMA¶

In [ ]:
# Criando 30 dias de previsão
forecast_arima = resultado_arima.forecast(steps=30)
last_date = df.index[-1]  # Última data da série original
forecast_index = pd.date_range(start=last_date, periods=31, freq='B')[1:]  # Frequência 'B' para dias úteis
In [ ]:
print(forecast_index)
DatetimeIndex(['2024-12-31', '2025-01-01', '2025-01-02', '2025-01-03',
               '2025-01-06', '2025-01-07', '2025-01-08', '2025-01-09',
               '2025-01-10', '2025-01-13', '2025-01-14', '2025-01-15',
               '2025-01-16', '2025-01-17', '2025-01-20', '2025-01-21',
               '2025-01-22', '2025-01-23', '2025-01-24', '2025-01-27',
               '2025-01-28', '2025-01-29', '2025-01-30', '2025-01-31',
               '2025-02-03', '2025-02-04', '2025-02-05', '2025-02-06',
               '2025-02-07', '2025-02-10'],
              dtype='datetime64[ns]', freq='B')
In [93]:
# Plot das previsões
plt.figure(figsize=(14, 7))
plt.plot(df["Último"].iloc[-350:], label="Dados Originais")
plt.plot(forecast_index, forecast_arima, color="red", label="Previsão ARIMA")
plt.title("Previsão com ARIMA")
plt.xlabel("Data")
plt.ylabel("Valor de Fechamento")
plt.legend()
plt.show()
No description has been provided for this image

10. Avaliação dos Modelos¶

Para realizar os testes, importei os dados dos próximos 30 meses do site Investing, garantindo que não houvesse viés nos resultados. Foi necessário manipular esses dados para que tivessem o mesmo formato da previsão, permitindo a execução correta dos testes de acuracidade.

In [ ]:
# Importanto os dados de teste
df_test_data = pd.read_csv('data_teste.csv')
In [ ]:
# Invertendo a ordem dos dados
df_test_data = df_test_data.iloc[::-1]
In [121]:
df_test_data.head()
Out[121]:
Data Último Abertura Máxima Mínima Vol. Var%
21 02.01.2025 120.125 120.283 120.782 119.120 9,37M -0,13%
20 03.01.2025 118.533 120.125 120.356 118.404 9,80M -1,33%
19 06.01.2025 120.022 118.534 120.322 118.534 9,69M 1,26%
18 07.01.2025 121.163 120.022 121.713 120.022 11,12B 0,95%
17 08.01.2025 119.625 121.160 121.160 119.351 10,23B -1,27%
In [ ]:
# Definindo conjunto de teste
test_data = df_test_data["Último"]
In [ ]:
# Identifiando index da última data do nosso modelo
train_data = forecast_arima[-22:]
In [124]:
print(train_data)
2487    119.552630
2488    120.467149
2489    120.535934
2490    120.419304
2491    121.061430
2492    121.445930
2493    121.714878
2494    121.883767
2495    122.271074
2496    122.461738
2497    122.370495
2498    122.750636
2499    123.034948
2500    124.001928
2501    124.408968
2502    124.739857
2503    124.735286
2504    124.709540
2505    125.057245
2506    124.995565
2507    125.450082
2508    125.874183
Name: predicted_mean, dtype: float64
In [ ]:
# Criando index com os dados de teste para que o mesmo possa ser concatenado com os dados de treino
test_data.index = range(2509, 2531)
print(test_data)
2509    120.125
2510    118.533
2511    120.022
2512    121.163
2513    119.625
2514    119.781
2515    118.856
2516    119.007
2517    119.299
2518    122.650
2519    121.234
2520    122.350
2521    122.855
2522    123.338
2523    122.972
2524    122.483
2525    122.447
2526    124.862
2527    124.056
2528    123.432
2529    126.913
2530    126.135
Name: Último, dtype: float64

Vamos utilizar o método de avaliação MAPE (Mean Absolute Percentage Error), pois é a métrica mais adequada para medir a acuracidade do modelo.

In [ ]:
# Implementando MAPE
mape_arima = mean_absolute_percentage_error(test_data, train_data) * 100
In [127]:
print(f"MAPE ARIMA: {mape_arima}%")
MAPE ARIMA: 1.0724259267876892%
In [ ]:
# Calculando a acuracidade
acuracidade = 100 - mape_arima
print(f'A acuracidade do modelo ARIMA é de {acuracidade:.2f}%')
A acuracidade do modelo ARIMA é de 98.93%

11. Conclusão¶

Com o modelo de previsão ARIMA, alcançamos uma acuracidade de 98,93%, indicando que o modelo teve um desempenho altamente preciso na previsão. No entanto, o principal desafio foi o tempo de treinamento, que se mostrou elevado. Apesar desse fator, o ARIMA demonstrou grande eficácia ao capturar e prever corretamente o comportamento do Ibovespa.

In [ ]:
plt.figure(figsize=(14, 7))

# Plot dos dados originais
plt.plot(df["Último"].iloc[-350:], label="Dados Originais", color='gray')

# Plot dos dados de teste
plt.plot(forecast_index[-22:], test_data, color='gray', linestyle="--", label="Dados de Teste")

# Plot da previsão ARIMA
plt.plot(forecast_index, forecast_arima, color="blue", linewidth=2.5, label="Previsão ARIMA")


plt.title("Previsão com ARIMA")
plt.xlabel("Data")
plt.ylabel("Valor de Fechamento")
plt.legend()
plt.show()
No description has been provided for this image