Skip to content
All library documents

Adjusting Rescaled GARCH Volatility Forecasts to Original Units

Article Quant Q&A · Author: Thales Marques

Summary

The post investigates why annualized GARCH(1,1) volatility forecasts from Python's ARCH package matched observed volatility for a more volatile futures return series but appeared far too high for a less volatile series. The author initially assumed that automatic rescaling always multiplied returns by one hundred and therefore divided forecast variance by one hundred squared.

The resolution is that automatic rescaling is applied when needed for model fitting, and the actual scale factor may differ from that assumption. The fitted result exposes the scale used; forecast variance must be divided by the square of that factor before taking its square root and annualizing. The example also specifies a zero-mean model for the less volatile series. The account focuses on undoing the software's unit adjustment; it does not establish that GARCH forecasts will generally match realized volatility, and it offers no broader model diagnostics or forecast evaluation.

Key ideas

  • Automatic rescaling in the ARCH package may use a factor other than one hundred.
  • Undo a variance rescaling by dividing forecast variance by the square of the scale actually applied.
  • Taking the square root and annualizing should follow the variance unit adjustment.
  • Correcting units resolves the example's discrepancy but does not validate GARCH forecast quality in general.

Tags

Full text
# Forecasting Volatility using GARCH in Python - Arch Package


# Forecasting Volatility using GARCH in Python - Arch Package












Disclaimer: Posted this on stackoverflow, but maybe here should be the right place to ask something about GARCH

I'm testing ARCH package to forecast the Variance (Standard Deviation) of two series using GARCH(1,1).

This is the first part of my code

```
import pandas as pd
import numpy as np
from arch import arch_model

returns = pd.read_csv('ret_full.csv', index_col=0)
returns.index = pd.to_datetime(returns.index)
```

## Ibovespa Returns

The first series is the 1st Future Contract of Ibovespa Index, has an observed annualized volatility really close to the Garch Forecast.

The first problem that I've found is that you need to rescale your sample by 100. To do this, you can multiply your return series by 100 or setting the parameter `rescale=True` in the `arch_model` function.

Why is necessary to do this?

```
# Ibov
ret_ibov = returns['IBOV_1st']
model_ibov = arch_model(ret_ibov, vol='Garch', p=1, o=0, q=1, dist='Normal', rescale=True)
res_ibov = model_ibov.fit()
```

After fitting the model I forecast the Variance (just 5 steps to illustrate the problem), get the Standard Deviation and annualize it. Obs: Since I had to rescale my return series, I divide my forecast by 10000 (100**2, because of rescale)

```
# Forecast
forecast_ibov = res_ibov.forecast(horizon=5)

# Getting Annualized Standard Deviation
# Garch Vol
vol_ibov_for = (forecast_ibov.variance.iloc[-1]/10000)**0.5 * np.sqrt(252) * 100
# Observed Vol
vol_ibov = ret_ibov.std() * np.sqrt(252) * 100
```

And that's the forecast output

```
vol_ibov_for
h.1    24.563208
h.2    24.543245
h.3    24.523969
h.4    24.505357
h.5    24.487385
```

Which is really close to Observed Vol `23.76`

This is a results that I was expecting.

## IRFM Returns

When I do exactly the same process a less volatile series, I got a really weird result.

```
# IRFM
ret_irfm = returns['IRFM1M']
model_irfm = arch_model(ret_irfm, vol='Garch', p=1, o=0, q=1, dist='Normal', rescale=True)
res_irfm = model_irfm.fit()

# Forecast
forecasts_irfm = res_irfm.forecast(horizon=5)

# Getting Annualized Standard Deviation
# Garch Vol
vol_irfm_for = (forecasts_irfm.variance.iloc[-1]/10000)**0.5 * np.sqrt(252) * 100
# Observed Vol
vol_irfm = ret_irfm.std() * np.sqrt(252) * 100
```

Forecast output:

```
vol_irfm_for
h.1    47.879679
h.2    49.322351
h.3    50.519282
h.4    51.517356
h.5    52.352894
```

And this is significantly different from the Observed Volatility `5.39`

Why is this happening? Maybe because of the rescaling? Do I have to do another adjust before the forecast?

Thanks

## Answer by Thales Marques (score 2)

https://quant.stackexchange.com/a/50819

Found the answer.

The `rescale=True` is used when the model fails to converge to a result. So rescale could be a solution for the problem. If the model doesn't need rescale, even if the parameter is `True`, it will not do anything.

Point of Attempion: If the `rescale=True` and, in fact, rescaled the series. It's necessary to adjust the outputs. In my question I was confused about how high my volatility was. That's because I was assuming that my rescale values was 100, which not necessarily is true.

The correct thing to do is to set the parameter as True and get the rescale value after that.

To do this, just need to insert the following code:

```
# IRFM
ret_irfm = returns['IRFM1M']
model_irfm = arch_model(ret_irfm, vol='Garch', p=1, o=0, q=1, dist='Normal', rescale=True, mean='Zero')
res_irfm = model_irfm.fit()
scale = res_irfm.scale  # New part of the code

# Forecast
forecasts_irfm = res_irfm.forecast(horizon=5)

# Getting Annualized Standard Deviation
# Garch Vol
# New part of the code: Divide variance by scale^2
vol_irfm_for = (forecasts_irfm.variance.iloc[-1] / np.power(scale, 2))**0.5 * np.sqrt(252) * 100 
# Observed Vol
vol_irfm = ret_irfm.std() * np.sqrt(252) * 100
```

Hope this help another users with the same problem. It's a really simple thing.

Thanks.

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.