Theory

$Beta$$t$$EGARCH$ models introduced by Harvey and Chakravarty (2008) address some of the problems with $GARCH$ models by using an $EGARCH$ formulation to constrain volatility as positive without ad hoc parameter restrictions, and using the score of a t-distribution to drive parameter updates rather than squared residuals, leading to greater robustness to outliers:

$y_{t} = \mu + \exp\left(\lambda_{t\mid{t-1}}/2\right)\epsilon_{t}$

$\lambda_{t\mid{t-1}} = \alpha_{0} + \sum^{p}_{i=1}\alpha_{i}\lambda_{t-i} + \sum^{q}_{j=1}\beta_{j}\left(\frac{\left(\nu+1\right)y_{t-j}^{2}} {\nu\exp\left(\lambda_{t-j\mid{t-j-1}}\right) + y_{t-j}^{2}}-1\right)$

$\epsilon_{t} \sim t_{\nu}$

PyFlux

First let us load some financial time series data from Yahoo Finance:

import numpy as np
import pyflux as pf
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
%matplotlib inline

jpm = DataReader('JPM',  'yahoo', datetime(2006,1,1), datetime(2016,3,10))
returns.index = jpm.index.values[1:jpm.index.values.shape[0]]
returns.columns = ['JPM Returns']

plt.figure(figsize=(15,5));
plt.plot(returns.index,returns);
plt.ylabel('Returns');
plt.title('JPM Returns');


One way to visualize the underlying volatility of the series is to plot the absolute returns $\mid{y}\mid$:

plt.figure(figsize=(15,5))
plt.plot(returns.index, np.abs(returns))
plt.ylabel('Absolute Returns')
plt.title('JP Morgan Absolute Returns');


Let’s fit an EGARCH(1,1) model using a point mass estimate $z^{MLE}$:

model = pf.EGARCH(returns,p=1,q=1)
x = model.fit()
x.summary()

EGARCH(1,1)
======================================================= =================================================
Dependent Variable: JPM Returns                         Method: MLE
Start Date: 2006-01-05 00:00:00                         Log Likelihood: 6663.2492
End Date: 2016-03-10 00:00:00                           AIC: -13316.4985
Number of observations: 2562                            BIC: -13287.2557
=========================================================================================================
Latent Variable                          Estimate   Std Error  z        P>|z|    95% C.I.
======================================== ========== ========== ======== ======== ========================
Vol Constant                             -0.0575    0.0166     -3.4695  0.0005   (-0.0899 | -0.025)
p(1)                                     0.9933
q(1)                                     0.103
v                                        6.0794
Returns Constant                         0.0007     0.0247     0.0292   0.9767   (-0.0477 | 0.0492)
=========================================================================================================


By default, the EGARCH latent variables are constrained in the range $\left(0,1\right)$, so standard errors are not shown above. If you wish to obtain standard errors, then you can remove the constraints and initialize as you see fit. We can plot the GARCH parameters with plot_z:

model.plot_z([1,2],figsize=(15,5))


We can plot the fit with plot_fit:

model.plot_fit(figsize=(15,5))


And obtain predictions of future conditional volatility with predict:

model.predict(h=10)

JPM Returns
2016-03-07 0.016908
2016-03-10 0.016882
2016-03-11 0.016855
2016-03-12 0.016829
2016-03-13 0.016803
2016-03-14 0.016778
2016-03-17 0.016752
2016-03-18 0.016727
2016-03-19 0.016702
2016-03-20 0.016677

model.plot_predict(h=10,figsize=(15,5))


And view how well we predicted using dynamic in-sample rolling prediction with plot_predict_is:

model.plot_predict_is(h=30,figsize=(15,5))


Leverage Extension

Past evidence suggests a leverage effect in stock returns, see Black (1976), that observes that volatility increases more after bad news than good news. Following Harvey and Succarrat (2014), we can incorporate a leverage effect in the Beta-t-EGARCH model as follows:

$\lambda_{t\mid{t-1}} = \alpha_{0} + \sum^{p}_{i=1}\alpha_{i}\lambda_{t-i} + \sum^{q}_{j=1}\beta_{j}u_{t-j} + \kappa\left(\text{sgn}\left(-\epsilon_{t-1}\right)(u_{t-1}+1)\right)$

Where $\kappa$ is the leverage coefficient. It is easy to amend our model to include a leverage effect:

model.add_leverage()
x = model.fit()
x.summary()

EGARCH(1,1)
======================================================= =================================================
Dependent Variable: JPM Returns                         Method: MLE
Start Date: 2006-01-05 00:00:00                         Log Likelihood: 6688.2732
End Date: 2016-03-10 00:00:00                           AIC: -13364.5465
Number of observations: 2562                            BIC: -13329.4552
=========================================================================================================
Latent Variable                          Estimate   Std Error  z        P>|z|    95% C.I.
======================================== ========== ========== ======== ======== ========================
Vol Constant                             -0.0586    0.0219     -2.6753  0.0075   (-0.1015 | -0.0157)
p(1)                                     0.9934
q(1)                                     0.0781
Leverage Term                            0.0578     0.0012     49.8546  0.0      (0.0555 | 0.0601)
v                                        6.3724
Returns Constant                         0.0005     0.0        160.6585 0.0      (0.0005 | 0.0005)
=========================================================================================================

We have a small leverage effect for this time series:

model.plot_z([0,3],figsize=(15,5))

We can plot the fit again with plot_fit:

model.plot_fit(figsize=(15,5))


And plot predictions with plot_predict:

model.plot_predict(h=30,figsize=(15,5))