In [2]:
%matplotlib inline
import numpy as np
import yfinance as yf
from arch import arch_model
In [3]:
import matplotlib.pyplot as plt
plt.rc('figure',figsize=(16,6))
plt.rc('font',family='sans-serif')
plt.rc('font',size=14)

Step 1: Get Stock Price DATA¶

In [4]:
data=yf.download('AAPL',start='2020-01-01',end='2022-10-31')
[*********************100%***********************]  1 of 1 completed
In [5]:
data
Out[5]:
Open High Low Close Adj Close Volume
Date
2020-01-02 74.059998 75.150002 73.797501 75.087502 73.683563 135480400
2020-01-03 74.287498 75.144997 74.125000 74.357498 72.967216 146322800
2020-01-06 73.447502 74.989998 73.187500 74.949997 73.548630 118387200
2020-01-07 74.959999 75.224998 74.370003 74.597504 73.202736 108872000
2020-01-08 74.290001 76.110001 74.290001 75.797501 74.380280 132079200
... ... ... ... ... ... ...
2022-10-24 147.190002 150.229996 146.000000 149.449997 149.449997 75981900
2022-10-25 150.089996 152.490005 149.360001 152.339996 152.339996 74732300
2022-10-26 150.960007 151.990005 148.039993 149.350006 149.350006 88194300
2022-10-27 148.070007 149.050003 144.130005 144.800003 144.800003 109180200
2022-10-28 148.199997 157.500000 147.820007 155.740005 155.740005 164762400

713 rows × 6 columns

In [6]:
adjusted_closes=data['Adj Close']
returns=100*adjusted_closes.pct_change().dropna()
In [21]:
returns.plot()
Out[21]:
<AxesSubplot:xlabel='Date'>
In [22]:
(returns**2).plot()
Out[22]:
<AxesSubplot:xlabel='Date'>

Step 2: Fit A GARCH Model¶

In [37]:
model=arch_model(returns)

Calling arch_model like this does three things:

  1. Uses a constant mean

  2. Assumes a GARCH(1, 0, 1) volatility model

  3. Uses a normal distribution for the standardized errors

Then fit the model

In [38]:
res = model.fit()
Iteration:      1,   Func. Count:      6,   Neg. LLF: 2858.147147760594
Iteration:      2,   Func. Count:     16,   Neg. LLF: 19148.66777569176
Iteration:      3,   Func. Count:     23,   Neg. LLF: 1969.642834550345
Iteration:      4,   Func. Count:     30,   Neg. LLF: 1773.3051684941222
Iteration:      5,   Func. Count:     37,   Neg. LLF: 1531.87509421799
Iteration:      6,   Func. Count:     43,   Neg. LLF: 1529.717329092487
Iteration:      7,   Func. Count:     48,   Neg. LLF: 1529.716499633174
Iteration:      8,   Func. Count:     53,   Neg. LLF: 1529.7164421794673
Iteration:      9,   Func. Count:     58,   Neg. LLF: 1529.716435351741
Iteration:     10,   Func. Count:     63,   Neg. LLF: 1529.716433288234
Iteration:     11,   Func. Count:     67,   Neg. LLF: 1529.7164332882217
Optimization terminated successfully    (Exit mode 0)
            Current function value: 1529.716433288234
            Iterations: 11
            Function evaluations: 67
            Gradient evaluations: 11

Step 3: Forecast Volatility¶

In [39]:
print(res.summary())
                     Constant Mean - GARCH Model Results                      
==============================================================================
Dep. Variable:              Adj Close   R-squared:                       0.000
Mean Model:             Constant Mean   Adj. R-squared:                  0.000
Vol Model:                      GARCH   Log-Likelihood:               -1529.72
Distribution:                  Normal   AIC:                           3067.43
Method:            Maximum Likelihood   BIC:                           3085.71
                                        No. Observations:                  712
Date:                Tue, Nov 01 2022   Df Residuals:                      711
Time:                        15:46:32   Df Model:                            1
                                Mean Model                                
==========================================================================
                 coef    std err          t      P>|t|    95.0% Conf. Int.
--------------------------------------------------------------------------
mu             0.1972  7.607e-02      2.593  9.518e-03 [4.815e-02,  0.346]
                             Volatility Model                             
==========================================================================
                 coef    std err          t      P>|t|    95.0% Conf. Int.
--------------------------------------------------------------------------
omega          0.2462  9.903e-02      2.486  1.290e-02 [5.213e-02,  0.440]
alpha[1]       0.1302  3.234e-02      4.026  5.662e-05 [6.684e-02,  0.194]
beta[1]        0.8250  3.737e-02     22.076 5.395e-108   [  0.752,  0.898]
==========================================================================

Covariance estimator: robust
In [36]:
fig=res.plot('D')
In [52]:
# get the variance forecast
forecast = res.forecast(horizon=1, reindex=False)
variance_forecast = forecast.variance.iloc[-1][0]
In [53]:
variance_forecast
Out[53]:
11.517069881883414
In [56]:
# compute the annualized volatility forecast
volatility_forecast = np.sqrt(variance_forecast)
annualized_volatility_forecast = volatility_forecast * np.sqrt(252) / 100
In [57]:
annualized_volatility_forecast
Out[57]:
0.5387301374746563
In [58]:
returns['2022'].std()* np.sqrt(252) / 100
Out[58]:
0.35065659897325996

Reference: https://pyquantnews.com/find-market-mispricings-like-the-pros/