MS-GARCH in R (MSGARCH)¶

The robust package cross-check (companion to msgarch_python.ipynb)¶

The from-scratch notebook showed that Markov-switching GARCH needs Student-t innovations to find sustained calm/turbulent regimes (Normal innovations collapse the second regime into one-day tail spikes). R's MSGARCH package (Ardia, Bluteau, Boudt, Catania & Trottier) is the professional tool: per-regime GARCH families, several distributions, $K$ regimes, and both ML and Bayesian estimation. We fit the same 2-regime Student-t model and confirm the from-scratch estimates.

Data: daily S&P 500 log returns, sp500ret.csv (1987–2009) — the same series the whole GARCH arc uses.

In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
suppressMessages(library(MSGARCH))
d <- read.csv('sp500ret.csv'); r <- d$ret; dates <- as.Date(d$date)   # S&P 500 daily returns, 1987-2009

# 2-regime Markov-switching sGARCH with Student-t innovations in each regime
spec <- CreateSpec(variance.spec     = list(model = c('sGARCH', 'sGARCH')),
                   distribution.spec = list(distribution = c('std', 'std')),
                   switch.spec       = list(do.mix = FALSE))          # do.mix=FALSE -> Markov switching
fit <- FitML(spec, data = r)
p <- fit$par; P <- TransMat(fit)
pr1 <- p['alpha1_1'] + p['beta_1']; pr2 <- p['alpha1_2'] + p['beta_2']
uv1 <- sqrt(p['alpha0_1'] / (1 - pr1)); uv2 <- sqrt(p['alpha0_2'] / (1 - pr2))
cat('MSGARCH-t (2-regime sGARCH, Student-t) on S&P 500 daily returns 1987-2009:\n')
cat(sprintf('  calm      regime: uncond vol %.2f%%  persistence %.3f  nu %.1f  stay-prob %.3f\n', uv1, pr1, p['nu_1'], P[1,1]))
cat(sprintf('  turbulent regime: uncond vol %.2f%%  persistence %.3f  nu %.1f  stay-prob %.3f\n', uv2, pr2, p['nu_2'], P[2,2]))
cat(sprintf('  AIC %.0f\n', AIC(fit)))
cat('\ncross-check -> from-scratch MS-GARCH-t: calm 0.64%/persist .991, turbulent 1.49%/persist .970  (agree)\n')
Warning message:
"package 'MSGARCH' was built under R version 4.6.1"
MSGARCH-t (2-regime sGARCH, Student-t) on S&P 500 daily returns 1987-2009:
  calm      regime: uncond vol 0.66%  persistence 0.991  nu 6.2  stay-prob 0.999
  turbulent regime: uncond vol 1.45%  persistence 0.969  nu 7.6  stay-prob 0.999
  AIC 14679
cross-check -> from-scratch MS-GARCH-t: calm 0.64%/persist .991, turbulent 1.49%/persist .970  (agree)

Conditional volatility and the regimes¶

In [2]:
options(repr.plot.width = 11, repr.plot.height = 4.3)
vol <- as.numeric(Volatility(fit)) * sqrt(252)                        # annualized conditional volatility (%)
sp  <- tail(State(fit)$SmoothProb[, 1, 2], length(r))                 # smoothed P(turbulent regime)
turb <- sp > 0.5; rl <- rle(turb); e <- cumsum(rl$lengths); s <- e - rl$lengths + 1

plot(dates, vol, type = 'n', xlab = '', ylab = 'annualized conditional vol (%)',
     main = 'MSGARCH-t: conditional volatility and regimes (S&P 500 daily, 1987-2009)')
for (i in which(rl$values)) rect(dates[s[i]], 0, dates[e[i]], max(vol), col = rgb(.7, .1, .1, .12), border = NA)
lines(dates, vol, col = 'firebrick', lwd = .6)
legend('topleft', c('conditional volatility', 'turbulent regime'),
       col = c('firebrick', rgb(.7, .1, .1, .35)), lwd = c(1.5, 9), bty = 'n', cex = .85)
No description has been provided for this image

Results¶

  • MSGARCH reproduces the from-scratch fit. Two sustained regimes — a calm state (~0.66% daily vol, persistence 0.99) and a turbulent state (~1.45% daily vol, persistence 0.97), each highly persistent (stay-probabilities ~0.999) — matching the hand-written Hamilton-filter estimates (0.64% / 1.49%). The shaded turbulent periods track the dot-com bust (2000–2002) and 2008; the fat tails are absorbed by the Student-$t$ ($\nu\approx6$–8), not by a spurious one-day regime.
  • The volatility rides the regimes. The conditional-volatility line sits low through the calm (unshaded) stretches and lifts into the shaded turbulent regimes — the regime is the slow level of volatility.
  • The verdict is unchanged. Even with these clean sustained regimes, the within-regime persistence stays near 0.99 — allowing regimes does not dissolve the persistence. The near-unit-root persistence is genuine long memory (FIGARCH $d=0.52$); the regimes add a slow business-cycle layer, but are not the explanation for the persistence.
  • What the package adds beyond the from-scratch: per-regime GARCH families and distributions, $K>2$ regimes, Bayesian (MCMC) estimation via FitMCMC, and built-in forecasting / VaR / model comparison.

References¶

  • Ardia, D., Bluteau, K., Boudt, K., Catania, L. & Trottier, D.-A. (2019). Markov-switching GARCH models in R: the MSGARCH package. J. Statistical Software 91(4).
  • Haas, M., Mittnik, S. & Paolella, M. S. (2004). A new approach to Markov-switching GARCH models. J. Financial Econometrics.