Theory

We can expand the Beta-t-EGARCH framework to include exogenous regressors in both the returns and conditional volatility equation:

$y_{t} = \mu + \sum^{m}_{k=1}\phi_{m}{X_{m,t}} + \exp\left(\lambda_{t\mid{t-1}}/2\right)\epsilon_{t}$

$\lambda_{t\mid{t-1}} = \alpha_{0} + \sum\alpha_{i}\lambda_{t-i} + \sum\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) + \sum\gamma_{m}{X_{m,t}}$

$\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

a = DataReader('JPM',  'yahoo', datetime(2000,1,1), datetime(2016,3,10))
a_returns.index = a.index.values[1:a.index.values.shape[0]]
a_returns.columns = ["JPM Returns"]

spy = DataReader('SPY',  'yahoo', datetime(2000,1,1), datetime(2016,3,10))
spy_returns.index = spy.index.values[1:spy.index.values.shape[0]]
spy_returns.columns = ['S&P500 Returns']

one_day = np.log(1+one_mon)/365

returns = pd.concat([one_day,a_returns,spy_returns],axis=1).dropna()
excess_m = returns["JPM Returns"].values - returns['DGS1MO'].values
excess_spy = returns["S&P500 Returns"].values - returns['DGS1MO'].values
final_returns = pd.DataFrame(np.transpose([excess_m,excess_spy, returns['DGS1MO'].values]))
final_returns.columns=["JPM","SP500","Rf"]
final_returns.index = returns.index

plt.figure(figsize=(15,5))
plt.title("Excess Returns")
x = plt.plot(final_returns);
plt.legend(iter(x), final_returns.columns);


Let’s fit an EGARCH-M model to JPM’s excess returns series, with a risk-free rate regressor:

modelx = pf.EGARCHMReg(p=1,q=1,data=final_returns,formula='JPM ~ Rf')
results = modelx.fit()
results.summary()

EGARCHMReg(1,1)
======================================================= =================================================
Dependent Variable: JPM                                 Method: MLE
Start Date: 2001-08-01 00:00:00                         Log Likelihood: 9621.2996
End Date: 2016-03-10 00:00:00                           AIC: -19226.5993
Number of observations: 3645                            BIC: -19176.9904
=========================================================================================================
Latent Variable                          Estimate   Std Error  z        P>|z|    95% C.I.
======================================== ========== ========== ======== ======== ========================
p(1)                                     0.9936
q(1)                                     0.0935
v                                        6.5414
GARCH-M                                  -0.0199    0.023      -0.8657  0.3866   (-0.0649 | 0.0251)
Vol Beta 1                               -0.0545    0.0007     -77.9261 0.0      (-0.0559 | -0.0531)
Vol Beta Rf                              -0.0346    1.1161     -0.031   0.9753   (-2.2222 | 2.153)
Returns Beta 1                           0.0011     0.0011     1.0218   0.3069   (-0.001 | 0.0032)
Returns Beta Rf                          -1.1324    0.0941     -12.0398 0.0      (-1.3167 | -0.948)
=========================================================================================================


We can plot some of the latent variables with plot_z:

modelx.plot_z([5,7],figsize=(15,5))


For this stock, the risk-free rate has a negative effect on excess returns. For the effects of returns on volatility, we are far more uncertain.

We can plot the fit with plot_fit:

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