Abstract
This is an undergrad student level instruction for class use.This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
Sugestão de citação: FIGUEIREDO, Adriano Marcos Rodrigues. Séries Temporais: previsão de ações com ETS com Python. Campo Grande-MS,Brasil: RStudio/Rpubs, 2025. Disponível em http://rpubs.com/amrofi/stocks_ETS_py.
Primeiro, instale os pacotes pelo terminal:
pip install yfinance pandas matplotlib statsmodels scikit-learn.
Depois, segue o code:
# Importing necessary libraries
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_percentage_error as mape
from sklearn.metrics import mean_absolute_error as mae
# Configurations
plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [12, 6]
# Downloading PETR4 and VALE3 data
petro_data = yf.download("PETR4.SA", start="2000-01-01", end="2019-09-01", interval="1mo", progress=False)
YF.download() has changed argument auto_adjust default to True
vale_data = yf.download("VALE3.SA", start="2000-01-01", end="2019-09-01", interval="1mo", progress=False)
# Check if 'Adj Close' column exists, otherwise use 'Close'
if 'Adj Close' in petro_data.columns:
petro = petro_data['Adj Close']
else:
print("'Adj Close' not found for PETR4.SA. Using 'Close' instead.")
petro = petro_data['Close']
'Adj Close' not found for PETR4.SA. Using 'Close' instead.
if 'Adj Close' in vale_data.columns:
vale = vale_data['Adj Close']
else:
print("'Adj Close' not found for VALE3.SA. Using 'Close' instead.")
vale = vale_data['Close']
'Adj Close' not found for VALE3.SA. Using 'Close' instead.
# Plotting the adjusted closing prices (or regular closing prices if 'Adj Close' is missing)
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro, color='darkblue')
plt.title('PETR4.SA - Adjusted Closing Price' if 'Adj Close' in petro_data.columns else 'PETR4.SA - Closing Price')
plt.xlabel('Month/Year')
plt.ylabel('R$')
plt.subplot(2, 1, 2)
plt.plot(vale, color='darkblue')
plt.title('VALE3.SA - Adjusted Closing Price' if 'Adj Close' in vale_data.columns else 'VALE3.SA - Closing Price')
plt.xlabel('Month/Year')
plt.ylabel('R$')
plt.tight_layout()
plt.show()
# Calculating monthly log returns
petro_returns = petro.pct_change().dropna()
vale_returns = vale.pct_change().dropna()
# Plotting the returns
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, color='darkblue')
plt.title('PETR4.SA Monthly Returns')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.subplot(2, 1, 2)
plt.plot(vale_returns, color='darkblue')
plt.title('VALE3.SA Monthly Returns')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.tight_layout()
plt.show()
# Splitting data into training and testing sets
train_size = int(len(petro_returns) * 0.8)
petro_train, petro_test = petro_returns[:train_size], petro_returns[train_size:]
vale_train, vale_test = vale_returns[:train_size], vale_returns[train_size:]
# Holt-Winters Forecasting
# PETR4
petro_hw = ExponentialSmoothing(petro_train, seasonal='add', seasonal_periods=12).fit()
petro_hw_forecast = petro_hw.forecast(len(petro_test))
# VALE3
vale_hw = ExponentialSmoothing(vale_train, seasonal='add', seasonal_periods=12).fit()
vale_hw_forecast = vale_hw.forecast(len(vale_test))
# Plotting Holt-Winters forecasts
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, label='Actual Data')
plt.plot(petro_hw_forecast, label='Holt-Winters Forecast', color='red')
plt.title('PETR4.SA - Holt-Winters Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(vale_returns, label='Actual Data')
plt.plot(vale_hw_forecast, label='Holt-Winters Forecast', color='red')
plt.title('VALE3.SA - Holt-Winters Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.tight_layout()
plt.show()
# ETS (Error, Trend, Seasonal) Forecasting
# PETR4
petro_ets = ExponentialSmoothing(petro_train, trend='add', seasonal='add', seasonal_periods=12).fit()
petro_ets_forecast = petro_ets.forecast(len(petro_test))
# VALE3
vale_ets = ExponentialSmoothing(vale_train, trend='add', seasonal='add', seasonal_periods=12).fit()
vale_ets_forecast = vale_ets.forecast(len(vale_test))
# Plotting ETS forecasts
plt.figure(figsize=(14, 8))
plt.subplot(2, 1, 1)
plt.plot(petro_returns, label='Actual Data')
plt.plot(petro_ets_forecast, label='ETS Forecast', color='green')
plt.title('PETR4.SA - ETS Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(vale_returns, label='Actual Data')
plt.plot(vale_ets_forecast, label='ETS Forecast', color='green')
plt.title('VALE3.SA - ETS Forecast')
plt.xlabel('Month/Year')
plt.ylabel('Return')
plt.legend()
plt.tight_layout()
plt.show()
# Evaluating accuracy
# Holt-Winters
petro_hw_smape = mape(petro_test, petro_hw_forecast) * 100
vale_hw_smape = mape(vale_test, vale_hw_forecast) * 100
petro_hw_mape = mae(petro_test, petro_hw_forecast)
vale_hw_mape = mae(vale_test, vale_hw_forecast)
# ETS
petro_ets_smape = mape(petro_test, petro_ets_forecast) * 100
vale_ets_smape = mape(vale_test, vale_ets_forecast) * 100
petro_ets_mape = mae(petro_test, petro_ets_forecast)
vale_ets_mape = mae(vale_test, vale_ets_forecast)
# Printing results
print("Holt-Winters Accuracy:")
Holt-Winters Accuracy:
print(f"PETR4 SMAPE: {petro_hw_smape:.2f}%")
PETR4 SMAPE: 115.80%
print(f"VALE3 SMAPE: {vale_hw_smape:.2f}%")
VALE3 SMAPE: 108.74%
print(f"PETR4 MAPE: {petro_hw_mape:.4f}")
PETR4 MAPE: 0.1140
print(f"VALE3 MAPE: {vale_hw_mape:.4f}")
VALE3 MAPE: 0.1078
print("\nETS Accuracy:")
ETS Accuracy:
print(f"PETR4 SMAPE: {petro_ets_smape:.2f}%")
PETR4 SMAPE: 130.68%
print(f"VALE3 SMAPE: {vale_ets_smape:.2f}%")
VALE3 SMAPE: 120.51%
print(f"PETR4 MAPE: {petro_ets_mape:.4f}")
PETR4 MAPE: 0.1173
print(f"VALE3 MAPE: {vale_ets_mape:.4f}")
VALE3 MAPE: 0.1116