Atlas
statminds
Time Series (Dynamic Systems Model)The underlying model family class (e.g. GLM, linear model, categorical matrix, log-linear).Parametric ReferenceStatistical methods that assume a specific probability distribution family (typically normal).12-stage workflow

VAR Models

The engine for Dynamic Multivariate Discovery. Vector Autoregression (VAR) audits the internal pulse of a cluster of time series, revealing how multiple variables influence each other's future in a unified temporal system.

Model familyTime Series (Dynamic Systems Model)
Hypothesisprediction_and_causality_focused
AliasesVector Autoregression · Dynamic System Model · Multivariate ARIMA (Reduced)
G1
Multivariate Pulse Audit
Decipher the 'Cross-Variable' signals where every series acts as both a predictor and an outcome.
G2
Dynamic Feedback Mapping
Identify the recursive loops where Change in X leads to Change in Y, which then feeds back into X.
G3
Systemic Forecasting
Construct a unified mathematical engine that projects the entire 'Outcome Vector' into the future.
Visual Overview Dashboard
1

What is it?

VAR Models analyzes sequences of data points ordered chronologically over time to extract patterns, model trends, and make forecasts.

The engine for Dynamic Multivariate Discovery. Vector Autoregression (VAR) audits the internal pulse of a cluster of time series, revealing how multiple variables influence each other's future in a unified temporal system.

2

Goals & Indications

  • Multivariate Pulse Audit: Decipher the 'Cross-Variable' signals where every series acts as both a predictor and an outcome.
  • Dynamic Feedback Mapping: Identify the recursive loops where Change in X leads to Change in Y, which then feeds back into X.
  • Systemic Forecasting: Construct a unified mathematical engine that projects the entire 'Outcome Vector' into the future.
3

Core Idea Diagram

Interdependent System (VAR)
4

Claims tested

H₀: H₀: No Granger causality between variables (past values of X don't help predict Y beyond Y's own past values)
Hₐ: Hₐ: Variables exhibit Granger causality (past values of some variables improve prediction of others beyond own history)
5

How it works

  1. Ensure all multivariate time series are stationary (difference if unit root exists).
  2. Select lag order p and set up system of linear autoregressive equations.
  3. Estimate system parameters simultaneously using OLS equation-by-equation.
  4. Analyze dynamic interactions using Impulse Response Functions and FEVD.
6

Assumptions

All variables stationary after transformation: All K variables in system must be stationary (constant mean, variance, covariance structure)
Appropriate lag order selection: Correct number of lags (p) to capture dynamic relationships without over-parameterization
No structural breaks across series: Parameters (A₁,...,A_p, Σ) constant over time; no regime changes or policy shifts
7

Important Note

VAR models capture dynamic interdependencies among multiple time series. Each variable is modeled as linear function of its own lags plus lags of all other variables in system. Key focus: (1) Granger causality tests: does X help predict Y controlling for Y's past? (2) Impulse Response Functions (IRFs): dynamic effect of shock in one variable on all variables over time. (3) Forecast Error Variance Decomposition (FEVD): proportion of forecast variance in each variable explained by shocks to other variables. (4) Multivariate forecasting: generate joint forecasts accounting for cross-series correlations. VAR(p) uses p lags: y_t = c + A₁y_{t-1} + A₂y_{t-2} + ... + A_py_{t-p} + ε_t where y_t is K×1 vector, A_i are K×K coefficient matrices. Key distinction from univariate ARIMA: VAR explicitly models cross-variable dynamics. Key distinction from structural models: VAR imposes minimal restrictions (reduced-form). Important: Granger causality ≠ true causation (only predictive relationship in temporal sense). All variables must be stationary or use cointegration framework (VECM).

8

Worked Example

EquationCoefficientEstimatet-statp-value
Y1 ~ Y1(t-1)φ₁₁0.625.14<0.001
Y1 ~ Y2(t-1)φ₁₂0.282.310.021
Interactive Sandbox

Vector Autoregressive (VAR) Laboratory

VAR models model multi-variable feedback loops. Change coefficients and observe transition eigenvalue stability. If eigenvalues ≥ 1.0, the system becomes explosive.

Self-impact of Y₁ (φ₁₁)0.50
Cross-impact Y₂ → Y₁ (φ₁₂)0.30
VAR System Stability Check
Maximum Eigenvalue (λmax): 0.7541
System State:Stable (Co-reverting)
Simulated Vector paths
3.0-3.2
Variable Y₁
Variable Y₂
The 12-Stage Precision Workflow
01Systemic Dependency
Hypotheses
We test the null of 'Isolated Random Walks' against the discovery of a non-zero dynamic bond between all series in the vector.
02Stationarity Parity
Assumptions
The ultimate gatekeeper: EVERY series in the vector MUST be stationary—ensuring the system doesn't drift into mathematical instability.
03Lag-Order Symmetry
Diagnostics
Utilizing HQ, SC, and AIC criteria to select the optimal 'Temporal Reach' (p)—balancing system richness against parsimony.
04focus
Modeling the dynamic interaction between App Engagement, Sleep Quality, and Pain Scores in a cohort of 500 daily users.
05VECM Pivot
Alternatives
Knowing when to switch to Vector Error Correction (VECM) if the series are non-stationary but 'Linked' through long-term cointegration.
06Impulse Response (IRF)
Forensics
Executing 'Shock Strikes'—simulating a 1-unit jump in X to see how the 'Ripple Effect' travels through Y and Z over time.
07Variance Decomp (FEVD)
Effect Size
Interpreting 'FEVD' to determine exactly what percentage of each series' future variance is 'Owned' by other variables in the system.
08Matrix Buffer
Sample Size
Accounting for the exponential N required: a VAR with 3 series and 4 lags requires estimating 39+ parameters—demanding massive temporal depth.
09The Stability Narrative
Reporting
Reporting the Roots of the Companion Matrix to prove the system is 'Stable' and will not explode or oscillate uncontrollably.
10vars / VAR Logic
Software
Executing 'VAR()' commands, ensuring the 'const' or 'trend' terms are correctly specified for every outcome vector.
11focus
Avoiding the 'Complexity Sin'—using too many lags which 'Chokes' the model with non-significant parameters, killing predictive power.
12focus
Tracing the model back to Christopher Sims (1980) and the revolution of 'Atheoretical' multivariate time series forensics.
01Hypothesis test logic

Hypotheses

Pragmatic null and alternative hypotheses defined in mathematical notation.

A hypothesis is a question sharpened to a point. Ambiguity is the enemy of inference.
Logic Core
Null · H₀

H₀: No Granger causality between variables (past values of X don't help predict Y beyond Y's own past values)

Alternative · Hₐ

Hₐ: Variables exhibit Granger causality (past values of some variables improve prediction of others beyond own history)

Why it matters prediction_and_causality_focused

VAR models capture dynamic interdependencies among multiple time series. Each variable is modeled as linear function of its own lags plus lags of all other variables in system. Key focus: (1) Granger causality tests: does X help predict Y controlling for Y's past? (2) Impulse Response Functions (IRFs): dynamic effect of shock in one variable on all variables over time. (3) Forecast Error Variance Decomposition (FEVD): proportion of forecast variance in each variable explained by shocks to other variables. (4) Multivariate forecasting: generate joint forecasts accounting for cross-series correlations. VAR(p) uses p lags: y_t = c + A₁y_{t-1} + A₂y_{t-2} + ... + A_py_{t-p} + ε_t where y_t is K×1 vector, A_i are K×K coefficient matrices. Key distinction from univariate ARIMA: VAR explicitly models cross-variable dynamics. Key distinction from structural models: VAR imposes minimal restrictions (reduced-form). Important: Granger causality ≠ true causation (only predictive relationship in temporal sense). All variables must be stationary or use cointegration framework (VECM).

02Model diagnostics

Assumptions

The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.

Stationarity is the prerequisite of prediction. You cannot forecast a system that is fundamentally changing its rules.
Integrity Shield
8
Assumptions
5
Critical / High Severity
How to check
Quick
Plot each time series: stationary shows constant mean (horizontal), constant variance. Plot ACF for each series: stationary has ACF decaying quickly. Non-stationary: wandering/trending mean, slow-decaying ACF (near 1.0 for many lags). Check correlation matrices across time: should be stable
Rigorous
Test each variable individually: (1) Augmented Dickey-Fuller (ADF) test: H₀=unit root (non-stationary), p<0.05 indicates stationarity. (2) KPSS test: H₀=stationarity, p<0.05 indicates non-stationarity. Use both tests: ADF rejects + KPSS doesn't reject = stationary. (3) Phillips-Perron test as robust alternative. (4) Joint stationarity: some implementations test multivariate stationarity via eigenvalues of companion matrix (all inside unit circle = stationary). (5) Check for cointegration if variables non-stationary: Johansen test. If cointegrated, use VECM not VAR
If violated
If any variable non-stationary: (1) Apply transformations: log transform for exponential growth/variance stabilization. First difference each variable: ∇y_t = y_t - y_{t-1} removes linear trend. Verify stationarity after transformation. (2) If multiple variables non-stationary but cointegrated (share long-run equilibrium): use Vector Error Correction Model (VECM) instead of VAR. VECM incorporates cointegrating relationship explicitly. (3) Seasonal differencing if seasonal non-stationarity: y_t - y_{t-s}. (4) Check for deterministic trends: include trend in VAR specification. (5) NEVER mix stationary and non-stationary variables without cointegration correction: spurious relationships. Test for cointegration using Johansen test or Engle-Granger
How to check
Quick
Use VARselect() in R or VAR.select_order() in Python: reports AIC, BIC, HQ for different lag orders. Start with reasonable maximum (e.g., 8-12 lags for monthly data, 4-6 for quarterly). BIC typically selects smaller p (more parsimonious), AIC larger p. Check if coefficient matrices at lag p significantly different from zero
Rigorous
Systematic lag selection: (1) Fit VAR(p) for p=0,1,2,...,p_max. (2) Compare information criteria: AIC = 2k - 2ln(L), BIC = k·ln(n) - 2ln(L), HQ = 2k·ln(ln(n)) - 2ln(L) where k=K²p+K (total parameters), K=number of variables. BIC most consistent, penalizes complexity most. (3) Likelihood ratio test: test H₀: VAR(p-1) vs Hₐ: VAR(p). (4) Check residual autocorrelation: Portmanteau test (multivariate Ljung-Box). If significant autocorrelation remains, increase p. (5) Forecast evaluation: out-of-sample RMSE on holdout. (6) Parsimony: prefer simplest adequate model. Typical range: p=1-4 for quarterly, p=1-12 for monthly
If violated
Under-specification (p too small): residuals show autocorrelation (Portmanteau test significant), Granger causality tests biased, omitted dynamics. Fix: (1) Increase p systematically until residual autocorrelation eliminated. (2) Check information criteria: if AIC/BIC still decreasing, increase p. (3) Test for autocorrelation at each lag. Over-specification (p too large): excessive parameters relative to sample size, coefficient estimates unstable (high standard errors), multicollinearity among lags, poor out-of-sample forecasts, loss of degrees of freedom. Fix: (1) Reduce p using BIC (penalizes complexity). (2) Check coefficient significance: many non-significant → over-parameterized. (3) Use shrinkage methods (Bayesian VAR with Minnesota prior, ridge regression). (4) Consider factor-augmented VAR (FAVAR) if many variables. Rule of thumb: need n ≥ 3×K²p observations
How to check
Quick
Plot all K time series: visual inspection for sudden level shifts, trend changes, or volatility changes around known events (policy changes, crises, regime shifts). Plot recursive residuals: systematic deviation suggests breaks. Check if major events (2008 crisis, COVID-19, policy interventions) within sample period
Rigorous
Formal break tests: (1) Chow test: split sample at suspected break date, test if coefficients differ across subsamples. (2) CUSUM test: cumulative sum of recursive residuals, significant deviation from zero indicates instability. (3) CUSUM-SQ test: cumulative sum of squared recursive residuals. (4) Bai-Perron test: endogenously detect multiple break points. (5) Rolling window estimation: fit VAR on moving windows, check if coefficients stable over time. (6) Stability test via eigenvalues: check if companion matrix eigenvalues stable over subsamples. Plot recursive coefficient estimates with confidence bands: should be stable
If violated
If structural break detected: (1) Split sample: fit separate VAR models pre/post break. Acknowledge limitation: historical dynamics may not apply to forecast period. (2) Include dummy variables: level shift dummy (0→1 at break), slope change (0,0,1,2,3,...), pulse dummy (single spike). Test joint significance. (3) Time-Varying Parameter VAR (TVP-VAR): allow coefficients to evolve smoothly over time (Bayesian framework, state-space). (4) Markov-Switching VAR: discrete regime changes with probabilistic transitions. (5) If break at end of sample near forecast period: use only post-break data for estimation (sacrifice sample size for parameter stability). (6) Conduct forecast evaluation pre/post break separately. Report uncertainty due to structural change
How to check
Quick
Scatterplot matrix of variables and their lags: should show linear patterns. Plot residuals vs fitted values for each equation: should be random cloud (no curved patterns, heteroscedasticity). Check if relationships change in high vs low regimes (expansions vs recessions, high vs low volatility)
Rigorous
Formal nonlinearity tests: (1) Ramsey RESET test on each VAR equation: add powers of fitted values, test joint significance. (2) BDS test on residuals: detects nonlinear dependence remaining in residuals. (3) Teräsvirta linearity test: tests against smooth transition VAR (STVAR). (4) Compare VAR to nonlinear alternatives (threshold VAR, neural network VAR) using out-of-sample forecast accuracy. (5) Examine IRFs: should be similar across different historical episodes if linear (use generalized IRFs). (6) Check if forecast errors systematically larger during specific regimes (suggests nonlinearity)
If violated
If nonlinearity detected: (1) Threshold VAR (TVAR): allow coefficients to differ across regimes based on threshold variable (e.g., lagged GDP growth). Estimate using threshold least squares or Bayesian methods. (2) Smooth Transition VAR (STVAR): smooth transition between regimes via logistic function. (3) Interaction terms: add cross-products of variables if specific nonlinear interaction suspected. (4) Transformations: log, Box-Cox on variables to linearize relationships. (5) Time-Varying Parameter VAR: allows for smooth parameter evolution. (6) Neural Network VAR or machine learning approaches for complex nonlinearities. (7) Piecewise linear: estimate separate VARs for different regimes (expansions/recessions). Document: nonlinear models more data-hungry, less interpretable, harder to estimate. Linear VAR often adequate approximation for forecasting
How to check
Quick
Plot residuals for each equation over time: should appear random, no patterns. Plot cross-correlation of residuals at different lags: should be near zero. Check if residual variances constant over time (no heteroscedasticity clusters). Residuals can be contemporaneously correlated (Σ ≠ I) but not serially correlated
Rigorous
Multivariate residual autocorrelation tests: (1) Portmanteau test (multivariate Ljung-Box): H₀=no autocorrelation up to lag h, test statistic Q(h) ~ χ²(K²(h-p)) where p=VAR lag order. Test at h=p+1, p+4, p+8. p>0.05 indicates white noise. (2) Breusch-Godfrey LM test: regress residuals on lagged residuals and original regressors. (3) Plot multivariate ACF: each element should be within confidence bands. (4) Check equation-by-equation: univariate Ljung-Box on each residual series. (5) Test for ARCH effects: multivariate ARCH test if volatility clustering suspected. Contemporaneous correlation OK: cov(ε_t) = Σ can be non-diagonal (captures same-period relationships)
If violated
If residual autocorrelation detected: (1) Increase lag order p: most common fix. If Portmanteau test fails, add more lags until autocorrelation eliminated. (2) Check for omitted variables: important variable missing from system? Adding relevant variable can absorb autocorrelation. (3) Examine structural breaks: autocorrelation may indicate misspecification due to breaks (see Assumption 3). (4) Check for outliers: extreme observations can create apparent autocorrelation. Use outlier detection, consider dummy variables. (5) If heteroscedasticity (ARCH effects): use multivariate GARCH or robust standard errors. (6) If all else fails: consider alternative model class (state-space, nonlinear). NEVER proceed with autocorrelated residuals: Granger causality tests invalid, forecasts suboptimal, IRFs biased
How to check
Quick
Check correlation matrix of all lagged variables: if any correlations ≥0.99, potential multicollinearity. Check if any variable is linear combination of others. Look for warning messages during estimation ('singular matrix', 'rank deficiency'). Examine coefficient standard errors: extremely large SEs indicate multicollinearity
Rigorous
Formal multicollinearity diagnostics: (1) Condition number of design matrix X'X: κ(X'X) > 30 indicates severe multicollinearity. (2) Variance Inflation Factors (VIFs) for each regressor: VIF > 10 problematic. (3) Eigenvalues of X'X: very small eigenvalues (near zero) indicate near-singularity. (4) Check if estimation algorithm converges: non-convergence or instability suggests rank deficiency. (5) Examine standard errors: if very large relative to coefficient estimates across many parameters, suspect multicollinearity. (6) Try dropping one variable: if coefficients change dramatically, variables highly collinear
If violated
Perfect multicollinearity: (1) Remove redundant variable: if one variable exact linear combination of others, drop it. (2) Check for duplicate variables or transformations (e.g., including both X and log(X)). (3) Reduce lag order: high p with many variables creates many regressors, increasing multicollinearity. High but not perfect multicollinearity: (1) Accept it: still unbiased estimates, just high variance. Focus on joint hypothesis tests and forecasts rather than individual coefficients. (2) Shrinkage/regularization: Bayesian VAR with Minnesota prior shrinks coefficients toward zero, reducing variance. Ridge regression adds penalty to large coefficients. (3) Increase sample size if possible. (4) Principal components: use PC-VAR (extract principal components, model those instead). (5) Factor models: FAVAR extracts common factors. (6) Remove least important variables based on domain knowledge. Note: some multicollinearity expected/acceptable in VAR due to lagged nature; perfect multicollinearity rare
ridge regression
How to check
Quick
Visual inspection: plot time series for systematic changes in relationships (correlations shifting over time). Estimate VAR on first half vs second half: compare coefficients informally. Plot rolling window forecasts: systematic deterioration suggests parameter instability. Check if IRFs from different historical periods qualitatively similar
Rigorous
Parameter stability tests: (1) Recursive estimation: estimate VAR on expanding windows (t=1:50, 1:51, ..., 1:T), plot coefficient paths with confidence bands. Systematic trends/breaks indicate instability. (2) Rolling window: estimate on moving windows, check coefficient variation. (3) Formal tests: Chow test at specific date, CUSUM/CUSUM-SQ tests, Hansen stability test. (4) Forecast stability: one-step-ahead forecast errors should be stable over time (plot recursive forecast errors, test for breaks). (5) Fluctuation test: test if coefficients significantly time-varying. (6) Compare AIC/BIC of fixed vs time-varying parameter models
If violated
If parameter instability detected: (1) See Assumption 3 (structural breaks): same fixes apply. (2) Time-Varying Parameter VAR (TVP-VAR): model coefficients as random walk or stochastic process. Estimated via Bayesian methods (Kalman filter for state evolution). Allows smooth parameter evolution. (3) Rolling window forecasting: estimate on recent window only (e.g., last 5-10 years), sacrificing sample size for parameter stability. Update window as new data arrives. (4) Weighted least squares: down-weight older observations (exponential smoothing). (5) Break-adjusted estimation: dummy variables for known breaks. (6) Regime-switching models: discrete parameter changes. (7) Acknowledge limitation: historical dynamics may differ from future; report parameter uncertainty. For forecasting: recent data often more relevant than distant past
How to check
Quick
Calculate parameters: K²p + K (K² coefficients per lag, K constants). Rule of thumb: need n ≥ 3×(K²p+K) for reliable estimation. Example: VAR(4) with K=3 variables has 3²×4 + 3 = 39 parameters, needs n≥117. For monthly data, that's ~10 years. Check if standard errors reasonable (not extremely large)
Rigorous
Sample size adequacy: (1) Degrees of freedom: df = n - K²p - K. Should be substantial (≥30) for asymptotic theory. (2) Parameters-to-observations ratio: K²p/n should be small (≤0.1). (3) Examine coefficient standard errors: very large SEs indicate insufficient data. (4) Forecast evaluation: if out-of-sample performance poor despite good in-sample fit, may be overfitting (too many parameters). (5) Information criteria penalties: AIC/BIC increase sharply as model grows → insufficient data for complexity. (6) Monte Carlo studies suggest n≥100 minimum for K=3, p=4; larger K or p requires more data. Monthly data: need ≥8-10 years. Quarterly: ≥20-25 years
If violated
Insufficient sample size (n too small for parameters): (1) Reduce K: include only most important variables. Use domain knowledge or data reduction (PCA). (2) Reduce p: use smaller lag order (BIC prefers parsimony). Trade lag length for sample size. (3) Bayesian VAR with shrinkage: Minnesota prior shrinks coefficients toward zero or random walk, effectively reducing parameter dimensionality. Allows estimation with smaller samples. (4) Factor-Augmented VAR (FAVAR): extract few factors from many variables, model factors instead. (5) Panel VAR: pool data from multiple units (countries, firms) to increase effective sample size. (6) Restricted VAR: impose restrictions based on theory (some coefficients=0, equality constraints). (7) Aggregation: use lower frequency (monthly→quarterly) to get longer effective history. (8) Acknowledge limitation: report large standard errors, wide forecast intervals
03Residual Forensics

Diagnostics

Checking residual plots and indices to examine model deviations and ensure standard error integrity.

White noise is the goal. If your residuals have a pattern, you have left information on the table.
System Health
Essential checks
  1. Multivariate Ljung-Box / Portmanteau test for systemic residual autocorrelation.
  2. Stability audit of the Companion Matrix (eigenvalues must be < 1.0).
  3. Lag-length selection audit using AIC / BIC / FPE consensus.
  4. Individual unit root tests (ADF) for every series in the vector.
  5. Residual covariance matrix check for cross-variable signal bleed.
Recommended checks
  1. Impulse Response Function (IRF) bootstrapping to audit shock-response sensitivity.
  2. FEVD (Forecast Error Variance Decomposition) stability audit.
  3. ARCH-LM test on residuals to detect multivariate volatility clustering.
  4. Granger Causality strikes to verify the directional flow of the system.
  5. Structural Break audit (Chow Test) to ensure system parameters are constant over time.
04Live Instances

Applied Minds

Review concrete study examples, data layout guidelines, and copy executable syntax scripts.

Theory is the map. Practice is the terrain. Simulation bridges the gap.
Applied Wisdom
Example 01

GDP, Inflation, and Interest Rates

3-variable VAR system modeling the dynamic relationships among GDP growth, inflation rate, and interest rate using n=120 quarterly observations (30 years). Demonstrates complete VAR workflow: data generation with realistic interdependencies, stationarity testing for all variables, lag order selection via multiple information criteria (AIC, BIC, HQ), VAR estimation using vars package in R and statsmodels in Python, Granger causality tests to identify predictive relationships, impulse response function analysis with confidence bands to trace shock propagation, forecast error variance decomposition to quantify variable importance, stability diagnostics via companion matrix eigenvalues, multivariate forecasting with prediction intervals, and rigorous out-of-sample validation using rolling windows. The example illustrates bidirectional causality, persistent shock effects, and the superiority of VAR over univariate models for correlated economic indicators.

# ============================================================================
# VAR MODELS: Macroeconomic Forecasting (GDP, Inflation, Interest Rate)
# ============================================================================
# Demonstrates: VAR estimation, Granger causality, IRFs, FEVD, forecasting
# Data: 120 quarters (30 years) of GDP growth, inflation, interest rate
# Model: VAR(p) with p selected via information criteria
# ============================================================================

# Load required packages
library(vars)          # VAR estimation, IRF, FEVD, Granger causality
library(urca)          # Unit root tests (ADF, KPSS)
library(tseries)       # Additional time series tests
library(ggplot2)       # Visualization
library(gridExtra)     # Multiple plots
library(MASS)          # Multivariate normal

set.seed(123)

# ============================================================================
# 1. DATA GENERATION: Three interdependent time series
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("VAR MODELS: Macroeconomic System(GDP, Inflation, Interest Rate)\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Generate 120 quarterly observations (30 years)
n <- 120

# True VAR(2) parameters for data generation
# Variables: [GDP_growth, Inflation, Interest_rate]
K <- 3  # Number of variables
p_true <- 2  # True lag order

# Coefficient matrices (K x K for each lag)
# A1: lag-1 coefficients
A1 <- matrix(c(
  0.5,  0.2, -0.1,   # GDP equation: own lag=0.5, inflation=0.2, rate=-0.1
  0.3,  0.4,  0.1,   # Inflation equation: GDP=0.3, own lag=0.4, rate=0.1
  0.4,  0.5,  0.3    # Interest rate equation: GDP=0.4, inflation=0.5, own=0.3
), nrow=K, ncol=K, byrow=TRUE)

# A2: lag-2 coefficients (smaller effects)
A2 <- matrix(c(
  0.2,  0.1,  0.0,
  0.1,  0.2,  0.0,
  0.1,  0.2,  0.2
), nrow=K, ncol=K, byrow=TRUE)

# Constant term
c_vec <- c(2.0, 2.5, 4.0)  # Mean levels: ~2% GDP growth, ~2.5% inflation, ~4% rate

# Innovation covariance matrix (contemporaneous correlations)
Sigma <- matrix(c(
  1.00, 0.30, 0.20,
  0.30, 0.50, 0.40,
  0.20, 0.40, 0.60
), nrow=K, ncol=K)

# Initialize series
Y <- matrix(0, nrow=n, ncol=K)
colnames(Y) <- c("GDP_growth", "Inflation", "Interest_rate")

# Set initial values
Y[1,] <- c(2.0, 2.5, 4.0)
Y[2,] <- c(2.2, 2.6, 4.1)

# Generate VAR(2) process
for (t in 3:n) {
  epsilon_t <- mvrnorm(n=1, mu=rep(0, K), Sigma=Sigma)
  Y[t,] <- c_vec + A1 %*% Y[t-1,] + A2 %*% Y[t-2,] + epsilon_t
}

# Convert to time series object
Y_ts <- ts(Y, start=c(1994, 1), frequency=4)

cat("Data generated: n =", n, "quarterly observations\n")
cat("Variables: K =", K, "(GDP growth, Inflation, Interest rate)\n")
cat("True model: VAR(2) with interdependencies\n\n")

# Summary statistics
cat("Summary statistics:\n")
cat("-----------------------------------\n")
for (i in 1:K) {
  cat(sprintf("%-15s: Mean=%.3f, SD=%.3f, Range=[%.2f, %.2f]\n",
              colnames(Y)[i], mean(Y[,i]), sd(Y[,i]), min(Y[,i]), max(Y[,i])))
}
cat("\n")

# ============================================================================
# 2. EXPLORATORY DATA ANALYSIS
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("EXPLORATORY ANALYSIS\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Time series plots
par(mfrow=c(3,1), mar=c(3,4,2,1))
for (i in 1:K) {
  plot(Y_ts[,i], main=paste(colnames(Y)[i], "over Time"),
       ylab=colnames(Y)[i], xlab="", col="steelblue", lwd=2)
  abline(h=mean(Y[,i]), col="red", lty=2)
}
par(mfrow=c(1,1))

# Scatterplot matrix (contemporaneous relationships)
cat("Scatterplot matrix(contemporaneous correlations):\n")
pairs(Y, main="Pairwise Relationships",
      col="steelblue", pch=19, cex=0.5)

# Correlation matrix
cat("\nCorrelation matrix:\n")
cor_matrix <- cor(Y)
print(round(cor_matrix, 3))
cat("\n")

# ============================================================================
# 3. STATIONARITY TESTING
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("STATIONARITY TESTS\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Testing each variable for stationarity(required for VAR)\n")
cat("-----------------------------------\n\n")

for (i in 1:K) {
  var_name <- colnames(Y)[i]
  cat("Variable:", var_name, "\n")
  cat("-----------------------------------\n")
  
  # ADF test
  adf_result <- ur.df(Y[,i], type="trend", lags=4, selectlags="AIC")
  adf_summary <- summary(adf_result)
  test_stat <- adf_summary@teststat[1]
  crit_5pct <- adf_summary@cval[1,2]
  
  cat("ADF test(H0: unit root):")
  cat("\n  Test statistic:", round(test_stat, 4))
  cat("\n  5% critical value:", round(crit_5pct, 4))
  if (test_stat < crit_5pct) {
    cat("\n  Conclusion: STATIONARY(reject H0)\n")
  } else {
    cat("\n  Conclusion: NON-STATIONARY(fail to reject H0)\n")
  }
  
  # KPSS test
  kpss_result <- ur.kpss(Y[,i], type="tau", lags="short")
  kpss_summary <- summary(kpss_result)
  kpss_stat <- kpss_summary@teststat
  kpss_crit_5pct <- kpss_summary@cval[2]
  
  cat("\nKPSS test(H0: stationarity):")
  cat("\n  Test statistic:", round(kpss_stat, 4))
  cat("\n  5% critical value:", round(kpss_crit_5pct, 4))
  if (kpss_stat < kpss_crit_5pct) {
    cat("\n  Conclusion: STATIONARY(fail to reject H0)\n")
  } else {
    cat("\n  Conclusion: NON-STATIONARY(reject H0)\n")
  }
  
  cat("\n")
}

cat("All variables appear stationary → proceed with VAR estimation\n")
cat("(If any non-stationary: would need differencing or VECM)\n\n")

# ============================================================================
# 4. LAG ORDER SELECTION
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("LAG ORDER SELECTION\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Test lag orders from 1 to 8
lag_select <- VARselect(Y_ts, lag.max=8, type="const")

cat("Information criteria for different lag orders:\n")
cat("-----------------------------------\n")
print(lag_select$selection)
cat("\n")

cat("Criteria table:\n")
print(lag_select$criteria)
cat("\n")

# Extract recommended lags
aic_lag <- lag_select$selection["AIC(n)"]
bic_lag <- lag_select$selection["SC(n)"]  # SC = Schwarz (BIC)
hq_lag <- lag_select$selection["HQ(n)"]

cat("Recommended lag orders:\n")
cat("  AIC:", aic_lag, "(tends to select larger models)\n")
cat("  BIC:", bic_lag, "(penalizes complexity more, parsimonious)\n")
cat("  HQ:", hq_lag, "(intermediate)\n\n")

# Use BIC recommendation (most parsimonious)
p_selected <- bic_lag
cat("Selected lag order: p =", p_selected, "(using BIC)\n\n")

# ============================================================================
# 5. VAR MODEL ESTIMATION
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("VAR MODEL ESTIMATION\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Estimate VAR(p) with selected lag order
var_model <- VAR(Y_ts, p=p_selected, type="const")

cat("VAR(", p_selected, ") Model Summary:\n", sep="")
cat("-----------------------------------\n")
print(summary(var_model))

# Extract key information
cat("\n\nModel diagnostics:\n")
cat("  Number of observations:", nobs(var_model), "\n")
cat("  Number of parameters per equation:", K*p_selected + 1, "\n")
cat("  Total parameters:", K*(K*p_selected + 1), "\n")
cat("  Degrees of freedom:", nobs(var_model) - K*p_selected - 1, "\n\n")

# Log-likelihood and information criteria
cat("Information criteria:\n")
cat("  AIC:", round(AIC(var_model), 2), "\n")
cat("  BIC:", round(BIC(var_model), 2), "\n\n")

# ============================================================================
# 6. RESIDUAL DIAGNOSTICS
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("RESIDUAL DIAGNOSTICS\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Portmanteau test for autocorrelation
cat("Portmanteau test(multivariate Ljung-Box):\n")
cat("H0: No autocorrelation up to lag h\n")
cat("-----------------------------------\n")

portmanteau <- serial.test(var_model, lags.pt=16, type="PT.asymptotic")
print(portmanteau)

if (portmanteau$serial$p.value > 0.05) {
  cat("\nConclusion: Residuals are WHITE NOISE(no autocorrelation) - GOOD\n")
cat("p-value =", round(portmanteau$serial$p.value, 4), "> 0.05\n\n")
} else {
  cat("\nConclusion: Residuals show autocorrelation - consider increasing lag order\n")
  cat("p-value =", round(portmanteau$serial$p.value, 4), "< 0.05\n\n")
}

# Test for multivariate normality
cat("\nMultivariate normality test(Jarque-Bera):\n")
cat("H0: Residuals are multivariate normal\n")
cat("-----------------------------------\n")

normality <- normality.test(var_model, multivariate.only=TRUE)
print(normality)

if (normality$jb.mul$JB$p.value > 0.05) {
  cat("\nConclusion: Residuals are approximately NORMAL - GOOD\n")
  cat("p-value =", round(normality$jb.mul$JB$p.value, 4), "> 0.05\n\n")
} else {
  cat("\nConclusion: Residuals deviate from normality\n")
  cat("Impact: Point forecasts still valid; use bootstrap for IRF confidence bands\n")
  cat("p-value =", round(normality$jb.mul$JB$p.value, 4), "< 0.05\n\n")
}

# Plot residuals
cat("\nGenerating residual plots...\n")
par(mfrow=c(3,2), mar=c(4,4,2,1))
resids <- residuals(var_model)

for (i in 1:K) {
  # Residuals over time
  plot(resids[,i], type="l", main=paste(colnames(Y)[i], "- Residuals"),
       ylab="Residual", xlab="Time", col="darkblue")
  abline(h=0, col="red", lty=2)
  
  # Q-Q plot
  qqnorm(resids[,i], main=paste(colnames(Y)[i], "- Q-Q Plot"), pch=19, cex=0.5)
  qqline(resids[,i], col="red", lwd=2)
}
par(mfrow=c(1,1))

cat("\n")

# ============================================================================
# 7. STABILITY CHECK
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("STABILITY CHECK\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Checking VAR stability via companion matrix eigenvalues\n")
cat("Stability requires all eigenvalues inside unit circle(modulus < 1)\n")
cat("-----------------------------------\n\n")

# Stability check
stability <- stability(var_model, type="OLS-CUSUM")

# Get roots (inverse of eigenvalues)
roots_vals <- roots(var_model)

cat("Inverse roots of characteristic polynomial:\n")
print(round(roots_vals, 4))

if (all(abs(roots_vals) < 1)) {
  cat("\nConclusion: VAR is STABLE(all roots inside unit circle)\n")
  cat("Maximum root modulus:", round(max(abs(roots_vals)), 4), "< 1\n\n")
} else {
  cat("\nWARNING: VAR is UNSTABLE(some roots outside unit circle)\n")
  cat("Maximum root modulus:", round(max(abs(roots_vals)), 4), ">= 1\n\n")
}

# Plot roots
plot(var_model, names="GDP_growth")

# ============================================================================
# 8. GRANGER CAUSALITY TESTS
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("GRANGER CAUSALITY TESTS\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Testing whether variable X Granger-causes variable Y\n")
cat("H0: X does not Granger-cause Y(lags of X don't help predict Y)\n")
cat("Ha: X Granger-causes Y(lags of X improve prediction of Y)\n")
cat("-----------------------------------\n\n")

# Test all pairwise Granger causalities
causality_results <- list()
var_names <- colnames(Y)

for (i in 1:K) {
  for (j in 1:K) {
    if (i != j) {
      cause_var <- var_names[j]
      effect_var <- var_names[i]
      
      # Granger causality test
      gc_test <- causality(var_model, cause=cause_var)
      
      cat("Does", cause_var, "Granger-cause", effect_var, "?\n")
      cat("  F-statistic:", round(gc_test$Granger$statistic, 4), "\n")
      cat("  p-value:", round(gc_test$Granger$p.value, 4), "\n")
      
      if (gc_test$Granger$p.value < 0.05) {
        cat("  Conclusion: YES - Reject H0(Granger causality detected)\n")
        causality_results[[paste(cause_var, "->", effect_var)]] <- "YES"
      } else {
        cat("  Conclusion: NO - Fail to reject H0(no Granger causality)\n")
        causality_results[[paste(cause_var, "->", effect_var)]] <- "NO"
      }
      cat("\n")
    }
  }
}

cat("\nGranger causality summary:\n")
cat("-----------------------------------\n")
for (name in names(causality_results)) {
  cat(sprintf("  %-35s: %s\n", name, causality_results[[name]]))
}
cat("\n")

# ============================================================================
# 9. IMPULSE RESPONSE FUNCTIONS (IRFs)
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("IMPULSE RESPONSE FUNCTIONS\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Computing IRFs: dynamic effect of one-SD shock on all variables\n")
cat("Using orthogonalized IRF(Cholesky decomposition)\n")
cat("Ordering: GDP_growth, Inflation, Interest_rate\n")
cat("95% confidence bands via bootstrap(100 runs)\n")
cat("-----------------------------------\n\n")

# Compute orthogonalized IRFs with bootstrap CI
irf_result <- irf(var_model, impulse=var_names, response=var_names,
                  n.ahead=20, ortho=TRUE, boot=TRUE, runs=100, ci=0.95)

cat("IRF computed for 20 periods ahead\n\n")

# Plot IRFs
cat("Generating IRF plots...\n")
plot(irf_result, main="Impulse Response Functions(Orthogonalized)")

# Detailed interpretation example
cat("\nExample interpretation:\n")
cat("  - GDP shock → Inflation: Shows how inflation responds over time to GDP surprise\n")
cat("  - Inflation shock → Interest_rate: Central bank response to inflation\n")
cat("  - Interest_rate shock → GDP: Monetary policy transmission mechanism\n")
cat("  - Persistence: How long effects last(return to baseline)\n")
cat("  - Confidence bands: Statistical uncertainty around IRF path\n\n")

# ============================================================================
# 10. FORECAST ERROR VARIANCE DECOMPOSITION (FEVD)
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("FORECAST ERROR VARIANCE DECOMPOSITION\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Decomposing forecast error variance by shock source\n")
cat("Shows relative importance of each variable's shocks\n")
cat("-----------------------------------\n\n")

# Compute FEVD
fevd_result <- fevd(var_model, n.ahead=20)

cat("FEVD Summary:\n\n")
print(fevd_result)

# Plot FEVD
cat("\nGenerating FEVD plots...\n")
plot(fevd_result, main="Forecast Error Variance Decomposition")

cat("\nInterpretation:\n")
cat("  - Each panel shows variance decomposition for one variable\n")
cat("  - Colors represent shocks from different variables\n")
cat("  - Sum to 100% at each horizon\n")
cat("  - Short horizon: own shocks dominant(diagonal elements)\n")
cat("  - Long horizon: cross-variable effects emerge(spillovers)\n\n")

# ============================================================================
# 11. FORECASTING
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("FORECASTING\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Forecast next 12 quarters (3 years)
h_forecast <- 12
cat("Forecasting horizon: h =", h_forecast, "quarters(3 years)\n\n")

# Generate forecasts
forecast_result <- predict(var_model, n.ahead=h_forecast, ci=0.95)

cat("Forecast summary(first 8 quarters):\n")
cat("-----------------------------------\n\n")

for (i in 1:K) {
  cat("Variable:", var_names[i], "\n")
  forecast_df <- data.frame(
    Forecast = forecast_result$fcst[[i]][1:8, "fcst"],
    Lower_95 = forecast_result$fcst[[i]][1:8, "lower"],
    Upper_95 = forecast_result$fcst[[i]][1:8, "upper"]
  )
  print(round(forecast_df, 3))
  cat("\n")
}

# Plot forecasts
cat("Generating forecast plots...\n")
par(mfrow=c(3,1), mar=c(4,4,3,1))

for (i in 1:K) {
  # Observed data
  plot(Y_ts[,i], xlim=c(start(Y_ts)[1], end(Y_ts)[1] + h_forecast/4),
       ylim=range(c(Y_ts[,i], forecast_result$fcst[[i]][,"fcst"],
                    forecast_result$fcst[[i]][,"lower"],
                    forecast_result$fcst[[i]][,"upper"])),
       main=paste(var_names[i], "- Forecast"),
       ylab=var_names[i], xlab="Time", col="steelblue", lwd=2)
  
  # Forecast path
  forecast_time <- seq(end(Y_ts)[1] + 0.25, by=0.25, length.out=h_forecast)
  lines(ts(forecast_result$fcst[[i]][,"fcst"],
           start=forecast_time[1], frequency=4),
        col="darkred", lwd=2, lty=2)
  
  # Confidence interval
  polygon(c(forecast_time, rev(forecast_time)),
          c(forecast_result$fcst[[i]][,"lower"], rev(forecast_result$fcst[[i]][,"upper"])),
          col=rgb(1,0,0,0.2), border=NA)
  
  legend("topleft", legend=c("Observed", "Forecast", "95% CI"),
         col=c("steelblue", "darkred", rgb(1,0,0,0.2)),
         lwd=c(2,2,10), lty=c(1,2,1), bty="n")
}
par(mfrow=c(1,1))

cat("\nNote: Prediction intervals widen with horizon(increasing uncertainty)\n\n")

# ============================================================================
# 12. OUT-OF-SAMPLE VALIDATION
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("OUT-OF-SAMPLE VALIDATION\n")
cat("=", rep("=", 78), "\n\n", sep="")

# Rolling window forecast evaluation
train_size <- 100
test_size <- n - train_size

cat("Rolling window validation:\n")
cat("  Training size:", train_size, "observations\n")
cat("  Test size:", test_size, "observations\n")
cat("  Forecast horizon: 1-step-ahead\n")
cat("-----------------------------------\n\n")

# Initialize forecast storage
forecasts_oos <- matrix(NA, nrow=test_size, ncol=K)
actuals_oos <- matrix(NA, nrow=test_size, ncol=K)

# Rolling forecasts
for (i in 1:test_size) {
  # Training data
  train_end <- train_size + i - 1
  Y_train <- Y_ts[1:train_end, ]
  
  # Fit VAR on training data
  var_train <- VAR(Y_train, p=p_selected, type="const")
  
  # 1-step-ahead forecast
  forecast_1step <- predict(var_train, n.ahead=1)
  
  # Store forecasts and actuals
  for (j in 1:K) {
    forecasts_oos[i, j] <- forecast_1step$fcst[[j]][1, "fcst"]
    actuals_oos[i, j] <- Y_ts[train_end + 1, j]
  }
}

# Calculate forecast errors
errors_oos <- actuals_oos - forecasts_oos

# Compute accuracy metrics
cat("Forecast accuracy metrics(1-step-ahead):\n")
cat("-----------------------------------\n")

for (i in 1:K) {
  rmse <- sqrt(mean(errors_oos[, i]^2))
  mae <- mean(abs(errors_oos[, i]))
  mape <- mean(abs(errors_oos[, i] / actuals_oos[, i])) * 100
  
  cat(sprintf("\n%s:\n", var_names[i]))
  cat(sprintf("  RMSE: %.4f\n", rmse))
  cat(sprintf("  MAE:  %.4f\n", mae))
  cat(sprintf("  MAPE: %.2f%%\n", mape))
}

cat("\n")

# Plot forecast errors
par(mfrow=c(3,1), mar=c(4,4,3,1))
for (i in 1:K) {
  plot(errors_oos[,i], type="l", main=paste(var_names[i], "- Forecast Errors"),
       ylab="Error", xlab="Forecast Period", col="darkgreen", lwd=1.5)
  abline(h=0, col="red", lty=2, lwd=2)
  abline(h=c(-2*sd(errors_oos[,i]), 2*sd(errors_oos[,i])),
         col="orange", lty=2)
}
par(mfrow=c(1,1))

cat("\n")

# ============================================================================
# 13. FINAL SUMMARY
# ============================================================================

cat("=", rep("=", 78), "\n", sep="")
cat("FINAL SUMMARY\n")
cat("=", rep("=", 78), "\n\n", sep="")

cat("Data: n =", n, "quarterly observations\n")
cat("Variables: K =", K, "(GDP growth, Inflation, Interest rate)\n")
cat("Final model: VAR(", p_selected, ")\n\n", sep="")

cat("Model Diagnostics:\n")
cat("-----------------------------------\n")
cat("  Portmanteau test p-value:", round(portmanteau$serial$p.value, 4))
if (portmanteau$serial$p.value > 0.05) cat(" ✓ (white noise residuals)")
cat("\n")
cat("  Normality test p-value:", round(normality$jb.mul$JB$p.value, 4))
if (normality$jb.mul$JB$p.value > 0.05) cat(" ✓ (normal residuals)")
cat("\n")
cat("  Stability: All eigenvalues < 1:", all(abs(roots_vals) < 1))
if (all(abs(roots_vals) < 1)) cat(" ✓ (stable system)")
cat("\n\n")

cat("Granger Causality:\n")
cat("-----------------------------------\n")
for (name in names(causality_results)) {
  cat(sprintf("  %-35s: %s\n", name, causality_results[[name]]))
}
cat("\n")

cat("Key Findings:\n")
cat("-----------------------------------\n")
cat("  ✓ All variables stationary(no unit roots)\n")
cat("  ✓ VAR captures dynamic interdependencies\n")
cat("  ✓ Granger causality tests reveal predictive relationships\n")
cat("  ✓ IRFs show shock propagation through system\n")
cat("  ✓ FEVD quantifies relative importance of shocks\n")
cat("  ✓ Out-of-sample validation confirms forecast accuracy\n")
cat("  ✓ Multivariate approach superior to univariate models\n\n")

cat("=", rep("=", 78), "\n", sep="")
cat("VAR ANALYSIS COMPLETE\n")
cat("=", rep("=", 78), "\n\n", sep="")

# ============================================================================
# END OF VAR EXAMPLE
# ============================================================================
05Tactical Pivots

Alternatives

Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.

When the path is blocked, pivot. Rigor is not rigidity; it is the intelligent adaptation to reality.
Adaptive Strategy
Measurement Precision Ladder Ideal · Multivariate Continuous Grid
Ratio Vector
Maintain VAR logic. Optimal for auditing the dynamic feedback between high-fidelity continuous series.
Peak Signal
Interval Vector
Ideal for Primary Systems. Ensure no extreme lag-specific multicollinearity chokes the matrix.
Standard Profile
Nominal Vector
Abandon VAR. Use Multivariate Markov-Chain models to audit categorical state-synergy.
Information Suicide
Temporal Trajectory Audit Dynamic System Flow
Multivariate Pulse
Cross-variable synergy.
Stay with VAR. Capture the 'Ripple Effect' of shocks across the system.
Integrated Flux
Co-moving series.
Pivot to VECM (Vector Error Correction Model) to model the 'Long-Term Anchor' between series.
Adaptive Technical Safeguards · adaptive safeguards
matrix instability
  • Lasso-VAR — Apply L1 penalties to select only the most robust cross-variable lags.
  • Bayesian VAR — Use informative priors to protect the system from exploding coefficients.
volatility spillover
  • MGARCH Strike — Model the time-varying covariance matrix to audit risk-bleed across the vector.
structural breaks
  • Threshold VAR (TVAR) — Switch models if the system pulse changes after a critical clinical event.
06Adjusted Comparisons

Post-hoc

Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.

The omnibus test opens the door; post-hoc analysis explores the room.
Forensic Detail
Adjusted Comparisons
  • Vary lag order using AIC/BIC/HQ criteria
  • Compare recursive vs structural identification
  • Assess stability (eigenvalues inside unit circle)
  • Bootstrap confidence intervals for IRF
  • Test for cointegration if variables are I(1)
Interpretation Guidelines

VAR models multivariate time series. Post-hoc involves impulse responses and variance decomposition.

07Standardized scale impact

Effect Size

Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.

Significance is noise. Magnitude is the signal. Measure the impact, not just the probability.
Impact Magnitude
N/A
Recommended Measure
08Statistical Power

Sample Size

Guidelines for minimum sample requirements and power analysis parameters.

An underpowered study is an ethical failure. Respect the data by collecting enough of it.
Power Protocol
Floor Requirements

The 'Matrix Buffer' Mandate: A minimum of 100-200 timepoints is required. VAR models are parameter-heavy; a system with 3 variables and 4 lags requires estimating 39+ coefficients, demanding massive temporal depth.

Effect SizeParametersRequired n
Small EffectLow Coupling (f²=.02)n ≈ 1000
Medium EffectModerate Coupling (f²=.15)n ≈ 250
Large EffectStrong Coupling (f²=.35)n ≈ 100
Key considerations

The 'Stability Strike': If your VAR system is near the 'Unit Root' boundary (non-stationary), the power math becomes invalid. Always verify stationarity for every series in the vector before trusting the N.

G*Power StrategyBenchmark: Multivariate Time Series (VAR). Parameters: Number of series (K), Lags (p), Systemic R², α = .05, Power = .80. Note: N must be significantly larger than (K²p + K) to allow for stable matrix inversion.
09APA narrative blueprint

Reporting

How to compile statistical results into publication prose matching APA and journal style guides.

Data does not speak for itself. It requires a translator. Be clear, be precise, be honest.
Narrative Arc
10Exhibit Builder

Manuscript Lab

Copy standard summary tables and forensic reporting grids to outline analysis details.

Table 1: Vector Autoregression (VAR) for Macroeconomic System
EquationPredictorEstimateSEtp
Equation: GDPGDP.L10.850.0810.6< .001
Equation: GDPInterest.L1-0.120.04-3.0.003
Equation: InterestGDP.L10.450.153.0.003
Equation: InterestInterest.L10.620.106.2< .001
Note. Lag Order p = 2 (selected via AIC). N = 120 periods.
Interest.L1 → GDP (p=.003)Confirms 'Systemic Drag'. Rising interest rates in the previous quarter significantly predict a drop in current GDP growth.
Header glossary

The Immediate Past. Represents the value of the variable from the previous time period.

The 'Ecosystem' Logic. In VAR, every variable gets its own regression equation, treating it as a dependent variable predicted by everyone else.

11Algorithmic Logic

Command Center

Syntax libraries and function parameters for executing calculations in stats packages.

Code is the modern laboratory. Clean execution ensures reproducible discovery.
Execution Engine
# 1. Select Optimal Lag
lag_select <- vars::VARselect(df_combined, lag.max = 10)

# 2. Fit VAR Model
model <- vars::VAR(df_combined, p = lag_select$selection[1])
summary(model)

# 3. Forecast System Dynamics
plot(predict(model, n.ahead = 12))
Library stack
R
varsggplot2
Python
statsmodels.tsa.api
Elite Forensic Strike

The VAR model is a 'Black Box' until you run Impulse Response Functions (IRF). Use IRF to visualize how a single 'shock' to one variable (e.g., Oil Price hike) ripples through the other variables over the next 12 months.

# Execute Impulse Response Analysis
plot(vars::irf(model, impulse = 'x', response = 'y'))
12The Over-adjustment Trap

Common Mistakes

Analytical caveats and corrections to maintain modeling integrity.

Wisdom is learning from the failures of others. Anticipate the error before it occurs.
Defensive Logic
Why it's wrong
VAR requires all variables stationary. Non-stationary variables lead to spurious regression, invalid inference, meaningless Granger causality tests, and unreliable forecasts. Mixing stationary and non-stationary variables creates inconsistent estimates. IRFs and FEVD become uninterpretable
The correction
ALWAYS test each variable individually: ADF test (H₀=unit root), KPSS test (H₀=stationarity). Both should indicate stationarity (ADF rejects, KPSS doesn't reject). If any variable non-stationary: (1) Difference until stationary, OR (2) Test for cointegration using Johansen test. If cointegrated: use VECM (Vector Error Correction Model) instead of VAR. VECM preserves long-run equilibrium relationships. If not cointegrated: difference all variables, estimate VAR in differences. Report stationarity test results
Why it's wrong
VAR parameters grow as K²p where K=variables, p=lags. Example: K=5, p=4 → 5²×4=100 coefficients per equation, 500 total parameters. With limited data: overfitting, unstable estimates, poor out-of-sample forecasts, degrees of freedom exhausted, multicollinearity among regressors. Estimation becomes unreliable or impossible
The correction
Balance model richness with sample size. Rules of thumb: (1) n ≥ 3×K²p (preferably 5-10×). (2) Keep K≤6 for typical quarterly data (30 years), K≤4 for monthly. (3) Use BIC for lag selection (penalizes complexity). (4) For large systems: use Bayesian VAR with Minnesota prior (shrinks coefficients toward zero/random walk), Factor-Augmented VAR (FAVAR - extract factors from many variables), or restricted VAR (impose zero constraints based on theory). (5) Validate on holdout: if in-sample fit good but out-of-sample poor, over-parameterized. Reduce K or p
Why it's wrong
VAR assumes constant parameters over time. Structural breaks (policy regime changes, crises, technological shifts) invalidate this. Using pre-break data to forecast post-break period gives poor forecasts. Parameter estimates are weighted average of different regimes, meaningful in neither. Granger causality and IRFs change across regimes but model assumes constant
The correction
Test for breaks: Chow test at suspected dates (2008 crisis, policy changes), CUSUM/CUSUM-SQ tests, recursive estimation with coefficient plots. If break detected: (1) Estimate separate VARs pre/post break, use only post-break for forecasting. (2) Include dummy variables for level shifts or regime changes. (3) Time-Varying Parameter VAR (Bayesian) allows smooth parameter evolution. (4) Markov-Switching VAR for discrete regime changes with probabilistic transitions. (5) Acknowledge limitation: historical dynamics may differ from future. Report forecast uncertainty
Why it's wrong
If residuals autocorrelated, model hasn't captured all dynamics. Granger causality tests invalid (omitted dynamics bias tests). Forecasts suboptimal (predictable patterns remain). Standard errors wrong (underestimated), confidence intervals too narrow. IRF confidence bands invalid. Model fundamentally mis-specified
The correction
ALWAYS run Portmanteau test (multivariate Ljung-Box) on residuals. Test at multiple lags (h=p+4, p+8, etc.). H₀=no autocorrelation, p>0.05 required. If fails: (1) Increase lag order p systematically until test passes. (2) Check if important variable omitted from system. (3) Look for structural breaks creating apparent autocorrelation. (4) Examine outliers (can create spurious autocorrelation). Plot residual ACF for each equation. Never proceed with autocorrelated residuals. Consider state-space formulation if classical VAR fails
Why it's wrong
Granger causality only tests predictive priority (does past X help predict Y?). Does NOT establish: (1) Structural causation (X causes Y), (2) Direction of true causation (could be Y→X with lags), (3) Common factor driving both, (4) Contemporaneous causation (same-period effects). Misleading policy conclusions if interpreted as structural causation
The correction
Granger causality interpretation: 'X Granger-causes Y' means past X contains useful information for predicting Y beyond Y's own history. Report as 'predictive relationship' not 'causal effect'. For structural causation: need external identification (theory-based restrictions, instrumental variables, timing assumptions). Use Structural VAR (SVAR) with identifying restrictions based on economic theory. Or panel VAR with cross-sectional variation. Acknowledge: temporal precedence ≠ causation. Granger causality useful for forecasting, limited for policy inference
Why it's wrong
If K variables non-stationary but cointegrated (share long-run equilibrium relationship), differencing all variables discards information about equilibrium. VAR in differences omits error-correction mechanism. Forecasts ignore mean-reversion to equilibrium. Long-run forecasts diverge from equilibrium path. Efficiency loss (worse forecasts than VECM)
The correction
Test for cointegration using Johansen test on non-stationary variables. If cointegration rank r>0: use Vector Error Correction Model (VECM) instead of VAR. VECM = VAR with error-correction terms preserving long-run relationships: Δy_t = ΠΠy_{t-1} + Γ₁Δy_{t-1} + ... + ε_t, where Π has rank r (number of cointegrating vectors). VECM nests VAR (if r=0, reduces to VAR in differences). Better forecasts for cointegrated systems. Example: exchange rates, prices at different locations, interest rates of different maturities often cointegrated
Why it's wrong
Point estimates of IRFs show dynamic responses but have substantial uncertainty. Without confidence bands: can't assess statistical significance of responses. May conclude shock has persistent effect when actually insignificant. Policy decisions based on uncertain IRF paths risky. Uncertainty often large, especially at longer horizons and for cross-variable effects
The correction
ALWAYS report IRF confidence intervals: (1) Bootstrap: non-parametric (resample residuals) or parametric (resample from estimated distribution). 500-1000 bootstrap replications. (2) Asymptotic approximation (faster but less reliable with small samples). (3) Bayesian credible intervals (if using Bayesian VAR). Plot IRFs with shaded 68% and 95% bands. Check if zero line within bands → effect not significant. Uncertainty typically increases with horizon. Report findings: 'IRF suggests positive effect but large uncertainty' rather than treating point estimate as truth
Why it's wrong
Orthogonalized IRF uses Cholesky decomposition to orthogonalize contemporaneous shocks. Ordering matters: variables earlier in ordering assumed to affect later variables contemporaneously but not vice versa. Different orderings give different IRFs. Arbitrary ordering produces arbitrary results. Policy-relevant IRF conclusions may reverse with different ordering
The correction
Two approaches: (1) Theory-based ordering: order variables by timing of decision/reaction. Example in macro: output (slow-moving) before interest rates (fast-moving policy variable). Production before prices. (2) Generalized IRF: ordering-free, allows for contemporaneous correlation. No orthogonalization, shocks not independent but more robust to ordering. Report which approach used and why. If using Cholesky: justify ordering via economic theory, test sensitivity to alternative orderings. If IRF conclusions change dramatically with ordering, generalized IRF or structural VAR (SVAR) with explicit identifying restrictions preferred
Why it's wrong
In-sample fit can be excellent due to overfitting, but out-of-sample performance poor. Model may capture historical patterns that don't persist. True test of forecasting model is performance on unseen data. Reporting only in-sample metrics (R², information criteria) misleading for forecast evaluation. VAR can overfit with many parameters
The correction
ALWAYS validate on holdout data: (1) Fixed split: reserve last 10-20% for testing. Fit on training, forecast test period, compute RMSE/MAE/MAPE. (2) Rolling window: fit on moving window, 1-step-ahead forecasts throughout sample. More observations for evaluation. (3) Expanding window: growing training set. (4) Compare VAR to benchmarks: random walk, univariate ARIMA, simple averages. VAR should beat univariate to justify complexity. (5) Multi-horizon evaluation: accuracy at h=1, 4, 8 quarters. Report all metrics. If out-of-sample poor despite good in-sample: reduce model complexity, use shrinkage, or simpler benchmark
Why it's wrong
Standard VAR treats all variables as endogenous (mutually determined). If some variables truly exogenous (e.g., oil prices for small economy, foreign variables), standard VAR imposes unnecessary restrictions. Modeling exogenous variable as function of domestic variables wastes degrees of freedom and can bias estimates of domestic dynamics
The correction
If K_exog variables exogenous: use VAR with exogenous variables (VARX). Only K_endog endogenous variables appear on LHS. Exogenous variables appear on RHS only. Example: domestic VAR with foreign variables as exogenous regressors. Reduces parameters: K_endog²p instead of (K_endog+K_exog)²p. Test exogeneity: if variable truly exogenous, its equation should have poor fit and no Granger causality from domestic variables. Block exogeneity tests: test if all domestic variables can be excluded from exogenous variable equations. Software: include exogenous variables separately in estimation
Why it's wrong
VAR typically includes constant (intercept). If data has deterministic trend but no trend in VAR, model mis-specified. If strong seasonality but not modeled, residuals show seasonal autocorrelation. Forecasts miss deterministic components. Parameter estimates absorb deterministic patterns, reducing efficiency
The correction
Specify deterministic components appropriately: (1) Constant ('const'): most common, allows non-zero means. (2) Trend ('trend'): linear time trend if data trending even after stationarity transformation. (3) Both ('const+trend'): for trending levels. (4) None ('none'): only if data truly mean-zero (unusual). For seasonality: (1) Seasonal dummies if strong seasonal pattern. (2) Seasonal differencing before VAR (SARIMA-type). (3) Seasonal adjustment pre-processing (X-13ARIMA-SEATS). Test specification: include trend, test if coefficient significant. Information criteria can help select deterministic specification
Why it's wrong
VAR requires substantial data: K²p+K parameters per system (K per equation). Small sample relative to parameters: unreliable estimates, overfitting, unstable IRFs, poor forecasts, degrees of freedom issues. Asymptotic theory (used for inference) invalid with small samples. Example: K=4, p=4 needs n≥200 for reliable estimation (4²×4×3=192 minimum)
The correction
Check sample adequacy: n ≥ 3×K²p minimum, preferably 5-10×. Monthly data: 20+ years for K=3. Quarterly: 30+ years for K=3. If sample small: (1) Reduce K: include only most important variables. (2) Reduce p: use BIC, accept lower lag order. (3) Bayesian VAR with shrinkage: Minnesota prior pulls coefficients toward parsimonious values (random walk, zero cross-effects), allows estimation with limited data. Hyperparameters control shrinkage strength. (4) Restricted VAR: economic theory suggests some coefficients zero → impose restrictions. (5) Panel VAR if multiple cross-sections available. (6) Acknowledge limitation: report wide confidence intervals
13Academic Lineage

References

Scholarly lineage and citation keys grounding the statistical framework.

We stand on the shoulders of giants. Honor the source of the method.
Academic Lineage
[1]
[2]
[3]
[4]
[5]
[6]
In a system, no variable is an island. Use VAR to see the ripples before they become waves, for the truth of the system lies in the interaction, not the isolated pulse.
The Interpretive Rigor Directive
statminds · VARMind reference · v2.2 · updated 2026-01-1715 of 15 sections