Estoy intentando hacer una previsión de la volatilidad de una acción en un tiempo futuro (digamos 90 días). Parece que GARCH es un modelo tradicionalmente utilizado para esto.
He implementado esto a continuación utilizando el sistema de Python arch
biblioteca. Todo lo que hago está explicado en los comentarios, lo único que hay que cambiar para ejecutar el código es proporcionar tus propios precios diarios, en lugar de que yo los recupere de mi propia API.
import utils
import numpy as np
import pandas as pd
import arch
import matplotlib.pyplot as plt
ticker = 'AAPL' # Ticker to retrieve data for
forecast_horizon = 90 # Number of days to forecast
# Retrive prices from IEX API
prices = utils.dw.get(filename=ticker, source='iex', iex_range='5y')
df = prices[['date', 'close']]
df['daily_returns'] = np.log(df['close']).diff() # Daily log returns
df['monthly_std'] = df['daily_returns'].rolling(21).std() # Standard deviation across trading month
df['annual_vol'] = df['monthly_std'] * np.sqrt(252) # Annualize monthly standard devation
df = df.dropna().reset_index(drop=True)
# Convert decimal returns to %
returns = df['daily_returns'] * 100
# Fit GARCH model
am = arch.arch_model(returns[:-forecast_horizon])
res = am.fit(disp='off')
# Calculate fitted variance values from model parameters
# Convert variance to standard deviation (volatility)
# Revert previous multiplication by 100
fitted = 0.1 * np.sqrt(
res.params['omega'] +
res.params['alpha[1]'] *
res.resid**2 +
res.conditional_volatility**2 *
res.params['beta[1]']
)
# Make forecast
# Convert variance to standard deviation (volatility)
# Revert previous multiplication by 100
forecast = 0.1 * np.sqrt(res.forecast(horizon=forecast_horizon).variance.values[-1])
# Store actual, fitted, and forecasted results
vol = pd.DataFrame({
'actual': df['annual_vol'],
'model': np.append(fitted, forecast)
})
# Plot Actual vs Fitted/Forecasted
plt.plot(vol['actual'][:-forecast_horizon], label='Train')
plt.plot(vol['actual'][-forecast_horizon - 1:], label='Test')
plt.plot(vol['model'][:-forecast_horizon], label='Fitted')
plt.plot(vol['model'][-forecast_horizon - 1:], label='Forecast')
plt.legend()
plt.show()
En el caso de Apple, esto produce el siguiente gráfico:
Claramente, los valores ajustados son constantemente mucho más bajos que los valores reales, y esto hace que la previsión sea también una enorme subestimación (Este es un mal ejemplo dado que la volatilidad de Apple fue inusualmente alta en este período de prueba, pero con todas las empresas que pruebo, el modelo siempre está subestimando los valores ajustados).
¿Estoy haciendo todo correctamente, y el modelo GARCH no es muy potente, o modelar la volatilidad es muy difícil? ¿O hay algún error que estoy cometiendo?