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

A 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 factors at 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 entries numpy.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

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)

Attributes#

panel

The (nobs, p) yield panel as supplied, nan where missing.

maturities

The (p,) strictly positive maturities, in the panel's time unit.

decay

The loading decay \(\lambda\), fitted or held fixed.

decay_fixed

Whether decay was held at a supplied value rather than estimated.

mu

The (3,) unconditional factor means, in the order level, slope, curvature.

ar

The (3,) diagonal of \(\Phi\), one persistence per factor.

state_innovation_cov

The (3, 3) factor innovation covariance \(Q\).

measurement_var

The (p,) per-maturity measurement variances.

factors

The (nobs, 3) smoothed factor paths.

factor_cov

The (nobs, 3, 3) smoothed factor covariances.

llf

The exact Gaussian log-likelihood at the estimate.

nobs

Curve dates in the panel.

n_params

3 + 3 + 6 + p, plus one when the decay was estimated.

curvature

The smoothed curvature factor, (nobs,).

information_criteria

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

level

The smoothed level factor (the long end), (nobs,).

loadings

The (p, 3) Nelson-Siegel loadings at the fitted decay.

n_maturities

Points on the curve.

slope

The smoothed slope factor, (nobs,).

state_space

The fitted system, re-applicable to data it was not estimated on.

Methods#

compare(*others[, criterion])

Rank this result against others by an information criterion.

curve(date_index[, maturities])

The fitted curve at one date, on any maturity grid.

fitted_curves()

Smoothed fitted yields, (nobs, p).

forecast(steps)

Curve forecasts with honest standard errors.

likelihood_ratio_test(unrestricted)

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

summary()

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