GitHub - joshuaccchan/statespace-toolkit: MATLAB library for Bayesian state space models: precision-based samplers that draw the whole state path at once, for unobserved components, time-varying parameter, stochastic volatility and dynamic factor models, with examples and the replication packages from joshuachan.org. · GitHub
Skip to content

About

MATLAB library for Bayesian state space models: precision-based samplers that draw the whole state path at once, for unobserved components, time-varying parameter, stochastic volatility and dynamic factor models, with examples and the replication packages from joshuachan.org.

Topics

Resources

Stars

24 stars

Watchers

1 watching

Forks

Repository files navigation

statespace-toolkit statespace-toolkit logo

DOI View on File Exchange

A MATLAB library for Bayesian state space models: estimate latent trends, track time-varying relationships, model stochastic volatility, and handle missing or mixed-frequency data. The library builds on the precision sampler of Chan and Jeliazkov (2009), which draws the entire state path jointly, with reusable routines for integrated likelihood evaluation and stochastic volatility sampling. Worked examples cover unobserved components, time-varying parameter, stochastic volatility and dynamic factor models.

run setup.m                 % adds core/ to the path
cd examples
ex01_precision_sampler      % the precision sampler on a local level model

The code needs MATLAB with the Statistics and Machine Learning Toolbox. setup.m also checks for the Optimization Toolbox, which four of the archived packages use. The library and the examples are tested on MATLAB R2025b, on Windows and, in the repository's continuous integration, on Linux.

Learn the Methods

Most of the methods are developed in the book Bayesian Macroeconometrics: Methods and Applications (Chapman & Hall/CRC, forthcoming): see the sample chapters and its code repository, with MATLAB, R and Python for all fourteen chapters. The missing-data draw of ex05 comes from Chan, Poon and Zhu (2023), and the TVP-MIDAS model of ex10 from Chan, Poon and Zhu (2026).

The how-to guides show how to use the core functions, one step at a time, on small examples with generated data. The first series draws the states of the local level model with the precision sampler, estimates its parameters by Gibbs sampling, and integrates the states out of its likelihood:

The second series does the same for a regression whose coefficients follow random walks, and then asks whether a coefficient varies at all:

The ten scripts in examples/ each run in under a minute. Start with ex01. ex01-ex06 are linear Gaussian models. ex07-ex09 add stochastic volatility: ex07 and ex08 draw the log-volatility by the auxiliary mixture sampler, and ex09, in which the volatility also enters the mean, by accept-reject Metropolis-Hastings. ex10 combines the two groups: it imposes the linear restriction on its time-varying weights with ssm.restrict, which ex05 uses for the quarterly aggregation, and draws its volatility as ex07 does.

Script What it shows Data
ex01_precision_sampler The precision sampler, ssm.simulate_states, on a local level model, checked against a Kalman smoother and the truth generated; US CPI inflation
ex02_integrated_likelihood Two models compared by marginal likelihood, with the states integrated out by ssm.intlike, checked against a Kalman filter and quadrature US PCE inflation
ex03_tvp_regression A time-varying parameter Phillips curve, and the precision sampler timed against the Kalman filter with backward sampling US PCE inflation and output gap; generated
ex04_output_gap The output gap from a local linear trend with an AR(2) cycle, built with ssm.lagpolymat US real GDP
ex05_missing_data Missing data and mixed frequencies with ssm.select_obs: the selection matrices of a small pattern, then a late start, a hole and a ragged edge in one VAR, and a series observed only through quarterly aggregates, both checked against dense algebra and the values removed generated
ex06_dynamic_factor A dynamic factor model with one factor, as a business-cycle indicator FRED-MD
ex07_aux_mixture The auxiliary mixture sampler for stochastic volatility, ssm.ksc_rw_h0: the seven-component mixture against the exact density it approximates, the collapsed Gibbs sampler checked against the truth, and the draws reweighted to the exact posterior generated
ex08_sv_ma_t Stochastic volatility with MA(1) and Student-t errors, the models of Chan and Hsiao (2014) with mean zero: the log-volatility drawn by ssm.ksc_ar1_mean, and the MA coefficient and the degrees of freedom by Metropolis-Hastings with normal proposals at modes found by ssm.mode_newton silver returns
ex09_svm_armh Stochastic volatility in mean, with the log-volatility drawn by accept-reject Metropolis-Hastings, ssm.armh US CPI inflation
ex10_tvp_midas A MIDAS regression with time-varying weights under a linear restriction, checked against dense algebra and the truth generated

examples/data/README.md gives the source of every data file and the rows each example reads.

The tutorials answer one empirical question each with published models, on the data in examples/data/, and can be read without MATLAB. Each comes with a script that runs the same analysis on your own series.

Reproduce a Paper

Each package is kept byte for byte as published under replications/<paper>/legacy/, with a permanent as-published/<paper> git tag and the source zip's md5 recorded in provenance.md. Run the entry scripts from their own folders.

Which Model Do I Want?

If you want Paper Folder Entry scripts
A trend inflation model whose trend stays within bounds Chan, Koop & Potter (2013, JBES) chan_koop_potter2013_jbes_trendbound ARtrend_bound.m
Stochastic volatility with moving average errors, for inflation Chan (2013, JoE) chan2013_joe_masv main_UCMA.m
Trend inflation, the NAIRU and the Phillips curve, with bounded time variation Chan, Koop & Potter (2016, JAE) chan_koop_potter2016_jae_boundedpc biUC.m, main_forecasting.m
Stochastic volatility in mean with time-varying parameters Chan (2017, JBES) chan2017_jbes_svm UC_SVM.m
Trend inflation tied to long-run inflation expectations Chan, Clark & Koop (2018, JMCB) chan_clark_koop2018_jmcb_trendie main_estimation.m, main_forecasting.m
The uncertainty of inflation expectations, from high-frequency data Chan & Song (2018, JMCB) chan_song2018_jmcb_inflrv main_inflation_RV.m
Trend-cycle decompositions of output compared by marginal likelihood Grant & Chan (2017, JMCB) grant_chan2017_jmcb_trendcycle main_UC.m
The Hodrick-Prescott filter as an unobserved components model Grant & Chan (2017, JEDC) grant_chan2017_jedc_hpfilter main_script.m
Stochastic volatility with heavy tails and serial dependence Chan & Hsiao (2014, Wiley) chan_hsiao2014_wiley_sv plainSV.m, MASV.m, MASVt.m
GARCH against stochastic volatility, for energy prices Chan & Grant (2016, Energy Economics) chan_grant2016_eneco_garchsv main_SV.m, main_GARCH.m, main_forecasting.m
Marginal likelihoods by the cross-entropy method Chan & Eisenstat (2015, ER) chan_eisenstat2015_er_mlce probit_mc.m, logit_mc.m, bin_t_mc.m, VAR_mc.m, TVPVAR_mc.m, DFVAR_mc.m
The deviance information criterion for latent variable models Chan & Grant (2016, CSDA) chan_grant2016_csda_dic SF.m, VAR1.m, TVPVAR.m, CTVPVAR.m, semireg.m
The observed-data deviance information criterion for volatility models Chan & Grant (2016, JFEC) chan_grant2016_jfec_dicsv main_SV.m
Tests of whether a model needs its time variation or its stochastic volatility Chan (2018, ER) chan2018_er_spectest main_NAIRU.m, main_UCSV.m

tests/golden_runs/manifest.md names the few legacy scripts that do not run as shipped.

Build on the Code

Across the fourteen packages the same code recurs: the truncated normal sampler is copied into six of them, and the function that builds the design matrix of a time-varying parameter regression into four. The ssm package under core/ holds one copy of each step. Call the functions directly, or copy the example closest to your model; every example but ex05 builds a complete sampler from them.

Function Does Shown in
ssm.simulate_states Draws a state path from its posterior, given the banded precision matrix of the states: the precision sampler of Chan and Jeliazkov (2009) ex01-ex06, ex09, ex10, both tutorials
ssm.intlike Computes the log likelihood of a linear Gaussian state space model, with the states integrated out ex02, the UC tutorial
ssm.select_obs Splits the stacked data into observed and missing values, keeping the precision matrix of the missing values banded ex05
ssm.restrict Conditions draws from a Gaussian on linear restrictions M*x = z, reusing the Cholesky factor that ssm.simulate_states returns ex05, ex10, the MIDAS tutorial
ssm.diffmat Builds the first-difference matrix of a state equation, I - aL ex01-ex03, ex06, ex07, ex09, ex10, both tutorials
ssm.lagpolymat Builds the matrix of a lag polynomial, such as second differences or an AR(2) ex04
ssm.surform Builds the design matrix of a regression whose coefficients vary over time ex03, ex09, ex10, both tutorials
ssm.midas_basis Builds the regressors and restriction rows of a high-frequency predictor in a MIDAS regression with a Fourier weighting function, over the window of each period the MIDAS tutorial
ssm.mode_newton Finds the mode of a concave log density by Newton-Raphson, with a banded Hessian ex08, and through ssm.armh
ssm.armh Takes the accept-reject Metropolis-Hastings step of Chan (2017), for states whose conditional distribution is not Gaussian ex09
ssm.ksc_rw_h0 Draws the log-volatility path of a random walk by the auxiliary mixture sampler of Kim, Shephard and Chib (1998) ex07, ex10, the UC tutorial
ssm.ksc_ar1_mean Draws the log-volatility path of a stationary AR(1) by the same sampler ex08, the UC tutorial
ssm.ksc_rw_diffuse Draws the log-volatility path of a random walk whose first value has a normal prior with mean zero, by the same sampler both tutorials
ssm.ksc_rw_noncentered Draws a random-walk log-volatility in the noncentered form of Chan (2018), and returns the conditional mean and variance of its scale, which the Savage-Dickey ratio of that paper uses not yet in an example
ssm.tnormrnd Draws from a truncated normal distribution ex02, the UC tutorial
ssm.shaded_band Shades credible bands in figures ex01, ex03, ex04, ex07-ex09, the MIDAS tutorial
ssm.inefficiency_factor Computes the inefficiency factor of MCMC draws, the number of draws that carry the information of one independent draw the Gibbs sampling, integrated likelihood and TVP Gibbs sampling guides, the UC tutorial
ssm.mcse Computes the Monte Carlo standard error of a posterior mean estimated from MCMC draws the Gibbs sampling, integrated likelihood and TVP Gibbs sampling guides
ssm.geweke Computes the convergence diagnostic of Geweke (1992), which compares the means of an early and a late segment of a chain not yet in an example
ssm.specvar0 Estimates the long-run variance of the mean of MCMC draws, for the three diagnostics above through the diagnostics

Eleven of these functions, ssm.diffmat, ssm.geweke, ssm.inefficiency_factor, ssm.ksc_ar1_mean, ssm.ksc_rw_diffuse, ssm.ksc_rw_h0, ssm.mcse, ssm.shaded_band, ssm.specvar0, ssm.surform and ssm.tnormrnd, have the same code as their counterparts in bvar-toolkit, apart from error identifiers and the diagnostics' calls to ssm.specvar0. A unit test checks each against a pinned copy of the bvar-toolkit original. bvar.util.select_obs in bvar-toolkit copies ssm.select_obs, apart from its error identifier. The Econometrics Toolbox has a class named ssm: calls such as ssm.simulate_states resolve to this package, and ssm(...) still constructs the toolbox's state space object.

The subpackage ssm.models holds complete samplers built from these steps, each estimated in one call. ssm.models.tvp_midas estimates the MIDAS regressions of Chan, Poon and Zhu (2026), whose coefficients, weights and volatility can each vary over time, and nowcasts the next period. The MIDAS tutorial calls it. ssm.models.uc estimates unobserved components models, among them UCSV. Each has a random-walk trend and a gap. The variances of the trend innovations and of the gap can each be constant or time-varying, and the gap can have MA(1) errors. ssm.models.uc_forecast forecasts with these models. The UC tutorial calls both.

Verification

The unit tests check each function against the code it comes from or replaces. ssm.surform and ssm.tnormrnd are run beside every copy in the archived packages, four of SURform.m and six of tnormrnd.m, and must give identical output, ssm.tnormrnd draw for draw under a fixed seed. For ssm.simulate_states, the tests run the published scripts UC.m, linreg_tvp.m and DFM.m on a short chain twice: once as published, and once with the lines the function replaces swapped for a call to it. UC_SVM.m is run the same way for ssm.armh, and the published functions sample_CSV.m and the book's sample_SVM_h_ARMH.m are run on generated data beside ssm.armh, with the same target. Each of these tests checks that the random number stream ends in the same state and that the draws agree to rounding error.

ssm.ksc_ar1_mean is checked the same way against SV.m of Chan (2013), and ssm.ksc_rw_diffuse against SVRW.m of Chan, Clark and Koop (2018), which draws the same path when its initial value h0 is zero. ssm.ksc_rw_noncentered is checked draw for draw against a copy of SVRW_gam.m of Chan (2018) that solves with the same Cholesky factor, with UCSV_gam.m run whole.

ssm.intlike is checked against a Kalman filter and against two archived functions that compute it, intlike_UC0.m and intlike_tvpvar.m. ssm.diffmat and ssm.lagpolymat are checked against the matrices the published scripts write out, and ssm.select_obs against the worked illustrations of Chan, Poon and Zhu (2023). ssm.restrict must equal, bit for bit, the update that ex05 and ex10 wrote out before it existed, and its conditional mean and covariance must equal those from dense algebra. ssm.models.tvp_midas runs on generated data whose restriction rows change from period to period, and the restrictions must hold in every draw, those of the period to nowcast included. With high-frequency regressors of zeros, the variances of the weights must keep their prior, and the weights of the period to nowcast must have the moments of the random walk conditioned on every restriction. ssm.midas_basis with averaged rows must equal, bit for bit, the loop it replaced in the MIDAS tutorial. ssm.models.uc and ssm.models.uc_forecast must reproduce, draw for draw, the code with which the UC tutorial's results were computed, in each of its four models. That code is kept in tests/unit/private/. Their forecasts at every horizon must match paths simulated forward from the same draws.

Files from other repositories that the tests run are held verbatim in tests/fixtures/, with their md5s. On every push to main and every pull request, CI checks the archived packages against their as-published tags and the fixtures against their md5s, and runs the unit suite, setup.m, the examples, each tutorial's your_data.m and each guide's guide.m. To run the tests locally:

run tests/unit/run_unit_tests.m
run tests/run_examples.m

tests/variant_map.md records where each function's code comes from, which archived copies it matches and which pairs must never be merged.

Related Repositories

bvar-toolkit is the companion library for Bayesian VARs. It also archives, as chan_jeliazkov2009_statespace, the replication package of Chan and Jeliazkov (2009), which implements the precision sampler. The tests here run copies of its UC.m and SVRW.m.

Every quarter, trend-cycle-toolkit re-estimates published models of US trend inflation, the output gap and trend output growth. Five of those models come from packages archived here: Chan (2013); Chan, Koop and Potter (2013); Grant and Chan (2017, JMCB); Grant and Chan (2017, JEDC); and Chan, Clark and Koop (2018).

Citation

Cite the paper behind each method you use: CITING.md maps every replication package, library function and example to its paper, with BibTeX. To cite the toolkit itself:

Chan, J. C. C. (2026). statespace-toolkit: MATLAB code for Bayesian state space models. Zenodo. https://doi.org/10.5281/zenodo.22884655

The DOI resolves to the latest release. CITATION.cff holds the machine-readable record that GitHub's "Cite this repository" button reads.

License

MIT; see LICENSE. The license covers the archived packages too. The one third-party file, kde2d.m in grant_chan2017_jedc_hpfilter, the kernel density estimator of Botev, Grotowski and Kroese (2010), keeps its own terms. NOTICE.md states what the license covers.

About

MATLAB library for Bayesian state space models: precision-based samplers that draw the whole state path at once, for unobserved components, time-varying parameter, stochastic volatility and dynamic factor models, with examples and the replication packages from joshuachan.org.

Topics

Resources

Stars

24 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages