Updating GARCH Volatility Estimates with New Observations
Summary
The answer explains how to continue a fitted GARCH(1,1) volatility series when historical data is unavailable. Keep the previously estimated parameters fixed, then initialize the recursion with the most recent residual and conditional volatility estimate. For each new close, calculate the return and residual, and use the prior residual and volatility to update the next conditional volatility. The example reports a sequence of daily estimates for newly observed S&P 500 closes.
With fixed parameters, forecasts gradually move toward unconditional volatility, and convergence is slower when the persistence terms sum close to one. Fixed parameters can also miss changes in the return process, including volatility shifts or structural breaks. The response therefore points to rolling or expanding windows that re-estimate parameters as observations arrive, and mentions a Python package for GARCH forecasting. It provides a practical continuation method but no forecast evaluation; its stated estimates depend on the supplied parameterization and initialization values.
Key ideas
- A fitted GARCH recursion can be continued using fixed parameters and the latest residual and volatility estimate.
- New close prices must first be converted into returns and residuals before updating conditional volatility.
- With fixed parameters, volatility estimates tend toward unconditional volatility over time.
- Rolling or expanding estimation can adapt parameters to new observations but requires renewed fitting.
Tags
Full text
# Continuation of GARCH(1,1) without data
# Continuation of GARCH(1,1) without data
Please be easy on me since quant finance is not my strength.
I have the following Python code that models volatility under GARCH(1,1) for the S&P500:
```
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import scipy.optimize as spop
ticker = '^GSPC'
start = '2015-12-31'
end = '2021-06-25'
prices = yf.download(ticker, start, end)['Close']
returns = np.array(prices)[1:]/np.array(prices)[:-1] - 1
mean = np.average(returns)
var = np.std(returns)**2
def garch_mle(params):
mu = params[0]
omega = params[1]
alpha = params[2]
beta = params[3]
long_run = (omega/(1 - alpha - beta))**(1/2)
resid = returns - mu
realised = abs(resid)
conditional = np.zeros(len(returns))
conditional[0] = long_run
for t in range(1,len(returns)):
conditional[t] = (omega + alpha*resid[t-1]**2 + beta*conditional[t-1]**2)**(1/2)
likelihood = 1/((2*np.pi)**(1/2)*conditional)*np.exp(-realised**2/(2*conditional**2))
log_likelihood = np.sum(np.log(likelihood))
return -log_likelihood
res = spop.minimize(garch_mle, [mean, var, 0, 0], method='Nelder-Mead')
params = res.x
mu = res.x[0]
omega = res.x[1]
alpha = res.x[2]
beta = res.x[3]
log_likelihood = -float(res.fun)
long_run = (omega/(1 - alpha - beta))**(1/2)
resid = returns - mu
realised = abs(resid)
conditional = np.zeros(len(returns))
conditional[0] = long_run
for t in range(1,len(returns)):
conditional[t] = (omega + alpha*resid[t-1]**2 + beta*conditional[t-1]**2)**(1/2)
```
And it produces these values, after optimization:
```
mu 0.000961
omega 4e-06
alpha 0.2604
beta 0.721
long-run volatility 0.0149
log-likelihood 4691.0025
```
How do I continue modeling volatility, under GARCH(1,1), using the parameters from above (aka I wouldn't have access to the initial raw data anymore) and for the following new daily closes on the S&P500?
```
new_data = {
'2021-06-25': 4280.700195,
'2021-06-28': 4290.609863,
'2021-06-29': 4291.799805,
'2021-06-30': 4297.500000,
'2021-07-01': 4319.939941,
'2021-07-02': 4352.339844
}
```
Thanks!
## Answer by Pleb (score 1, accepted)
https://quant.stackexchange.com/a/80104
You can remove the estimation section from your code and extend the end of your data to include out-of-sample data. As written below, I have used the previous residual and volatility estimate for initialization. This implies that your code reduces to:
```
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import scipy.optimize as spop
ticker = '^GSPC'
start = '2021-06-25'
end = '2021-07-03' #Changing the end date will give you the new data that you desire.
prices = yf.download(ticker, start, end)['Close']
returns = np.array(prices)[1:]/np.array(prices)[:-1] - 1
#The estimated parameters from previous:
mu = 0.000961
omega = 4e-06
alpha = 0.2604
beta = 0.721
long_run = 0.0149
#The last part of your code can be re-run with the added prices
old_conditional = 0.008016 #estimates from 2021-06-24
old_resid = 0.00485025 #estimates from 2021-06-24
#initiate resid and conditional
resid = np.zeros(len(returns) + 1)
resid[0] = old_resid
conditional = np.zeros(len(returns) + 1)
conditional[0] = old_conditional
resid[1:] = returns - mu
realised = abs(resid)
for t in range(1,len(returns) + 1):
conditional[t] = (omega + alpha*resid[t-1]**2 + beta*conditional[t-1]**2)**(1/2)
#Created a dataframe for results.
vol = pd.DataFrame(columns=["sigma"], index=prices.index)
vol["sigma"] = conditional
```
With corresponding results:
```
>>> vol.tail(7)
Date sigma
2021-06-24 0.008016
2021-06-25 0.007514
2021-06-28 0.006795
2021-06-29 0.006145
2021-06-30 0.005599
2021-07-01 0.005161
2021-07-02 0.005285
```
Please consider this:
- Under no re-estimation, the out-of-sample GARCH estimates eventually converges to the unconditional volatility (aka. around 0.0149). When $\alpha + \beta \approx 1$ the process converges at a slower rate. Hence, a re-estimation is needed for better volatility forecasts.
- Using fixed GARCH parameters out-of-sample, makes the estimates insensitive to new underlying characteristics in the price process. This implies that during periods of increased volatility or structural breaks, the fixed estimates will perform poorly in forecasting the altered volatility process. To address this issue, econometricians opt either for a rolling- or expanding-window forecast, allowing the GARCH parameters to be re-estimated with each new consecutive price point. The "estimation" part, along with the concluding section of your code, can be rewritten to accommodate either of these forecasting methodologies.
- Other than that, there is also a package in Python called arch, that contains various GARCH models. I have written a small and convenient answer here on how to produce forecasts using the package.
If this is truly for a sensitive thing, I would recommend you to consult with your peers on how to improve the volatility forecasts. For now, the above code do as you intend.Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.