DynamicNelsonSiegelResult#
- class cultivars.multivariate.reduced_form.term_structure.DynamicNelsonSiegelResult(*, panel, maturities, decay, decay_fixed, mu, ar, state_innovation_cov, measurement_var, factors, factor_cov, llf, nobs, n_params)[source]
Bases:
_SummaryMixin,_ComparisonMixinA fitted dynamic Nelson-Siegel model: factors, decay, and noise.
The yield at maturity \(\tau\) on date \(t\) is three factors through fixed exponential loadings,
\[y_t(\tau) = L_t + S_t\,\frac{1 - e^{-\lambda\tau}}{\lambda\tau} + C_t\Bigl(\frac{1 - e^{-\lambda\tau}}{\lambda\tau} - e^{-\lambda\tau}\Bigr) + \varepsilon_t(\tau), \qquad f_t - \mu = \Phi\,(f_{t-1} - \mu) + \eta_t,\]with \(f_t = (L_t, S_t, C_t)'\), diagonal \(\Phi\), full innovation covariance \(Q\) and per-maturity measurement variances \(\sigma^2_\tau\). Everything here was estimated in one step by exact maximum likelihood through the Kalman filter, and the factor paths are the smoothed states given the whole panel.
Note
Three things to read correctly. The factors are smoothed, so
factorsat date \(t\) uses curves after \(t\);forecast()re-filters to the one-sided state before propagating, which is why its first step does not equal the last fitted curve pushed forward. Standard errors are reported for the forecast only, from factor uncertainty and measurement noise, never for the parameters – there is no coefficient covariance on this record. And the decay \(\lambda\) sets where the curvature loading peaks, at \(\tau \approx 1.79 / \lambda\); a fitted decay far from the Diebold-Li value for the panel’s time unit (0.0609 per month, 0.73 per year) usually means the unit, not the curve, is unusual.- Variables:
panel – The observed
(nobs, p)yield panel (missing entriesnumpy.nan).maturities – The
(p,)maturities.decay – The fitted loading decay
lambda.decay_fixed – Whether the decay was held fixed rather than estimated.
mu –
(3,)factor means (level, slope, curvature).ar –
(3,)diagonal factor persistences.state_innovation_cov –
(3, 3)factor innovation covariance.measurement_var –
(p,)per-maturity measurement variances.factors –
(nobs, 3)smoothed factor paths.factor_cov –
(nobs, 3, 3)smoothed factor covariances.llf – Exact Gaussian log-likelihood.
nobs – Curve dates.
n_params – Free parameters the likelihood was maximized over.
See also
DynamicNelsonSiegel– the model whosefit()returns this record.TimeVaryingNelsonSiegelResult– the extension in which the decay itself follows a random walk.FunctionalVARResult– the same loadings used as a fixed basis for an unrestricted factor VAR, with the decay profiled rather than estimated jointly.LinearGaussianSSM– the type ofstate_space.
References
Nelson, C. R., & Siegel, A. F. (1987). Parsimonious modeling of yield curves. Journal of Business, 60(4), 473-489.
Diebold, F. X., & Li, C. (2006). Forecasting the term structure of government bond yields. Journal of Econometrics, 130(2), 337-364.
Diebold, F. X., Rudebusch, G. D., & Aruoba, S. B. (2006). The macroeconomy and the yield curve: A dynamic latent factor approach. Journal of Econometrics, 131(1-2), 309-338.
Example
Curves on four maturities generated from persistent level, slope and curvature factors at decay 0.6 and read with 5 basis points of noise. The one-step fit tracks each factor, reproduces the panel to within the noise, prices a maturity it never saw, and forecasts with standard errors that grow with the horizon:
>>> import numpy as np >>> from cultivars.engine._core._polynomials import _nelson_siegel_loadings >>> rng = np.random.default_rng(0) >>> taus = np.array([0.5, 2.0, 5.0, 10.0]) >>> basis = _nelson_siegel_loadings(taus, 0.6) >>> mu = np.array([5.0, -1.5, 0.5]) >>> f = np.tile(mu, (40, 1)) >>> for t in range(1, 40): ... f[t] = mu + 0.9 * (f[t - 1] - mu) + 0.2 * rng.standard_normal(3) >>> curves = f @ basis.T + 0.05 * rng.standard_normal((40, 4)) >>> res = DynamicNelsonSiegel(curves, taus, decay=0.6).fit() >>> res.nobs, res.n_maturities, res.n_params, res.decay_fixed (40, 4, 16.0, True) >>> bool(np.corrcoef(res.level, f[:, 0])[0, 1] > 0.95) True >>> bool(np.sqrt(np.mean((res.fitted_curves() - curves) ** 2)) < 0.1) True >>> res.curve(-1, maturities=[7.0]).shape (1,) >>> mean, std = res.forecast(6) >>> mean.shape, bool(np.all(std[-1] > std[0])) ((6, 4), True)
Private attributes#
The parameter record, rebuilt for the system builder. |
Private methods#
Specification label used when this result appears in a ranking. |
|
Structured summary rendered by every display path. |