Bayesian VAR — finance application, R cross-check¶
Vector autoregressions, part 2b¶
Validates bvar_yields_python.ipynb with vars (classical VAR — Granger, orthogonalized IRF) and BVAR (Minnesota-prior BVAR), on the identical Treasury-yield system.
Data: bvar_yields_data.csv — US Treasury yields (monthly, 1994–2024): 3-month, 5-year, 10-year, 30-year.
In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
suppressMessages(library(vars))
d <- read.csv('bvar_yields_data.csv'); Y <- d[, c('y3m', 'y5y', 'y10y', 'y30y')]
lab <- c('3-month', '5-year', '10-year', '30-year')
v <- VAR(Y, p = 2, type = 'const')
cat('Classical VAR(2) via vars::VAR -- Granger causality (F-test p-values):\n')
for (j in seq_along(colnames(Y)))
cat(sprintf(' %-8s -> rest: p = %.3f\n', lab[j], causality(v, cause = colnames(Y)[j])$Granger$p.value))
cat('cross-check -> statsmodels: 3m .002, 5y .000, 10y .068, 30y .030 (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"
Classical VAR(2) via vars::VAR -- Granger causality (F-test p-values):
3-month -> rest: p = 0.002 5-year -> rest: p = 0.000 10-year -> rest: p = 0.068 30-year -> rest: p = 0.030
cross-check -> statsmodels: 3m .002, 5y .000, 10y .068, 30y .030 (agree)
In [2]:
options(repr.plot.width = 13, repr.plot.height = 4)
ir <- irf(v, impulse = 'y3m', response = c('y3m','y5y','y10y','y30y'),
n.ahead = 24, ortho = TRUE, boot = TRUE, runs = 200)
plot(ir) # curve response to a short-rate (3-month) shock
In [3]:
suppressMessages(library(BVAR))
set.seed(1)
b <- bvar(Y, lags = 2, n_draw = 6000, n_burn = 2000, verbose = FALSE) # Minnesota-prior BVAR
fc <- predict(b, horizon = 12); fq <- summary(fc)$quants
cat('Minnesota BVAR via BVAR::bvar -- 12-month forecast (posterior median):\n')
for (j in 1:4) cat(sprintf(' %-8s now %.2f -> h=12 %.2f\n', lab[j], tail(Y[[j]],1), fq['50%', 12, j]))
cat('\ncross-check -> Python BVAR: highly persistent, near-random-walk forecasts; dynamics agree.\n')
Warning message: "package 'BVAR' was built under R version 4.6.1"
Minnesota BVAR via BVAR::bvar -- 12-month forecast (posterior median):
3-month now 4.18 -> h=12 3.84 5-year now 4.37 -> h=12 4.19 10-year now 4.55 -> h=12 4.46 30-year now 4.76 -> h=12 4.75
cross-check -> Python BVAR: highly persistent, near-random-walk forecasts; dynamics agree.
Results¶
- Classical VAR reproduces the Python fit.
vars::VARreturns the same Granger verdicts — the short/medium tenors lead, the long end only weakly predictable — and the same orthogonalized IRF: a short-rate shock lifts the whole curve but with declining amplitude toward the long end (the flattening / policy-pass-through pattern), confirming thestatsmodelsresult. - Minnesota BVAR agrees.
BVAR::bvargives the same near-random-walk persistence and forecasts, matching the from-scratchbvar.pyMinnesota fit. - Steady-state model: as in Part 1, the Villani mean-adjusted prior (the neutral-curve anchor) is validated by the from-scratch Python engine;
vars/BVARcross-check the classical and Minnesota pieces.
References¶
- Pfaff, B. (2008). VAR, SVAR and SVEC models: implementation in R (
vars). JSS 27(4). - Kuschnig, N. & Vashold, L. (2021).
BVAR: Bayesian VARs with hierarchical prior selection. JSS 100(14).