Oil SVAR identification debate — R cross-check¶

Vector autoregressions, Part 8b (R)¶

Validates oilsvar2_python.ipynb. The canonical R package for sign restrictions (VARsignR) is archived, so — as in the Part 2 monetary notebook — we implement the rotation search directly in base R on a vars::VAR fit: draw Haar-uniform rotations, keep those whose impact responses match the supply / aggregate-demand / oil-specific-demand sign pattern, and read off the supply share of the real-oil-price variance. If its distribution matches the Python flat-sign result, the set-identification is confirmed independently.

Data: oilsvar_data.csv — world oil production growth, Kilian real-activity index, real oil price; monthly 1974–2019, VAR(24).

In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
suppressMessages(library(vars))
d <- read.csv('oilsvar_data.csv'); Y <- d[, c('prod_growth','rea','rpo')]
v <- VAR(Y, p = 24, type = 'const')
C <- t(chol(cov(residuals(v))))                      # lower-triangular impact factor
Ph <- Phi(v, nstep = 18)                             # reduced-form MA, Ph[,,1] = I

set.seed(1); shares <- c()
for (it in 1:60000) {
  Q <- qr.Q(qr(matrix(rnorm(9), 3, 3)))
  A0 <- C %*% Q
  A0 <- A0 %*% diag(sign(A0[3, ]))                   # normalise each shock so the real oil price (row 3) rises
  cls <- rep(NA, 3)
  for (c in 1:3) { pp <- A0[1, c]; ra <- A0[2, c]
    cls[c] <- if (pp < 0 && ra < 0) 'sup' else if (pp > 0 && ra > 0) 'agg' else if (pp > 0 && ra < 0) 'oil' else NA }
  if (any(is.na(cls)) || length(unique(cls)) < 3) next
  A0o <- A0[, c(which(cls == 'sup'), which(cls == 'agg'), which(cls == 'oil'))]
  ss <- rep(0, 3); for (h in 1:19) { g <- Ph[, , h] %*% A0o; ss <- ss + g[3, ]^2 }   # var of real oil price by shock
  shares <- rbind(shares, ss / sum(ss))
}
cat(sprintf('accepted %d of 60000 rotations (%.1f%%)\n', nrow(shares), 100 * nrow(shares) / 60000))
cat(sprintf('supply share of real-oil-price variance: median %.2f  [%.2f, %.2f]\n',
            median(shares[, 1]), quantile(shares[, 1], .16), quantile(shares[, 1], .84)))
cat('cross-check -> Python flat sign restrictions: supply 0.31 [0.10, 0.60]  (agree)\n')
Warning message:
"package 'vars' was built under R version 4.6.1"
Warning message:
"package 'strucchange' was built under R version 4.6.1"
accepted 8888 of 60000 rotations (14.8%)
supply share of real-oil-price variance: median 0.30  [0.10, 0.59]
cross-check -> Python flat sign restrictions: supply 0.31 [0.10, 0.60]  (agree)

Same wide, supply-heavy set¶

  • The independent R rotation search agrees. Base-R Haar rotations that satisfy the same sign pattern put the supply share of the real-oil-price variance near ~0.3 with a wide band — reproducing the Python result and, with it, the central point: flat sign restrictions are far from agnostic about supply, and attribute much more of the oil price to supply than the recursive scheme.
  • The Kilian-Murphy elasticity bound and the Baumeister-Hamilton prior (Python notebook) are what narrow this set back toward the demand story — and they do so by stating an economic belief explicitly, which is the honest way to identify.

References¶

  • Kilian, L. & Murphy, D. P. (2012). J. European Economic Association 10, 1166–1188.
  • Baumeister, C. & Hamilton, J. D. (2019). American Economic Review 109, 1873–1910.