Metadata-Version: 2.4
Name: srvar-toolkit
Version: 0.3.1
Summary: SRVAR toolkit inspired by Grammatikopoulos (2025)
Author-email: charles shaw <charles@charlesshaw.net>
License: MIT
License-File: LICENSE
Keywords: bayesian,forecasting,macro,srvar,time-series,var
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.1
Requires-Dist: scipy>=1.11
Provides-Extra: accel
Requires-Dist: numba>=0.60; extra == 'accel'
Provides-Extra: arviz
Requires-Dist: arviz>=0.17; extra == 'arviz'
Requires-Dist: xarray>=2024.7; extra == 'arviz'
Provides-Extra: cli
Requires-Dist: pyyaml>=6; extra == 'cli'
Requires-Dist: rich>=13; extra == 'cli'
Requires-Dist: tqdm>=4; extra == 'cli'
Provides-Extra: dev
Requires-Dist: black>=24; extra == 'dev'
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: hypothesis>=6.100; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: openpyxl>=3.1; extra == 'dev'
Requires-Dist: pre-commit>=3.7; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.1; extra == 'dev'
Provides-Extra: docs
Requires-Dist: furo>=2024.8.6; extra == 'docs'
Requires-Dist: myst-parser>=2.0; extra == 'docs'
Requires-Dist: sphinx-autodoc-typehints>=2.3; extra == 'docs'
Requires-Dist: sphinx-copybutton>=0.5; extra == 'docs'
Requires-Dist: sphinx-design>=0.6; extra == 'docs'
Requires-Dist: sphinx>=7.2; extra == 'docs'
Provides-Extra: excel
Requires-Dist: openpyxl>=3.1; extra == 'excel'
Provides-Extra: fred
Requires-Dist: fredapi>=0.5.2; extra == 'fred'
Requires-Dist: python-dotenv>=1.0; extra == 'fred'
Requires-Dist: requests>=2.31; extra == 'fred'
Provides-Extra: plot
Requires-Dist: matplotlib>=3.8; extra == 'plot'
Provides-Extra: xarray
Requires-Dist: xarray>=2024.7; extra == 'xarray'
Description-Content-Type: text/markdown

<!-- Improved compatibility of back to top link -->
<a id="readme-top"></a>

<!-- PROJECT SHIELDS -->
[![License][license-shield]][license-url]
[![Python][python-shield]][python-url]
[![Code style: black][black-shield]][black-url]
[![Ruff][ruff-shield]][ruff-url]

<!-- PROJECT LOGO -->
<br />
<div align="center">

<h1 align="center">srvar-toolkit</h1>

  <p align="center">
    Shadow-rate VAR toolkit for Bayesian macroeconomic forecasting in pure Python.
    <br />
    <a href="https://srvar-toolkit.charlesshaw.net/"><strong>Explore the docs »</strong></a>
    <br />
    <br />
    <a href="#usage">Quick Start</a>
    ·
    <a href="https://github.com/shawcharles/srvar-toolkit/issues/new?labels=bug">Report Bug</a>
    ·
    <a href="https://github.com/shawcharles/srvar-toolkit/issues/new?labels=enhancement">Request Feature</a>
  </p>
</div>

<!-- TABLE OF CONTENTS -->
<details>
  <summary>Table of Contents</summary>
  <ol>
    <li><a href="#about-the-project">About The Project</a></li>
    <li><a href="#features">Features</a></li>
    <li><a href="#getting-started">Getting Started</a></li>
    <li><a href="#usage">Usage</a></li>
    <li><a href="#roadmap">Roadmap</a></li>
    <li><a href="#contributing">Contributing</a></li>
    <li><a href="#license">License</a></li>
    <li><a href="#contact">Contact</a></li>
    <li><a href="#citing">Citing</a></li>
    <li><a href="#acknowledgments">Acknowledgments</a></li>
  </ol>
</details>

---

<!-- ABOUT THE PROJECT -->
## About The Project

<br />

**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.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

### Built With

[![Python][python-badge]][python-url]
[![NumPy][numpy-badge]][numpy-url]
[![SciPy][scipy-badge]][scipy-url]
[![Pandas][pandas-badge]][pandas-url]

---

<table>
  <tr>
    <td align="center" width="50%">
      <img src="plots/shadow_rate_r.png" alt="Shadow Rate Example" height="240" />
    </td>
    <td align="center" width="50%">
      <img src="plots/forecast_fan_r_observed.png" alt="Forecast Fan Chart" height="240" />
    </td>
  </tr>
</table>


---

<!-- FEATURES -->
## 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 |

<p align="right">(<a href="#readme-top">back to top</a>)</p>

---

## 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 -->
## 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.

```bash
python -m venv .venv

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

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

python -m pip install -U pip

# Normal user install from PyPI (CLI + FRED fetch)
python -m pip install "srvar-toolkit[cli,fred]"
```

For development (tests + docs + plotting):

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

### Installation

#### Option 1: PyPI install (recommended for users)

```bash
pip install srvar-toolkit
```

#### Option 2: PyPI install with extras

```bash
# With CLI commands
pip install 'srvar-toolkit[cli]'

# With plotting support
pip install 'srvar-toolkit[plot]'

# With labeled xarray outputs
pip install 'srvar-toolkit[xarray]'

# With ArviZ integration (InferenceData outputs)
pip install 'srvar-toolkit[arviz]'

# With FRED data fetching
pip install 'srvar-toolkit[fred]'

# CLI plus FRED fetching
pip install 'srvar-toolkit[cli,fred]'
```

#### Option 3: Editable install (recommended for development)

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

#### Option 4: Editable install with extras

```bash
# 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.

If you are working from a local checkout and see a warning like `srvar-toolkit ... does not provide the extra 'fred'`, make sure you are installing from the repository root:

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

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


---

<!-- USAGE -->
## Usage

### Quick Start

Fit a simple Bayesian VAR and generate forecasts:

```python
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`):

```python
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:

```python
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`.

```python
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:

```yaml
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

```python
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

```python
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`](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.

```bash
# 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:

```python
from srvar.artifacts import load_run_dir

fit_res = load_run_dir("outputs/my_run")
```

New artifacts do not require pickle deserialisation. Artifacts written before the format migration
need `allow_legacy_pickle=True` and must only be loaded when their source and integrity are trusted;
that option can execute pickle code. See the [artifact reference](docs/reference/artifacts.md) for
the migration details.

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:

```bash
# 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 -->
## Roadmap

- [x] Conjugate BVAR (NIW) with closed-form posteriors
- [x] Legacy Minnesota-style NIW shrinkage priors
- [x] Stochastic Search Variable Selection (SSVS)
- [x] Shadow-rate / ELB data augmentation
- [x] Stochastic volatility (RW/AR1; diagonal/triangular covariance)
- [x] Combined ELB + SV model
- [x] Forecasting with fan charts
- [x] Plotting utilities
- [x] Bayesian LASSO prior
- [x] Steady-state VAR parameterisation
- [x] Dirichlet-Laplace prior
- [x] Full-covariance stochastic volatility (factor SV; v1: NIW+RW)
- [x] 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](https://github.com/shawcharles/srvar-toolkit/issues) for a full list of proposed features.



---

<!-- CONTRIBUTING -->
## 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

```bash
# 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 -->
## License

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


---



<!-- CITING -->
## Citing

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

### Software Citation

```bibtex
@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.1}
}
```

### Methodology Citation

```bibtex
@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 -->
## 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](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](https://github.com/MichaelGrammmatikopoulos/MLSRVARs)

For an additional MATLAB replication toolbox with code written by Elmar Mertens, see: [elmarmertens/CCMMshadowrateVAR-code](https://github.com/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.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

---

<!-- MARKDOWN LINKS & IMAGES -->
[license-shield]: https://img.shields.io/github/license/shawcharles/srvar-toolkit.svg?style=for-the-badge
[license-url]: https://github.com/shawcharles/srvar-toolkit/blob/main/LICENSE
[python-shield]: https://img.shields.io/badge/python-3.11+-blue.svg?style=for-the-badge&logo=python&logoColor=white
[python-url]: https://www.python.org/
[black-shield]: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
[black-url]: https://github.com/psf/black
[ruff-shield]: https://img.shields.io/badge/linting-ruff-orange.svg?style=for-the-badge
[ruff-url]: https://github.com/astral-sh/ruff

[python-badge]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
[numpy-badge]: https://img.shields.io/badge/NumPy-013243?style=for-the-badge&logo=numpy&logoColor=white
[numpy-url]: https://numpy.org/
[scipy-badge]: https://img.shields.io/badge/SciPy-8CAAE6?style=for-the-badge&logo=scipy&logoColor=white
[scipy-url]: https://scipy.org/
[pandas-badge]: https://img.shields.io/badge/Pandas-150458?style=for-the-badge&logo=pandas&logoColor=white
[pandas-url]: https://pandas.pydata.org/
