Skip to content

shawcharles/srvar-toolkit

Repository files navigation

License Python Code style: black Ruff


srvar-toolkit

Shadow-rate VAR toolkit for Bayesian macroeconomic forecasting in pure Python.
Explore the docs »

Quick Start · Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Features
  3. Getting Started
  4. Usage
  5. Roadmap
  6. Contributing
  7. License
  8. Contact
  9. Citing
  10. Acknowledgments

About The Project


srvar-toolkit is a lightweight, testable implementation of Shadow-Rate Vector Autoregression (SRVAR) models for macroeconomic forecasting. It provides a complete Bayesian workflow for fitting VARs with:

  • Effective Lower Bound (ELB) constraints — Model interest rates that are censored at the zero lower bound
  • Stochastic volatility — Capture time-varying uncertainty in economic variables
  • Legacy Minnesota-style NIW shrinkage — Structured shrinkage on the historical compatibility path
  • Variable selection (SSVS) — Identify which predictors matter most

The toolkit is designed for researchers and practitioners who need transparent, reproducible Bayesian VAR estimation without the overhead of large econometric frameworks.

(back to top)

Built With

Python NumPy SciPy Pandas


Shadow Rate Example Forecast Fan Chart

Features

Component Description How to Enable Status
Conjugate BVAR (NIW) Closed-form posterior updates and fast sampling for VAR coefficients/covariance PriorSpec.niw_default(...) Supported
Legacy Minnesota-style NIW shrinkage Historical Minnesota-style NIW construction (non-canonical; compatibility path) PriorSpec.niw_minnesota_legacy(...) Supported
Canonical Minnesota shrinkage Equation-wise Minnesota own-vs-cross shrinkage for homoskedastic and diagonal SV models PriorSpec.niw_minnesota_canonical(...) or prior.method: "minnesota_canonical" Supported (homo + diagonal SV)
Tempered Minnesota bridge Experimental geometric bridge between legacy and canonical Minnesota scaling PriorSpec.niw_minnesota_tempered(...) or prior.method: "minnesota_tempered" Experimental (diagonal SV only)
Variable Selection (SSVS) Spike-and-slab inclusion indicators for stochastic search PriorSpec.from_ssvs(...) Supported
Bayesian LASSO (BLASSO) Bayesian LASSO shrinkage prior for VAR coefficients (global or adaptive) PriorSpec.from_blasso(...) Supported
Shadow-Rate / ELB Latent shadow-rate sampling at the effective lower bound ModelSpec(elb=ElbSpec(...)) Supported
Stochastic Volatility Diagonal SV with RW/AR(1) state dynamics; optional triangular covariance (time-invariant correlations); optional factor SV (full time-varying covariance; v1: NIW+RW) ModelSpec(volatility=VolatilitySpec(...)) Supported
Combined ELB + SV Joint shadow-rate and stochastic volatility model ModelSpec(elb=..., volatility=...) Supported
Robust Shocks Student‑t and outlier-mixture innovations (homoskedastic VARs; factor SV supported) ModelSpec(shocks=ShockSpec(...)) Supported
Steady-State VAR (SSP) Parameterize the VAR intercept via a steady-state mean mu (optional mu-SSVS) ModelSpec(steady_state=SteadyStateSpec(...)) Supported
Forecasting Posterior predictive simulation with quantiles srvar.api.forecast(...) Supported
Conditional Forecasting Scenario forecasts with hard constraints (linear Gaussian VARs) srvar.scenario.conditional_forecast(...) Supported
Stationarity Conditioning Optional rejection of unstable VAR draws during forecasting/backtesting forecast(..., stationarity="reject") Supported
Structural IRFs (Cholesky) Cholesky-identified impulse responses from posterior draws srvar.analysis.irf_cholesky(...) Supported
Sign-Restricted IRFs Structural IRFs via sign-restriction rotations srvar.analysis.irf_sign_restricted(...) Supported
FEVD (Cholesky) Forecast error variance decomposition from Cholesky-identified IRFs srvar.analysis.fevd_cholesky(...) Supported
Historical Decompositions (Cholesky) Decompose historical movements into structural shock contributions srvar.analysis.historical_decomposition_cholesky(...) Supported
Plotting Shadow rate, volatility, and fan chart visualisations srvar.plotting Supported
Backtesting Rolling/expanding refit + forecast with plots + metrics; optional streaming metrics mode srvar backtest config.yml Supported
ELB-Censored Evaluation Floor realized values and (optionally) forecast draws at an ELB to match interest-rate scoring conventions evaluation.elb_censor (backtest) Supported
WIS (Weighted Interval Score) Draw-based WIS for probabilistic forecast evaluation evaluation.wis (backtest) Supported
Pinball Loss (Quantile Score) Draw-based pinball (quantile) loss for probabilistic forecast evaluation evaluation.pinball (backtest) Supported
Log Score (Gaussian LPD) Gaussian approximation log score from predictive draws evaluation.log_score (backtest) Supported
Model Comparison (DM test) Diebold–Mariano test with Newey–West/HAC variance for comparing loss series srvar.stats.diebold_mariano_test(...) Supported
Model Comparison (GW test) Giacomini–White conditional predictive ability test (MATLAB-compatible NW covariance) srvar.stats.giacomini_white_test(...) Supported
Forecast Pooling (Ensembles) Combine multiple predictive distributions via weighted mixtures srvar.ensemble.pool_forecasts(...) Supported
Labeled Outputs (xarray) Convert fit/forecast/IRF/FEVD outputs to labeled xarray.Dataset objects srvar.xarray.*_to_xarray(...) Supported
Posterior Diagnostics (ArviZ) Convert outputs to arviz.InferenceData for diagnostics/plotting srvar.arviz.*_to_inferencedata(...) Supported
Replication Harness Starter configs + scripts for Carriero et al. (2025) baselines papers/carriero2025forecasting/ Supported

(back to top)


Glossary (acronyms)

  • ELB: effective lower bound (censoring constraint applied to selected observed series)
  • NIW: Normal-Inverse-Wishart prior (conjugate Bayesian VAR)
  • SSVS: stochastic search variable selection (spike-and-slab variable selection)
  • SVRW: stochastic volatility random walk (diagonal log-variance random-walk model)
  • KSC: Kim-Shephard-Chib mixture approximation for log-(\chi^2)
  • SSP: steady-state parameterization (replace intercept with long-run mean mu)

Getting Started

Prerequisites

  • Python 3.11 or higher
  • pip package manager

Reproducible local environment (recommended)

If you're concerned about cross-platform differences (macOS/Windows/Linux), installing into a fresh virtual environment is the most reliable workflow.

python -m venv .venv

# Activate (macOS/Linux)
source .venv/bin/activate

# Activate (Windows PowerShell)
# .venv\Scripts\Activate.ps1

python -m pip install -U pip

# Install from this repo (CLI + FRED fetch)
python -m pip install -e ".[cli,fred]"

For development (tests + docs + plotting):

python -m pip install -e ".[dev,cli,fred,docs,plot]"

Installation

Option 1: Editable Install (recommended for development)

git clone https://github.com/shawcharles/srvar-toolkit.git
cd srvar-toolkit
pip install -e .

Option 2: Install with Extras

# With plotting support
pip install -e '.[plot]'

# With labeled xarray outputs
pip install -e '.[xarray]'

# With ArviZ integration (InferenceData outputs)
pip install -e '.[arviz]'

# With FRED data fetching
pip install -e '.[fred]'

# With all development tools
pip install -e '.[dev]'

# All extras
pip install -e '.[dev,plot,fred,docs,xarray,arviz]'

Note: srvar fetch-fred requires the optional fred extra (it depends on fredapi).

If you see a warning like srvar-toolkit ... does not provide the extra 'fred', you are likely installing a different distribution than this repository. From the repository root, prefer:

python -m pip install -e ".[fred]"

You will also need a FRED API key (set FRED_API_KEY in your environment).


Usage

Quick Start

Fit a simple Bayesian VAR and generate forecasts:

import numpy as np
from srvar import Dataset
from srvar.api import fit, forecast
from srvar.spec import ModelSpec, PriorSpec, SamplerConfig

# Create a dataset
ds = Dataset.from_arrays(
    values=np.random.standard_normal((80, 2)),
    variables=["y1", "y2"]
)

# Configure the model
model = ModelSpec(p=2, include_intercept=True)
prior = PriorSpec.niw_default(k=1 + ds.N * model.p, n=ds.N)
sampler = SamplerConfig(draws=500, burn_in=100, thin=1)

# Fit and forecast
fit_res = fit(ds, model, prior, sampler)
fc = forecast(fit_res, horizons=[1, 4], draws=200)
print(fc.mean)

Labeled outputs (xarray / ArviZ)

Optional labeled outputs are available via srvar.xarray and srvar.arviz (see srvar/xarray.py and srvar/arviz.py):

from srvar.xarray import fit_to_xarray, forecast_to_xarray

ds_fit = fit_to_xarray(fit_res)
ds_fc = forecast_to_xarray(fc)

Conventions:

  • Core dims are draw, time, variable (plus horizon for forecasts and factor for FSV).
  • Time-varying SV/FSV states are aligned to the full time index; the first p entries are NaN because these states are defined on the effective sample T - p.
  • For factor SV, ds_fit["loadings"] is an alias of ds_fit["lambda"].

ArviZ (InferenceData) integration:

from srvar.arviz import fit_to_inferencedata

idata = fit_to_inferencedata(fit_res)

Notebooks

Expository Jupyter notebooks live in examples/notebooks/ (see examples/notebooks/README.md).

Steady-State VAR (SSP)

SSP replaces the explicit intercept with a steady-state mean vector mu.

import numpy as np
from srvar.api import fit, forecast
from srvar.spec import ModelSpec, PriorSpec, SamplerConfig, SteadyStateSpec

model = ModelSpec(
    p=2,
    include_intercept=True,
    steady_state=SteadyStateSpec(mu0=np.array([0.0, 0.0]), v0_mu=0.1),
)

YAML (CLI) example:

model:
  p: 2
  include_intercept: true
  steady_state:
    mu0: [0.02, 0.03]
    v0_mu: 0.01
    ssvs:
      enabled: false
      spike_var: 0.0001
      slab_var: 0.01
      inclusion_prob: 0.5

Shadow-Rate Model with Stochastic Volatility

from srvar import Dataset, ElbSpec, VolatilitySpec
from srvar.api import fit, forecast
from srvar.spec import ModelSpec, PriorSpec, SamplerConfig

# Configure ELB + SV model
model = ModelSpec(
    p=4,
    include_intercept=True,
    elb=ElbSpec(applies_to=["interest_rate"], bound=0.125),
    volatility=VolatilitySpec(enabled=True)
)

# Fit with the explicit legacy Minnesota-style NIW prior
prior = PriorSpec.niw_minnesota_legacy(p=4, y=data_array, n=n_vars)
sampler = SamplerConfig(draws=2000, burn_in=500, thin=2)

fit_res = fit(dataset, model, prior, sampler)

PriorSpec.niw_minnesota(...) remains available as a backward-compatible alias for PriorSpec.niw_minnesota_legacy(...). For equation-specific own-vs-cross shrinkage, use PriorSpec.niw_minnesota_canonical(...) or prior.method: "minnesota_canonical"; that explicit canonical path currently supports homoskedastic models and diagonal stochastic volatility. Triangular and factor SV remain on the legacy NIW path. For diagonal-SV sensitivity work, the experimental bridge PriorSpec.niw_minnesota_tempered(..., alpha=0.25) and prior.method: "minnesota_tempered" keep the equation-wise path but temper the canonical variance map back toward the legacy baseline.

Plotting

from srvar.plotting import plot_shadow_rate, plot_forecast_fanchart, plot_volatility

# Plot inferred shadow rate
fig, _ax = plot_shadow_rate(fit_res, var="interest_rate")
fig.savefig("shadow_rate.png", dpi=150, bbox_inches="tight")

# Plot forecast fan chart
fig, _ax = plot_forecast_fanchart(fc, var="gdp_growth")
fig.savefig("forecast.png", dpi=150, bbox_inches="tight")

# Plot volatility paths
fig, _ax = plot_volatility(fit_res, var="gdp_growth")
fig.savefig("volatility.png", dpi=150, bbox_inches="tight")

For more examples, see the examples/README.md.

SSP example:

  • examples/ssp_fit_forecast.py

CLI + YAML (config-driven runs)

For production-style usage, you can run the toolkit from a YAML configuration file.

# Validate a config (checks schema, variable names, and basic compatibility)
srvar validate config/demo_config.yaml

# Run fit (+ optional forecast/plots depending on the config)
srvar run config/demo_config.yaml

# Override output directory
srvar run config/demo_config.yaml --out outputs/my_run

# Run a rolling/expanding backtest (refit + forecast over multiple origins)
srvar backtest config/backtest_demo_config.yaml

# Override output directory for backtest
srvar backtest config/backtest_demo_config.yaml --out outputs/my_backtest

# Fetch macro data directly from FRED into a cached CSV
srvar fetch-fred config/fetch_fred_demo_config.yaml

# Preview what would be fetched/written (no network calls)
srvar fetch-fred config/fetch_fred_demo_config.yaml --dry-run

# Preflight-check that series IDs exist (network call)
srvar fetch-fred config/fetch_fred_demo_config.yaml --validate-series

Loading saved run artifacts

srvar run writes artifacts into the output directory (from output.out_dir or --out), including:

  • config.yml
  • fit_result.npz
  • forecast_result.npz (if forecasting is enabled)

To reload a run later (without re-reading the original CSV), use:

from srvar.artifacts import load_run_dir

fit_res = load_run_dir("outputs/my_run")

For an end-to-end example (fit → IRF/FEVD/HD, including factor SV), see examples/fsv_structural_analysis.py.

See:

  • config/demo_config.yaml (comment-rich template)
  • config/minimal_config.yaml (minimal runnable)
  • config/backtest_demo_config.yaml (comment-rich backtest template)
  • config/fetch_fred_demo_config.yaml (comment-rich FRED fetch template)
  • config/fsv_demo_config.yaml (factor SV demo config)
  • config/elb_fsv_demo_config.yaml (ELB + factor SV demo config)
  • config/ssp_fsv_demo_config.yaml (steady-state + factor SV demo config)
  • config/carriero2025_backtest_15var_shadow.yaml (Carriero et al. 2025: shadow-rate VAR baseline config)
  • papers/carriero2025forecasting/README.md (replication harness entrypoint)

Replication: Carriero et al. (2025)

From the repository root:

# Optional deps needed for YAML configs + (optional) FRED fetching
python -m pip install -e ".[cli,fred]"

# Run the two baseline configs (expects data/cache/carriero2025_15var.csv to exist)
python papers/carriero2025forecasting/run_replication.py

# Fetch the dataset from FRED first (requires FRED_API_KEY)
python papers/carriero2025forecasting/run_replication.py --fetch-data

Backtest config keys (high level)

In addition to the standard keys (data, model, prior, sampler, output), backtesting uses:

  • backtest: refit schedule and forecast horizons
    • mode: expanding or rolling
    • min_obs: minimum training sample size at first origin
    • step: origin step size
    • horizons: list of horizons to evaluate
    • draws, quantile_levels: forecast distribution settings
    • stationarity, stationarity_tol, stationarity_max_draws: optional stationarity conditioning
  • evaluation: which metrics/plots to generate
    • coverage: empirical interval coverage by horizon
    • pit: PIT histograms for calibration checks
    • crps: CRPS-by-horizon plot + CRPS in metrics table
    • wis: weighted interval score (WIS) in metrics table
    • pinball: pinball (quantile) loss in metrics table
    • log_score: Gaussian log score (log predictive density) in metrics table
    • elb_censor: ELB-censored scoring (floor realized values; optionally floor forecasts)
    • metrics_table: write metrics.csv
  • output.store_forecasts_in_memory: control whether backtests retain all forecast draws in RAM (required for plots)

Backtest artifacts

When you run srvar backtest, outputs are written into output.out_dir (or --out), for example:

  • config.yml
  • metrics.csv
  • coverage_all.png, coverage_<var>.png
  • pit_<var>_h<h>.png
  • crps_by_horizon.png
  • backtest_summary.json

Roadmap

  • Conjugate BVAR (NIW) with closed-form posteriors
  • Legacy Minnesota-style NIW shrinkage priors
  • Stochastic Search Variable Selection (SSVS)
  • Shadow-rate / ELB data augmentation
  • Stochastic volatility (RW/AR1; diagonal/triangular covariance)
  • Combined ELB + SV model
  • Forecasting with fan charts
  • Plotting utilities
  • Bayesian LASSO prior
  • Steady-state VAR parameterisation
  • Dirichlet-Laplace prior
  • Full-covariance stochastic volatility (factor SV; v1: NIW+RW)
  • Replication harness: Carriero et al. (2025) baseline configs + table builder
  • Replication: match paper tables/figures end-to-end (data and evaluation details)

See the open issues for a full list of proposed features.


Contributing

Contributions are welcome and appreciated. To contribute:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes and add tests
  4. Run the test suite (pytest)
  5. Commit your changes (git commit -m 'feat: add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

For full contributor guidelines (including docs builds, style, and testing expectations), see CONTRIBUTING.md.

Limitations and performance notes

  • This is currently an alpha research toolkit.
  • SV coverage is still evolving: diagonal SV, triangular covariance (time-invariant correlations), and factor SV (time-varying full covariance) are supported. Factor SV is currently limited to prior.family: "niw" with RW dynamics; ELB, steady-state, and robust shocks are supported.
  • MCMC runtime depends heavily on T, N, and sampler settings (draws/burn-in/thinning).
  • Backtests can stream metrics.csv without keeping all forecast draws in RAM (see output.store_forecasts_in_memory).
  • Stationarity conditioning is implemented as rejection of unstable coefficient draws; this can be expensive for weak priors (see forecast.stationarity_max_draws / backtest.stationarity_max_draws).

The documentation site contains more detailed guidance and caveats.

Development Setup

# Clone and install with dev dependencies
git clone https://github.com/shawcharles/srvar-toolkit.git
cd srvar-toolkit
pip install -e '.[dev]'

# Install pre-commit hooks
pre-commit install

# Run tests
pytest

# Run linting
ruff check
ruff format --check

License

Distributed under the MIT License. See LICENSE for more information.


Citing

If you use srvar-toolkit in your research, please cite both the software and the original methodology paper.

Software Citation

@software{shaw2025srvar,
  author       = {Shaw, Charles},
  title        = {srvar-toolkit: Shadow-Rate VAR Toolkit for Python},
  year         = {2026},
  url          = {https://github.com/shawcharles/srvar-toolkit},
  version      = {0.3.0}
}

Methodology Citation

@article{grammatikopoulos2025forecasting,
  author  = {Grammatikopoulos, Michael},
  title   = {Forecasting With Machine Learning Shadow-Rate VARs},
  journal = {Journal of Forecasting},
  year    = {2025},
  pages   = {1--17},
  doi     = {10.1002/for.70041}
}

@article{carriero2025forecasting,
  title={Forecasting with shadow rate VARs},
  author={Carriero, Andrea and Clark, Todd E and Marcellino, Massimiliano and Mertens, Elmar},
  journal={Quantitative Economics},
  volume={16},
  number={3},
  pages={795--822},
  year={2025},
  publisher={Wiley Online Library}
}

Acknowledgments

This toolkit implements methods from:

Grammatikopoulos, M. 2025. "Forecasting With Machine Learning Shadow-Rate VARs." Journal of Forecasting 1–17. https://doi.org/10.1002/for.70041

Carriero, A., Clark, T. E., Marcellino, M., & Mertens, E. 2025. "Forecasting with shadow rate VARs." Quantitative Economics 16(3), 795–822.

For the original MATLAB replication code, see: MichaelGrammmatikopoulos/MLSRVARs

For an additional MATLAB replication toolbox with code written by Elmar Mertens, see: elmarmertens/CCMMshadowrateVAR-code

Additional References

  • Kim, S., Shephard, N., & Chib, S. (1998). "Stochastic Volatility: Likelihood Inference and Comparison with ARCH Models." Review of Economic Studies 65(3), 361–393.
  • Carriero, A., Clark, T. E., & Marcellino, M. (2019). "Large Bayesian Vector Autoregressions with Stochastic Volatility and Non-Conjugate Priors." Journal of Econometrics 212(1), 137–154.

(back to top)


About

SRVAR toolkit inspired by Grammatikopoulos (2025, Journal of Forecasting)

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages