Fitting a volatility model on stocks
Today a quant posed me a question:
If I had a sorted timeseries, how would I know if it was ordered correctly? What if it's in reverse?
After having an interesting conversation about how I would solve the issue, he infomed me that a straightforward way was to fit a GARCH model, and that the model fit would be much higher if the timeseries was sorted in the right direction.
So I thought to myself, I'm going to try it out and see if this is true as I personally was not convinced. This code uses the ARCH package written by Prof. Kevin Sheppard from Oxford.
A brief introduction to GARCH follows. A simple regression model can be defined as followws
$$ r_{t} = m_{t} + \sqrt{h_{t}} \epsilon_{t} $$We can see that the variance of the residuals are being explicitly modeled using a GARCH model as below:
$$ h_{t+1} = \omega + \alpha(r_{t} - m_{t})^2 + \beta h_{t} = \omega + \alpha h_{t} \epsilon^{2}_{t} + \beta h_{t} $$THe intuition behind the GARCH model is fairly simple. The model itself is asserts that the best predictor of variance/volatility in the next period is the weighted average of the following:
- Long-run average variance. ($\omega$)
- Variance predicted for this period. ($h_{t}$)
- New information in this period that is captured by the most recent squared residual. ($h_{t} \epsilon^{2}_{t}$)
Thus, the weights that need to be estimated are $\omega$, $\alpha$, and $\beta$, and the inputs are the previous forecast ($h$) and the residual ($\epsilon$). The long-run average variance is given by $\sqrt{\omega/(1-\alpha-\beta)}$.
Importing all necessary modules¶
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn
import os
from arch import arch_model
from statsmodels.tsa.stattools import acf, pacf
Reading the Google timeseries¶
dir = os.getcwd() + '/inputs/'
filename = 'stk_GOOG.csv'
readFile = dir + filename
print(readFile)
stk_df = pd.read_csv(readFile,index_col='Date',parse_dates=True)
stk_df.info()
stk_df.head()
Converting prices to returns¶
stk_price = stk_df['Adj Close']
stk_ret = stk_price.pct_change().dropna()
stk_ret.plot(title='Google Returns')
Calculating ACF¶
stk_ret_acf_1 = acf(stk_ret)[1:32]
stk_ret_acf_2 = [stk_ret.autocorr(i) for i in range(1,32)]
test_df = pd.DataFrame([stk_ret_acf_1, stk_ret_acf_2]).T
test_df.columns = ['Pandas Autocorr', 'Statsmodels Autocorr']
test_df.index += 1
test_df.plot(kind='bar')
Fitting a GARCH model¶
am = arch_model(stk_ret)
res = am.fit(update_freq=5)
print(res.summary())
Reversing the time-series¶
stk_ret_reverse = stk_ret.iloc[::-1]
plt.plot(stk_ret_reverse.values)
plt.title('Google Returns (Reversed)')
stk_ret_acf_1_rev = acf(stk_ret_reverse)[1:32]
stk_ret_acf_2_rev = [stk_ret_reverse.autocorr(i) for i in range(1,32)]
test_df = pd.DataFrame([stk_ret_acf_1_rev, stk_ret_acf_2_rev]).T
test_df.columns = ['Pandas Autocorr', 'Statsmodels Autocorr']
test_df.index += 1
test_df.plot(kind='bar')
am_rev = arch_model(stk_ret_reverse)
res_rev = am_rev.fit(update_freq=5)
print(res_rev.summary())
So both the forward and reverse timeseries have GARCH models that can be estimated. Thus, I'm not sure what that quant meant. Technically, whether a time series is reversed or not, its just a set of returns thus its not certain how the fit of a GARCH model would lead to one knowing whether it is reversed or not. All I can see is that it took more iterations for a reverse timeseries to converge. The other part is that perhaps the p-values for the reverse case are very small indicating that all the variables of the GARCH model are extremely significant?
Fitting a GARCH-GJR model (Forward)¶
am = arch_model(stk_ret, p=1, o=1, q=1)
res = am.fit(update_freq=5, disp='off')
print(res.summary())
Fitting a GARCH-GJR model (Reverse)¶
am = arch_model(stk_ret_reverse, p=1, o=1, q=1)
res = am.fit(update_freq=5, disp='off')
print(res.summary())
Comments
Comments powered by Disqus