MarkovSwitchingVARResult#

class cultivars.multivariate.regime_switching.markov_switching.MarkovSwitchingVARResult(*, endog, names, order, trend, n_regimes, switching_mean, switching_ar, switching_variance, transition, regimes, filtered_prob, predicted_prob, smoothed_prob, ergodic_prob, expected_durations, fittedvalues, resid, llf, nobs, n_params, label_ordering, n_iter, converged)[source]#

A fitted Markov-switching vector autoregression.

Conditional on a latent first-order Markov chain \(S_t \in \{0, \dots, M-1\}\) with transition matrix \(P\), the panel follows

\[y_t = D_{S_t} d_t + \sum_{i=1}^{p} A_{i, S_t}\, y_{t-i} + u_t, \qquad u_t \mid S_t = m \sim N(0, \Sigma_m),\]

with each of the three blocks either switching with the chain or held common across regimes. The chain is never observed: the record carries its filtered, predicted and smoothed posteriors, and every quantity that depends on the regime – fitted values, residuals, the regime a date “is in” – is an expectation under the smoothed posterior.

Note

Two consequences of the mixture form are enforced here rather than left to the reader. The likelihood is invariant to relabelling the regimes, so a sorting convention is imposed and label_ordering names it; without it two runs of the same model could not be compared coefficient by coefficient. And the number of regimes cannot be tested by a likelihood ratio: under the null of fewer regimes the extra regime’s parameters are unidentified and its transition probabilities lie on the boundary, so likelihood_ratio_test() refuses any comparison that changes M while allowing those that hold it fixed.

Variables:
  • endog – The observed (n, k) panel.

  • names – Variable labels, in column order.

  • order – Autoregressive order p within each regime.

  • trend – "n", "c" or "ct".

  • n_regimes – Number of latent regimes M.

  • switching_mean – Whether the deterministic block switches.

  • switching_ar – Whether the lag coefficients switch.

  • switching_variance – Whether the innovation covariance switches.

  • transition – Row-stochastic (M, M) matrix, transition[i, j] = Pr(S_t = j | S_{t-1} = i).

  • regimes – One closed-system view per regime, in label order; the composition hook the structural layer consumes.

  • filtered_prob – Pr(S_t = m | y_1..t), shape (nobs, M).

  • predicted_prob – Pr(S_t = m | y_1..t-1), shape (nobs, M).

  • smoothed_prob – Pr(S_t = m | y_1..T), shape (nobs, M).

  • ergodic_prob – Stationary distribution of transition.

  • expected_durations – 1 / (1 - P_mm), in periods.

  • fittedvalues – (nobs, k) posterior-weighted one-step means.

  • resid – (nobs, k) residuals against those weighted means.

  • llf – Log-likelihood from the final filter pass.

  • nobs – Effective sample size n - p.

  • n_params – Free parameters: M (M - 1) transitions, the coefficient blocks counted once per regime that switches them, and the covariance block(s).

  • label_ordering – Which quantity regimes were sorted by, ascending.

  • n_iter – EM iterations used by the refining run.

  • converged – Whether the refining run met its tolerance.

See also

References

Hamilton, J. D. (1990). Analysis of time series subject to changes in regime. Journal of Econometrics, 45(1-2), 39-70.

Kim, C.-J. (1994). Dynamic linear models with Markov-switching. Journal of Econometrics, 60(1-2), 1-22.

Krolzig, H.-M. (1997). Markov-Switching Vector Autoregressions. Springer. Chapters 6 and 9.

Example

Two regimes that differ in their intercepts and scales around a common lag block. EM recovers the means, the transition matrix and the regime path, and the fitted values are the smoothed-posterior mixture of the two regimes’ conditional means:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> p = np.array([[0.97, 0.03], [0.05, 0.95]])
>>> mu = np.array([[-1.0, -0.5], [1.5, 1.0]])
>>> s, y = np.zeros(600, dtype=int), np.zeros((600, 2))
>>> for t in range(1, 600):
...     s[t] = np.searchsorted(np.cumsum(p[s[t - 1]]), rng.random())
...     y[t] = mu[s[t]] + 0.3 * y[t - 1] + 0.4 * rng.standard_normal(2)
>>> res = MarkovSwitchingVAR(y, order=1, n_regimes=2).fit(seed=0, n_init=3)
>>> res.specification, res.converged, res.nobs, res.n_params
('MSIH(2)-VAR(1)', True, 599, 16.0)
>>> np.vstack([view.deterministic[0] for view in res.regimes]).round(1)
array([[-1. , -0.5],
       [ 1.5,  1. ]])
>>> bool(np.abs(res.transition - p).max() < 0.03)
True
>>> bool((res.most_likely_regime == s[1:]).mean() > 0.98)
True
>>> mixture = sum(
...     res.smoothed_prob[:, [m]] * (v.deterministic[0] + y[:-1] @ v.coefficients[0].T)
...     for m, v in enumerate(res.regimes)
... )
>>> bool(np.allclose(mixture, res.fittedvalues))
True

Attributes#

endog

The observed (n, k) panel as supplied.

names

Variable labels, in column order.

order

Autoregressive order within each regime.

trend

The deterministic specification shared by every regime's design.

n_regimes

Number of latent regimes M.

switching_mean

Whether the deterministic block switches with the chain.

switching_ar

Whether the lag coefficients switch with the chain.

switching_variance

Whether the innovation covariance switches with the chain.

transition

The row-stochastic (M, M) transition matrix.

regimes

One closed linear system per regime, in label order.

filtered_prob

Pr(S_t = m | y_1..t), shape (nobs, M).

predicted_prob

Pr(S_t = m | y_1..t-1), shape (nobs, M).

smoothed_prob

Pr(S_t = m | y_1..T), shape (nobs, M).

ergodic_prob

The stationary distribution of transition, shape (M,).

expected_durations

1 / (1 - P_mm) per regime, in periods.

fittedvalues

The (nobs, k) smoothed-posterior mixture of the regimes' one-step means.

resid

endog[p:] - fittedvalues, shape (nobs, k).

llf

Log-likelihood from the final filter pass.

nobs

Effective sample size n - p.

n_params

transitions, the switching and common blocks, the covariance(s).

label_ordering

The quantity regimes were sorted by, ascending.

n_iter

EM iterations used by the refining run.

converged

Whether the refining run met its tolerance.

information_criteria

AIC, BIC, and HQIC derived from the likelihood and parameter count.

is_regimewise_stationary

Whether every regime is stable read as a linear VAR.

k_endog

Number of endogenous variables.

most_likely_regime

Pointwise most probable regime under the smoothed posterior.

regime_shares

Average smoothed probability of each regime over the sample.

regime_uncertainty

Mean posterior entropy, normalized so 0 is sharp and 1 flat.

specification

Krolzig code for which blocks switch, e.g. "MSIH(2)-VAR(1)".

Methods#

compare(*others[, criterion])

Rank this result against others by an information criterion.

likelihood_ratio_test(unrestricted)

Test this result as the restricted model against a nesting one.

probabilities([kind])

Return one of the three regime posteriors.

regime(index)

One regime's closed-system view, identity-stable across calls.

regime_table()

One row per regime: scale, persistence, and how much sample it owns.

summary()

The estimation summary, renderable as text, HTML, or a dataframe.