Source code for nelson_siegel_svensson.ns
# -*- coding: utf-8 -*-
"""Implementation of a Nelson-Siegel interest rate curve model.
See `NelsonSiegelCurve` class for details.
"""
from numbers import Real
from dataclasses import dataclass
from typing import Union, Tuple
import numpy as np
from numpy import exp
EPS = np.finfo(float).eps
[docs]@dataclass
class NelsonSiegelCurve:
"""Implementation of a Nelson-Siegel interest rate curve model.
This curve can be interpreted as a factor model with three
factors (including a constant).
"""
beta0: float
beta1: float
beta2: float
tau: float
[docs] def factors(
self, T: Union[float, np.ndarray]
) -> Union[Tuple[float, float], Tuple[np.ndarray, np.ndarray]]:
"""Factor loadings for time(s) T, excluding constant."""
tau = self.tau
if isinstance(T, Real) and T <= 0:
return 1, 0
elif isinstance(T, np.ndarray):
zero_idx = T <= 0
T[zero_idx] = EPS # avoid warnings in calculations
exp_tt0 = exp(-T / tau)
factor1 = (1 - exp_tt0) / (T / tau)
factor2 = factor1 - exp_tt0
if isinstance(T, np.ndarray):
T[zero_idx] = 0
factor1[zero_idx] = 1
factor2[zero_idx] = 0
return factor1, factor2
[docs] def factor_matrix(self, T: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""Factor loadings for time(s) T as matrix columns,
including constant column (=1.0).
"""
factor1, factor2 = self.factors(T)
constant: Union[float, np.ndarray] = (
np.ones(T.size) if isinstance(T, np.ndarray) else 1
)
return np.stack([constant, factor1, factor2]).transpose()
[docs] def zero(self, T: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""Zero rate(s) of this curve at time(s) T."""
factor1, factor2 = self.factors(T)
return self.beta0 + self.beta1 * factor1 + self.beta2 * factor2
def __call__(self, T: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""Zero rate(s) of this curve at time(s) T."""
return self.zero(T)
[docs] def forward(self, T: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""Instantaneous forward rate(s) of this curve at time(s) T."""
exp_tt0 = exp(-T / self.tau)
return self.beta0 + self.beta1 * exp_tt0 + self.beta2 * exp_tt0 * T / self.tau