TimeVaryingNelsonSiegel.fit#

TimeVaryingNelsonSiegel.fit(*, filter='unscented')[source]#

Maximize the approximate likelihood.

Parameters:

filter (str) – "unscented" (default; exact through the linear transition, third-order accurate through the loadings) or "extended" (central-difference Jacobians).

Return type:

TimeVaryingNelsonSiegelResult

Returns:

A TimeVaryingNelsonSiegelResult whose filter field records the choice.

Raises:

SpecificationError – If the filter is unknown.

Example

The extended filter is the cheaper reader of the same system; its approximate likelihood is not comparable with the unscented one, and the parameter count is the same under both:

>>> import numpy as np
>>> from cultivars.engine._core._polynomials import _nelson_siegel_loadings
>>> rng = np.random.default_rng(0)
>>> taus = np.array([0.25, 0.5, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0])
>>> mu, bar = np.array([5.0, -1.5, 1.0]), np.log(0.6)
>>> f, log_lam = np.tile(mu, (60, 1)), np.full(60, bar)
>>> for t in range(1, 60):
...     f[t] = mu + 0.9 * (f[t - 1] - mu) + 0.2 * rng.standard_normal(3)
...     log_lam[t] = bar + 0.95 * (log_lam[t - 1] - bar) + 0.15 * rng.standard_normal()
>>> curves = np.stack(
...     [_nelson_siegel_loadings(taus, np.exp(log_lam[t])) @ f[t] for t in range(60)]
... )
>>> curves = curves + 0.02 * rng.standard_normal((60, 8))
>>> res = TimeVaryingNelsonSiegel(curves, taus).fit(filter="extended")
>>> res.filter, res.n_params, bool(np.corrcoef(res.log_decay, log_lam)[0, 1] > 0.5)
('extended', 23.0, True)
>>> TimeVaryingNelsonSiegel(curves, taus).fit(filter="particle")
Traceback (most recent call last):
cultivars.exceptions.SpecificationError: filter must be 'extended' or 'unscented'; ...