Time-varying VAR — R cross-check (rolling-window IRFs)¶

Vector autoregressions, Part 5b¶

Validates bvar_tvpsv_python.ipynb. There is no maintained R package for a full Bayesian TVP-VAR-SV on this toolchain (bvarsv no longer installs), so we use the classical foil that Primiceri's model was designed to improve on: a rolling-window VAR. Re-estimate the same 3-variable VAR by OLS on a moving 15-year window, and compute the recursive (Cholesky, funds-rate-last) impulse response to a monetary shock as of four dates. If the shape and drift of the responses match the Bayesian TVP-VAR — a larger real effect in the 1970s–80s that shrinks by the Great Moderation — then the time-variation is real, not an artefact of the state-space prior.

The contrast is instructive in itself: rolling-window OLS is the non-parametric, frequentist way to let a VAR change over time (a hard window, equal weights, no smoothing), whereas the Bayesian TVP-VAR-SV lets every coefficient drift as a random walk and shrinks the drift — so the rolling estimates should be noisier and the Bayesian ones smoother.

Data: bvar_sv_data.csv — annualised GDP growth, GDP-deflator inflation, Fed funds rate, quarterly 1959–2019 (FRED-QD).

In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
suppressMessages(library(vars))
d <- read.csv('bvar_sv_data.csv'); dt <- as.Date(d$date)
Y <- d[, c('gdp_growth', 'inflation', 'tbill')]                       # tbill last = recursive monetary shock
W <- 60; H <- 20                                                      # 15-year rolling window, 20-qtr horizon
dates <- c('1975-03-01','1981-12-01','1996-03-01','2008-12-01')
labs  <- c('1975 (Great Inflation)','1981 (Volcker)','1996 (Great Moderation)','2008 (crisis)')
gdp <- inff <- matrix(NA, H + 1, length(dates))
for (kk in seq_along(dates)) {
  idx <- which.min(abs(dt - as.Date(dates[kk]))); w <- max(1, idx - W + 1):idx
  v  <- VAR(Y[w, ], p = 2, type = 'const')
  ir <- irf(v, impulse = 'tbill', response = c('gdp_growth','inflation','tbill'),
            n.ahead = H, ortho = TRUE, boot = FALSE)
  sc <- ir$irf$tbill[1, 'tbill']                                      # impact on funds rate -> normalise to +1pp
  gdp[, kk]  <- ir$irf$tbill[, 'gdp_growth'] / sc
  inff[, kk] <- ir$irf$tbill[, 'inflation'] / sc
}
cat('Rolling-window VAR, response to a +1pp monetary shock (peak |response|):\n')
for (kk in seq_along(dates))
  cat(sprintf('  %-28s gdp %+0.2f | inflation %+0.2f\n', labs[kk],
              gdp[which.max(abs(gdp[, kk])), kk], inff[which.max(abs(inff[, kk])), kk]))

options(repr.plot.width = 13, repr.plot.height = 4.2); par(mfrow = c(1, 2))
cols <- c('#1f77b4','#2ca02c','#ff7f0e','#d62728')
for (pj in 1:2) {
  M <- if (pj == 1) gdp else inff; ttl <- if (pj == 1) 'gdp_growth' else 'inflation'
  matplot(0:H, M, type = 'l', lty = 1, lwd = 2, col = cols, xlab = 'quarters after shock', ylab = '',
          main = paste('Response of', ttl, 'to +1pp monetary shock'))
  abline(h = 0, col = 'grey60')
  if (pj == 1) legend('bottomright', labs, col = cols, lwd = 2, bty = 'n', cex = .8)
}
Warning message:
"package 'vars' was built under R version 4.6.1"
Warning message:
"package 'strucchange' was built under R version 4.6.1"
Rolling-window VAR, response to a +1pp monetary shock (peak |response|):
  1975 (Great Inflation)       gdp -1.16 | inflation +1.06
  1981 (Volcker)               gdp -0.75 | inflation +0.43
  1996 (Great Moderation)      gdp +0.20 | inflation +0.11
  2008 (crisis)                gdp +0.40 | inflation -0.36
No description has been provided for this image

Same drift in the transmission, reached two different ways¶

  • The real effect of monetary policy shrinks over time — in both models. The rolling-window VAR, like the Bayesian TVP-VAR-SV, gives its largest output contraction for a monetary tightening in the 1970s–early-1980s (peak response about −1.2) and one that collapses toward zero by the mid-1990s Great Moderation — so noisily, in fact, that the rolling point estimate shrinks to near zero and even flips to a small wrong sign, where the smoother Bayesian TVP-VAR keeps it cleanly negative but small. Two entirely different machineries — a hard 15-year OLS window vs. a shrunk random-walk state space — agree that the monetary transmission mechanism was not constant.
  • The price puzzle appears here too. Under the same recursive identification, the inflation response is positive (a tightening followed by higher measured inflation) in the early windows — the well-known price-puzzle artefact of Part 2, not a failure of either implementation — and it, too, fades in the later windows.
  • Bayesian shrinkage buys smoothness. The rolling estimates are visibly choppier and jump as extreme observations enter and leave the window; the Bayesian TVP-VAR's random-walk prior smooths the evolution and pools information across time, which is exactly why Primiceri's state-space approach superseded rolling regressions for this question.

References¶

  • Primiceri, G. E. (2005). Time varying structural vector autoregressions and monetary policy. Review of Economic Studies 72, 821–852.
  • Cogley, T. & Sargent, T. J. (2005). Drifts and volatilities. Review of Economic Dynamics 8, 262–302.
  • Pfaff, B. (2008). VAR, SVAR and SVEC models: implementation in R (vars). J. Statistical Software 27(4).