波動率

這是使用 GARCH 預測股價波動的正確方法嗎

  • January 26, 2019

我試圖預測未來某個時間(比如 90 天)的股票波動。GARCH 似乎是一個傳統上使用的模型。

我在下面使用 Python 的arch庫實現了這一點。我所做的一切都在評論中進行了解釋,執行程式碼唯一需要更改的是提供您自己的每日價格,而不是我從自己的 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()

對於 Apple,這會產生以下情節:

在此處輸入圖像描述

顯然,擬合值始終遠低於實際值,這也導致預測被嚴重低估(這是一個糟糕的例子,因為蘋果的波動性在這個測試期間異常高,但我嘗試的所有公司,模型總是低估擬合值)。

我做的一切是否正確,GARCH 模型不是很強大,或者波動率建模非常困難?還是我犯了一些錯誤?

我通過以下方式執行滾動預測解決了這個問題。我不確定 (1) 此滾動預測是否正確,以及 (2) 如何在未來 30 天執行滾動預測。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from rpy2.robjects.packages import importr
import rpy2.robjects as robjects
from rpy2.robjects import numpy2ri

ticker = 'AAPL'
forecast_horizon = 30

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)         # Convert monthly standard devation to annualized volatility
df = df.dropna().reset_index(drop=True)

# Initialize R GARCH model
rugarch = importr('rugarch')
garch_spec = rugarch.ugarchspec(
   mean_model=robjects.r('list(armaOrder = c(0,0))'),
   variance_model=robjects.r('list(garchOrder=c(1,1))'),
   distribution_model='std'
)

# Used to convert training set to R list for model input
numpy2ri.activate()

# Train R GARCH model on returns as %
garch_fitted = rugarch.ugarchfit(
   spec=garch_spec,
   data=df['daily_returns'].values * 100,
   out_sample=forecast_horizon
)

numpy2ri.deactivate()

# Model's fitted standard deviation values
# Revert previous multiplication by 100
# Convert to annualized volatility
fitted = 0.01 * np.sqrt(252) * np.array(garch_fitted.slots['fit'].rx2('sigma')).flatten()

# Forecast using R GACRH model
garch_forecast = rugarch.ugarchforecast(
   garch_fitted,
   n_ahead=1,
   n_roll=forecast_horizon - 1
)

# Model's forecasted standard deviation values
# Revert previous multiplication by 100
# Convert to annualized volatility
forecast = 0.01 * np.sqrt(252) * np.array(garch_forecast.slots['forecast'].rx2('sigmaFor')).flatten()

volatility = pd.DataFrame({
   'actual': df['annual_vol'].values,
   'model': np.append(fitted, forecast),
})

plt.plot(volatility['actual'][:-forecast_horizon], label='Train')
plt.plot(volatility['actual'][-forecast_horizon - 1:], label='Test')
plt.plot(volatility['model'][:-forecast_horizon], label='Fitted')
plt.plot(volatility['model'][-forecast_horizon - 1:], label='Forecasted')
plt.legend()
plt.show()

這產生了這個情節:

在此處輸入圖像描述

引用自:https://quant.stackexchange.com/questions/43545