MS-GARCH — regimes vs long memory (from scratch)¶

Markov-switching GARCH (Haas, Mittnik & Paolella 2004), Normal and Student-t¶

The companion figarch_python notebook argued the near-unit-root persistence ($\alpha+\beta\approx0.99$) is genuine long memory ($d=0.52$). Here we test the rival explanation: that the persistence is spurious — an artifact of unmodeled regime shifts (Lamoureux–Lastrapes 1990; Diebold–Inoue 2001 proved switching and long memory are observationally confounded, so this really is a contest).

The model. A latent 2-state Markov chain $S_t\in\{\text{calm},\text{turbulent}\}$ switches the volatility dynamics. Each regime carries its own GARCH, and each return is drawn from the active regime: $$h_t^{(k)}=\omega_k+\alpha_k r_{t-1}^2+\beta_k h_{t-1}^{(k)},\qquad r_t\mid S_t=k\ \sim\ N\!\big(0,h_t^{(k)}\big)\ \text{or}\ t_\nu,\qquad P(S_t{=}j\mid S_{t-1}{=}i)=P_{ij}.$$

Why each regime gets its own GARCH. A naïve MS-GARCH would make a single $h_t$ depend on $S_t$ and $h_{t-1}$ — which depends on $S_{t-1}$, back to $S_1$ — so $h_t$ depends on the entire regime path ($2^t$ of them) and the likelihood is intractable. The Haas-Mittnik-Paolella device runs $K$ parallel GARCH processes, each depending only on its own past, which removes the path-dependence and makes the likelihood exact.

Data. The MS-GARCH (and its FIGARCH rival) are estimated on daily S&P 500 log returns — sp500ret.csv, 1987–2009, the same series used throughout the GARCH arc. The right-hand tiebreaker panel uses the 5-minute realized-volatility series spx_rv.csv (S&P 500, 2000–2013).

The algorithm: Hamilton filter + Kim smoother¶

Given the parallel variances $h_t^{(k)}$ (deterministic in the parameters and past returns), the likelihood is evaluated by the Hamilton (1989) filter, which carries the regime probability $\xi_t=P(S_t\mid\text{data})$ forward one step at a time:

  1. Predict the regime from the transition matrix: $\;\xi_{t\mid t-1}=\xi_{t-1\mid t-1}\,P$
  2. Emission density in each regime: $\;\eta_t(k)=f\!\big(r_t;\,0,\,h_t^{(k)}\big)$ (Normal, or standardized $t_\nu$)
  3. Likelihood of the observation, marginalising the regime: $\;f(r_t\mid\text{past})=\sum_k \xi_{t\mid t-1}(k)\,\eta_t(k)$, accumulate $\log f$
  4. Update (Bayes' rule): $\;\xi_{t\mid t}(k)=\xi_{t\mid t-1}(k)\,\eta_t(k)\big/ f(r_t\mid\text{past})$

The parameters maximise $\sum_t\log f(r_t\mid\text{past})$. The discrete state is integrated out at every step — never enumerated — which is what keeps it tractable. Afterwards the Kim (1994) smoother runs backward to give $P(S_t\mid\text{all data})$, the regime probabilities plotted below. All of this is in msgarch.py; we fit both Normal and Student-t versions because — as we will see — the choice decides everything.

In [1]:
import numpy as np, pandas as pd, matplotlib.pyplot as plt
from scipy.stats import norm
import msgarch as MS, garch_scratch as G

df = pd.read_csv("sp500ret.csv", parse_dates=["date"]); r = df["ret"].values; dates = df["date"]   # S&P 500 daily, 1987-2009
thN, rc = MS.mle(r, student=False)                         # Normal innovations
thT, _  = MS.mle(r, student=True)                          # Student-t innovations
smT, HT = MS.smooth(thT, rc, student=True); pturbT = smT[:, 1]
smN, _ = MS.smooth(thN, rc, student=False); pturbN = smN[:, 1]
thg, _, _ = G.garch_mle(r, student=True)

def rpt(tag, th, student):
    o1,a1,b1,o2,a2,b2,p11,p22 = th[:8]; aic = -2*MS.loglik(th, rc, student) + 2*len(th)
    ex = ("  nu %.1f" % th[8]) if student else ""
    print("%s (AIC %.0f):  calm vol %.2f%% persist %.3f dur %.0fd | crisis vol %.2f%% persist %.3f dur %.0fd%s"
          % (tag, aic, np.sqrt(o1/(1-a1-b1)), a1+b1, 1/(1-p11), np.sqrt(o2/(1-a2-b2)), a2+b2, 1/(1-p22), ex))
rpt("MS-GARCH-N", thN, False); rpt("MS-GARCH-t", thT, True)
print("single-regime GARCH-t persistence a+b = %.3f" % (thg[1]+thg[2]))
print("calm-regime persistence (t) = %.3f ~ single-regime %.3f  ->  regimes DON'T dissolve the persistence"
      % (thT[1]+thT[2], thg[1]+thg[2]))

rv = pd.read_csv("spx_rv.csv", parse_dates=["date"])       # S&P 500 5-min realized variance, 2000-2013
volt = np.sqrt(252 * (smT * HT).sum(1))                    # MS-GARCH-t conditional vol (annualized %), regime-weighted
fig, ax = plt.subplots(1, 3, figsize=(16, 4.2))
# A: conditional volatility WITH the regimes shaded
ax[0].plot(dates, volt, color="firebrick", lw=.7)
ym = np.percentile(volt, 99.7) * 1.05; ax[0].set_ylim(0, ym)
ax[0].fill_between(dates, 0, ym, where=pturbT > 0.5, color="firebrick", alpha=.10, lw=0)
ax[0].set_ylabel("annualized conditional vol (%)")
ax[0].set_title("Volatility & regimes (MS-GARCH-t, S&P 500 daily 1987-2009)\nhigh vol coincides with the turbulent regime (shaded)")
# B: Normal vs Student-t regimes (robustness)
ax[1].fill_between(dates, pturbT, color="firebrick", alpha=.3, lw=0, label="Student-t (sustained)")
ax[1].plot(dates, pturbN, color="navy", lw=.5, alpha=.7, label="Normal (transient spikes)")
ax[1].set_ylabel("P(turbulent regime)"); ax[1].set_ylim(0, 1); ax[1].legend(fontsize=7, loc="upper left")
ax[1].set_title("Innovation distribution decides:\nNormal -> 1-day spikes,  Student-t -> sustained states")
# C: tiebreaker
lrv = np.log(rv["rv"].values * 1e4)
ax[2].hist(lrv, bins=45, density=True, color="steelblue", alpha=.75)
xx = np.linspace(lrv.min(), lrv.max(), 200)
ax[2].plot(xx, norm.pdf(xx, lrv.mean(), lrv.std()), color="firebrick", lw=2, label="single Gaussian")
ax[2].set_xlabel("log realized variance (spx_rv, 2000-2013)"); ax[2].set_yticks([]); ax[2].legend(fontsize=8)
ax[2].set_title("Tiebreaker: realized vol is UNIMODAL\n(one hump) -- long memory, not two regimes")
for a in (ax[0], ax[1]):
    for lab in a.get_xticklabels(): lab.set_rotation(20)
plt.tight_layout(); plt.show()
MS-GARCH-N (AIC 14772):  calm vol 0.64% persist 0.988 dur 28d | crisis vol 43.31% persist 0.999 dur 1d
MS-GARCH-t (AIC 14663):  calm vol 0.64% persist 0.991 dur 749d | crisis vol 1.49% persist 0.970 dur 707d  nu 6.7
single-regime GARCH-t persistence a+b = 0.997
calm-regime persistence (t) = 0.991 ~ single-regime 0.997  ->  regimes DON'T dissolve the persistence
No description has been provided for this image

Reading the figure — the innovation distribution decides the regimes¶

  • Left — volatility rides the regimes. The MS-GARCH-$t$ conditional volatility (probability-weighted $\sqrt{h_t}$, annualised) sits low through the calm stretches and lifts into the shaded turbulent periods — the dot-com bust and 2008. This is the direct picture of what the model is: the regime is the slow level of volatility, and the fast GARCH dynamics ride on top of it.
  • Middle — Normal vs Student-t: the innovation distribution decides. With Gaussian tails the model is forced to invent a high-volatility regime just to fit the fattest days, so its "turbulent" state has an enormous 43% vol but lasts one day (navy spikes) — a tail mechanism, not a regime one, and it barely dents the persistence. With Student-$t$ tails ($\nu\approx7$) the two regimes become genuine sustained states — a calm one (~0.64% daily vol, ~3-year average duration) and a turbulent one (~1.49% vol) whose smoothed probability (red blocks) tracks the market cycle. MS-GARCH-$t$ posts the best AIC (14663) of any model in the whole arc. This is the robustness lesson — the regime picture is only as good as the innovation assumption.
  • Right — realized vol is unimodal. The two regimes overlap in level (calm ~10% annualised, turbulent ~24%), so the marginal distribution of log-RV is a single hump, not two — one continuous process, not two cleanly separated states.

The verdict: both — but the persistence is long memory¶

The Student-$t$ MS-GARCH shows regimes are real: sustained calm/turbulent states that track the business cycle. But two facts settle the FIGARCH-vs-MS-GARCH contest:

  1. Regimes don't dissolve the persistence. The within-regime persistence stays at 0.99 (calm regime 0.991 vs the pooled 0.997). If the near-unit-root persistence were merely an artifact of switching, accounting for the switches would have collapsed it — it didn't.
  2. The realized-vol evidence points the same way — a unimodal distribution and a smooth, hyperbolic autocorrelation (Phase 1) — matching FIGARCH's $d=0.52$, not discrete level-shifts.

So it is not either/or: the S&P 500 has both a slow regime structure (the vol level drifts with the cycle) and genuine long memory (the persistence within each state). FIGARCH captures the aggregate long memory from returns; MS-GARCH-$t$ adds the business-cycle layer; and the persistence at the heart of the entire volatility arc is real, not a statistical illusion.

Robustness and alternatives¶

  • The innovation distribution is the pivotal setting. Normal → a transient tail regime; Student-$t$ → sustained regimes (shown directly above). Lower-frequency data, three regimes, or level-only (SWARCH) switching also sharpen the regime structure.
  • Independent cross-check. The R MSGARCH package (msgarch_R.ipynb) reproduces the $t$-fit — calm 0.66% / persistence 0.99, turbulent 1.45% / persistence 0.97, AIC 14679 — with a professionally-optimised, independent implementation.
  • Bayesian. A Bayesian MS-GARCH is feasible in PyMC/NumPyro by writing the Hamilton filter as a scan that marginalises the discrete regime analytically (an HMM with GARCH emissions); the MSGARCH package also offers MCMC (FitMCMC) out of the box.

This closes the volatility arc — GARCH/GJR → stochastic volatility → realized volatility (HAR, Realized GARCH, the forecasting tournament) → and the persistence itself, explained as fractional long memory and adjudicated, with realized volatility as the tiebreaker, against the regime-switching rival.

References¶

  • Haas, M., Mittnik, S. & Paolella, M. S. (2004). A new approach to Markov-switching GARCH models. J. Financial Econometrics 2, 493–530.
  • Hamilton, J. D. (1989). A new approach to the economic analysis of nonstationary time series and the business cycle. Econometrica 57, 357–384. • Kim, C.-J. (1994). Dynamic linear models with Markov-switching. J. Econometrics 60, 1–22.
  • Lamoureux, C. G. & Lastrapes, W. D. (1990). JBES 8, 225–234. • Diebold, F. X. & Inoue, A. (2001). Long memory and regime switching. J. Econometrics 105, 131–159.