STVARResult#

class cultivars.multivariate.nonlinear.smooth_transition.STVARResult(*, endog, names, order, trend, delay, threshold, threshold_name, threshold_values, transition_series, lower_coefficients, upper_coefficients, lower_deterministic, upper_deterministic, resid, fittedvalues, llf, nobs, n_params, transition, gamma, transition_scale, sigma_u)[source]

Bases: _VectorObservedRegimeResult

A fitted smooth-transition vector autoregression.

The estimate of

\[y_t = (1 - G(z_t))\,\bigl(c_L + \textstyle\sum_{i} A_{L,i}\, y_{t-i}\bigr) + G(z_t)\,\bigl(c_U + \textstyle\sum_{i} A_{U,i}\, y_{t-i}\bigr) + u_t, \qquad z_t = s_{t-d},\]

with the transition weight either logistic or exponential in the standardized distance from the threshold,

\[G(z) = \bigl(1 + e^{-\gamma (z - c)/\sigma_z}\bigr)^{-1} \quad\text{or}\quad G(z) = 1 - e^{-\gamma (z - c)^2/\sigma_z^2},\]

estimated by concentrated maximum likelihood: given \((\gamma, c)\) the two regimes’ coefficients come from one weighted least-squares solve, and only the pair is searched on the log-determinant of the residual covariance. The record extends the observed-regime surface – the two coefficient stacks, regime-conditional irf, fevd, ma_representation and forecast, the stability of each regime, and comparison by information criteria – with the transition’s own parameters and the single covariance the smooth form admits.

Note

gamma is reported per standard deviation of the transition variable; divide by transition_scale for the speed on the original scale. The logistic weight equals one half at the threshold and is monotone in \(z\), so the regimes are directional; the exponential weight equals zero at the threshold and one far from it on either side, so the “upper” regime is the tails and the “lower” regime the middle. The likelihood is nearly flat in \(\gamma\) once the weight is effectively an indicator, so a very large fitted gamma is a lower bound rather than an estimate, and is_effectively_abrupt says when that has happened. Information criteria rank specifications but do not test for nonlinearity, because \((\gamma, c)\) are not identified under linearity. Standard errors are not yet available.

Variables:
  • transition – "logistic" or "exponential".

  • gamma – Transition speed, per standard deviation of the transition variable.

  • transition_scale – The standard deviation gamma is expressed against, retained so the weight path can be reproduced and so gamma can be read on the original scale.

  • sigma_u – Common innovation covariance, dof-corrected. One covariance rather than two: smooth weights never partition the sample, so a per-regime covariance has no subsample to be estimated from.

See also

  • STVAR – the model that produces this record.

  • TVARResult – the abrupt limit, with a covariance per regime.

  • STARResult – the univariate counterpart, split into LSTAR and ESTAR.

References

Auerbach, A. J., & Gorodnichenko, Y. (2012). Measuring the output responses to fiscal policy. American Economic Journal: Economic Policy, 4(2), 1-27.

Terasvirta, T., & Yang, Y. (2014). Specification, estimation and evaluation of vector smooth transition autoregressive models with applications. CREATES Research Paper 2014-08.

Example

A bivariate system whose own-lag coefficient slides from 0.6 to -0.3 as a logistic function of y1’s lag with speed 2 per unit; the fit recovers both regimes, a speed of the right size per standard deviation, and a weight path that spends a real share of the sample in between:

>>> import numpy as np
>>> rng = np.random.default_rng(1)
>>> y = np.zeros((400, 2))
>>> for t in range(1, 400):
...     g = 1.0 / (1.0 + np.exp(-2.0 * y[t - 1, 0]))
...     a = (1.0 - g) * 0.6 + g * -0.3
...     y[t] = a * y[t - 1] + rng.standard_normal(2)
>>> res = STVAR(y, order=1, transition_variable="y1").fit()
>>> res.transition, res.delay, res.threshold_name, res.nobs, res.n_params
('logistic', 1, 'y1', 399, 17.0)
>>> bool(1.0 < res.gamma < 3.0), bool(abs(res.threshold) < 0.5), res.is_effectively_abrupt
(True, True, False)
>>> lower, upper = np.diag(res.lower_coefficients[0]), np.diag(res.upper_coefficients[0])
>>> bool(np.all(np.abs(lower - 0.6) < 0.15)), bool(np.all(np.abs(upper + 0.3) < 0.25))
(True, True)
>>> weight = res.regime_weight
>>> between = np.mean((weight > 0.2) & (weight < 0.8))
>>> bool(0.2 < res.upper_fraction < 0.5), bool(between > 0.25)
(True, True)
>>> res.sigma_u.shape, bool(np.allclose(res._weight_at(np.array([res.threshold])), 0.5))
((2, 2), True)

Private methods#

_comparison_label()

Short specification label for a ranking table.

_from_fit(fit, model)

Assemble the public result from a raw fit and its specification.

_regime_sigma(regime)

The common innovation covariance, whichever regime is named.

_summary_table()

Build the structured summary.

_weight_at(values)

The transition function at arbitrary transition values.