DCC-GARCH in R (rmgarch)¶

Multivariate volatility, part 2c: the package cross-check¶

This validates the from-scratch and Bayesian DCC fits with rmgarch (Ghalanos) — the industry-standard R implementation of Engle's DCC, which wraps rugarch for the univariate stage and estimates the correlation dynamics in one dccfit call. (rmgarch runs fine through the Jupyter IR kernel used to execute this notebook; the segfaults seen earlier were an artifact of launching R through a bare non-interactive Rscript, not the package.)

Data: DJIA & NASDAQ daily returns, equity_returns.csv (1993–2024) — the same series as dcc_python.ipynb.

In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
suppressMessages(library(rmgarch))
d <- read.csv('equity_returns.csv'); dates <- as.Date(d$date); R <- as.matrix(d[, c('dji', 'nasdaq')])

# stage-1 spec: GARCH(1,1)-normal, zero mean, for each series; DCC(1,1) on top
uspec <- multispec(replicate(2, ugarchspec(mean.model = list(armaOrder = c(0, 0), include.mean = FALSE),
                                           variance.model = list(model = 'sGARCH', garchOrder = c(1, 1)),
                                           distribution.model = 'norm')))
spec <- dccspec(uspec, dccOrder = c(1, 1), distribution = 'mvnorm')
fit <- dccfit(spec, data = R)

cf <- coef(fit); a <- cf['[Joint]dcca1']; b <- cf['[Joint]dccb1']
for (s in c('dji', 'nasdaq'))
  cat(sprintf('  %-7s alpha %.4f  beta %.4f  persist %.3f\n', s,
              cf[paste0('[', s, '].alpha1')], cf[paste0('[', s, '].beta1')],
              cf[paste0('[', s, '].alpha1')] + cf[paste0('[', s, '].beta1')]))
cat(sprintf('\nDCC dynamics (rmgarch::dccfit):  a %.4f   b %.4f   a+b %.4f\n', a, b, a + b))
cat('cross-check -> from-scratch Python a .0532 b .9391 a+b .9923;  Bayesian a .042 b .953  (agree)\n')
Warning message:
"package 'rmgarch' was built under R version 4.6.1"
  dji     alpha 0.1103  beta 0.8729  persist 0.983
  nasdaq  alpha 0.0978  beta 0.8911  persist 0.989

DCC dynamics (rmgarch::dccfit):  a 0.0525   b 0.9400   a+b 0.9926
cross-check -> from-scratch Python a .0532 b .9391 a+b .9923;  Bayesian a .042 b .953  (agree)
In [2]:
options(repr.plot.width = 12, repr.plot.height = 4.4)
rho <- rcor(fit)[1, 2, ]                                    # DCC conditional correlation, DJIA-NASDAQ
rho_ma <- filter(rho, rep(1/21, 21)); const <- cor(R)[1, 2]
plot(dates, rho, type = 'l', col = rgb(.7,.1,.1,.30), lwd = .4, ylim = c(0.2, 1),
     xlab = '', ylab = 'conditional correlation',
     main = 'DCC-GARCH in R (rmgarch::dccfit): DJIA-NASDAQ correlation')
lines(dates, rho_ma, col = 'darkred', lwd = 1.4)
abline(h = const, col = 'navy', lty = 2, lwd = 1.2)
for (dt in c('2000-03-10', '2008-09-15', '2020-03-16')) abline(v = as.Date(dt), col = 'grey55', lty = 3)
legend('bottom', c('DCC rho (daily)', 'DCC rho (21-day avg)', sprintf('constant corr %.2f', const)),
       col = c(rgb(.7,.1,.1,.5), 'darkred', 'navy'), lwd = c(1, 1.5, 1.2), lty = c(1, 1, 2),
       bty = 'n', cex = .8, ncol = 3)
No description has been provided for this image

Results¶

  • rmgarch reproduces the from-scratch DCC. The industry-standard dccfit returns correlation dynamics $a\approx0.05,\ b\approx0.94,\ a+b\approx0.99$ — matching the hand-written two-step engine and the NumPyro posterior — and the conditional-correlation path shows the same story: low in the late-1990s tech divergence, high in every crisis (dashed: dot-com, GFC, COVID).
  • Three independent engines now agree on DCC: from-scratch Python (two-step QMLE), NumPyro (full Bayesian), and R (rmgarch::dccfit).
  • rmgarch adds: analytic standard errors, DCC/aDCC/FDCC variants, copula-GARCH, and multi-step covariance forecasting via dccforecast — the production toolkit around the same estimator.

References¶

  • Engle, R. F. (2002). Dynamic conditional correlation. JBES 20, 339–350.
  • Ghalanos, A. rmgarch: Multivariate GARCH models in R.