Skip to content

Yield Curve

quantflow.rates.yield_curve.YieldCurve pydantic-model

Bases: BaseModel, ABC

Abstract base class for yield curves

Fields:

ref_date pydantic-field

ref_date

Reference date for the yield curve

curve_type pydantic-field

curve_type = 'unknown'

Type of the yield curve, used for serialization and discrimination

instantaneous_forward_rate abstractmethod

instantaneous_forward_rate(ttm)

Calculate the instantaneous forward rate for a given time to maturity.

The instantaneous forward rate is related to discount factor by the following formula:

\[\begin{equation} f(\tau) = -\frac{\partial \ln D(\tau)}{\partial \tau} \end{equation}\]

where \(D(\tau)\) is the discount factor for a given time to maturity \(\tau\).

Accepts a scalar float or a float array. Returns a scalar float for scalar input and a numpy float array for array input.

Source code in quantflow/rates/yield_curve.py
@abstractmethod
def instantaneous_forward_rate(self, ttm: FloatArrayLike) -> FloatArrayLike:
    r"""Calculate the instantaneous forward rate for a given time to maturity.

    The instantaneous forward rate is related to discount factor
    by the following formula:

    \begin{equation}
        f(\tau) = -\frac{\partial \ln D(\tau)}{\partial \tau}
    \end{equation}

    where $D(\tau)$ is the discount factor for a given time to maturity $\tau$.

    Accepts a scalar float or a float array. Returns a scalar float for scalar
    input and a numpy float array for array input.
    """

discount_factor abstractmethod

discount_factor(ttm)

Calculate the discount factor for a given time to maturity.

The discount factor is related to the instantaneous forward rate by the following formula:

\[\begin{equation} D(\tau) = \exp{\left(-\int_0^\tau f(s) ds\right)} \end{equation}\]

where \(f(\tau)\) is the instantaneous forward rate for a given time to maturity \(\tau\).

Accepts a scalar float or a float array. Returns a scalar float for scalar input and a numpy float array for array input.

Source code in quantflow/rates/yield_curve.py
@abstractmethod
def discount_factor(self, ttm: FloatArrayLike) -> FloatArrayLike:
    r"""Calculate the discount factor for a given time to maturity.

    The discount factor is related to the instantaneous forward rate
    by the following formula:

    \begin{equation}
        D(\tau) = \exp{\left(-\int_0^\tau f(s) ds\right)}
    \end{equation}

    where $f(\tau)$ is the instantaneous forward rate for a given time to
    maturity $\tau$.

    Accepts a scalar float or a float array. Returns a scalar float for scalar
    input and a numpy float array for array input.
    """

calibrator

calibrator()

Return a calibration wrapper for this curve, or None if not available.

Source code in quantflow/rates/yield_curve.py
def calibrator(self) -> YieldCurveCalibration | None:
    """Return a calibration wrapper for this curve, or None if not available."""
    return None

jacobian

jacobian(ttm)

Analytical Jacobian of discount factors w.r.t. model parameters.

Returns None if no analytical Jacobian is available (default). Shape when not None: (len(ttm), n_params).

PARAMETER DESCRIPTION
ttm

Times to maturity in years.

TYPE: FloatArrayLike

Source code in quantflow/rates/yield_curve.py
def jacobian(
    self, ttm: Annotated[FloatArrayLike, Doc("Times to maturity in years.")]
) -> FloatArray | None:
    """Analytical Jacobian of discount factors w.r.t. model parameters.

    Returns None if no analytical Jacobian is available (default).
    Shape when not None: (len(ttm), n_params).
    """
    return None

continuously_compounded_rate

continuously_compounded_rate(ttm)

Calculate the continuously compounded rate for a given time to maturity.

The continuously compounded rate is related to the discount factor by the following formula:

\[\begin{equation} r(\tau) = -\frac{\ln D(\tau)}{\tau} \end{equation}\]

where \(D(\tau)\) is the discount factor for a given time to maturity \(\tau\).

Accepts a scalar float or a float array. Returns a scalar float for scalar input and a numpy float array for array input.

PARAMETER DESCRIPTION
ttm

Time to maturity in years

TYPE: ArrayLike

Source code in quantflow/rates/yield_curve.py
def continuously_compounded_rate(
    self, ttm: Annotated[ArrayLike, Doc("Time to maturity in years")]
) -> FloatArrayLike:
    r"""Calculate the continuously compounded rate for a given time to maturity.

    The continuously compounded rate is related to the discount factor
    by the following formula:

    \begin{equation}
        r(\tau) = -\frac{\ln D(\tau)}{\tau}
    \end{equation}

    where $D(\tau)$ is the discount factor for a given time to maturity $\tau$.

    Accepts a scalar float or a float array. Returns a scalar float for scalar
    input and a numpy float array for array input.
    """
    ttm_ = np.asarray(ttm, dtype=float)
    df = np.asarray(self.discount_factor(ttm_), dtype=float)
    result = np.where(
        ttm_ <= 0, self.instantaneous_forward_rate(0.0), -np.log(df) / ttm_
    )
    return maybe_float(result)

rates

rates(ttm, frequency=2)

Calculate zero rates compounded at the given frequency.

The continuously compounded rate \(r_c(\tau)\) is converted to a rate compounded \(m\) times per year via:

\[\begin{equation} r_m(\tau) = m\,(e^{r_c(\tau)/m} - 1) \end{equation}\]

When frequency=0 the result is continuously compounded (same as continuously_compounded_rate).

PARAMETER DESCRIPTION
ttm

Time to maturity in years

TYPE: ArrayLike

frequency

Compounding periods per year (e.g. 2 for semi-annual). Pass 0 for continuously compounded.

TYPE: int DEFAULT: 2

Source code in quantflow/rates/yield_curve.py
def rates(
    self,
    ttm: Annotated[ArrayLike, Doc("Time to maturity in years")],
    frequency: Annotated[
        int,
        Doc(
            "Compounding periods per year (e.g. 2 for semi-annual). "
            "Pass 0 for continuously compounded."
        ),
    ] = 2,
) -> FloatArrayLike:
    r"""Calculate zero rates compounded at the given frequency.

    The continuously compounded rate $r_c(\tau)$ is converted to a
    rate compounded $m$ times per year via:

    \begin{equation}
        r_m(\tau) = m\,(e^{r_c(\tau)/m} - 1)
    \end{equation}

    When ``frequency=0`` the result is continuously compounded (same as
    [continuously_compounded_rate][..continuously_compounded_rate]).
    """
    rc = self.continuously_compounded_rate(ttm)
    if frequency <= 0:
        return rc
    return frequency * np.expm1(rc / frequency)

plot

plot(ttm_max=10.0, n=200, **kwargs)

Plot the continuously compounded rate vs time to maturity.

Requires plotly to be installed.

PARAMETER DESCRIPTION
ttm_max

Maximum time to maturity in years

TYPE: float DEFAULT: 10.0

n

Number of points to evaluate

TYPE: int DEFAULT: 200

Source code in quantflow/rates/yield_curve.py
def plot(
    self,
    ttm_max: Annotated[float, Doc("Maximum time to maturity in years")] = 10.0,
    n: Annotated[int, Doc("Number of points to evaluate")] = 200,
    **kwargs: Any,
) -> Any:
    """Plot the continuously compounded rate vs time to maturity.

    Requires plotly to be installed.
    """
    return plot.plot_yield_curve(self, ttm_max=ttm_max, n=n, **kwargs)

register_curve_types classmethod

register_curve_types(*curve_classes)

Register a yield curve subclass for deserialization.

The registry key is the curve_type discriminator value rather than the class name, so the two can be named independently.

Source code in quantflow/rates/yield_curve.py
@classmethod
def register_curve_types(cls, *curve_classes: type[YieldCurve]) -> None:
    """Register a yield curve subclass for deserialization.

    The registry key is the ``curve_type`` discriminator value rather than
    the class name, so the two can be named independently.
    """
    for curve_cls in curve_classes:
        name = curve_cls.model_fields["curve_type"].default
        if not isinstance(name, str):
            raise TypeError(
                f"{curve_cls.__name__} must define a string curve_type default"
            )
        if current_type := _CURVE_TYPES.pop(name, None):
            _TYPES_TO_NAMES.pop(current_type, None)
        _CURVE_TYPES[name] = curve_cls
        _TYPES_TO_NAMES[curve_cls] = name

curve_types classmethod

curve_types()

Return the registered curve types.

Source code in quantflow/rates/yield_curve.py
@classmethod
def curve_types(cls) -> tuple[str, ...]:
    """Return the registered curve types."""
    return tuple(sorted(_CURVE_TYPES))

get_curve_class classmethod

get_curve_class(curve_type)

Get the yield curve class for a given curve type.

Source code in quantflow/rates/yield_curve.py
@classmethod
def get_curve_class(cls, curve_type: str) -> type[YieldCurve] | None:
    """Get the yield curve class for a given curve type."""
    return _CURVE_TYPES.get(curve_type)

quantflow.rates.no_discount.NoDiscountCurve pydantic-model

Bases: YieldCurve

Flat yield curve with zero rates (discount factor is always 1).

Fields:

curve_type pydantic-field

curve_type = 'no_discount_curve'

ref_date pydantic-field

ref_date

Reference date for the yield curve

calibrator

calibrator()

Return a [NoDiscountCalibration][quantflow.rates.no_discount.NoDiscountCurve.calibrator.NoDiscountCalibration] wrapping this curve.

Source code in quantflow/rates/no_discount.py
def calibrator(self) -> NoDiscountCalibration:
    """Return a [NoDiscountCalibration][.NoDiscountCalibration] wrapping
    this curve."""
    return NoDiscountCalibration(yield_curve=self)

instantaneous_forward_rate

instantaneous_forward_rate(ttm)
Source code in quantflow/rates/no_discount.py
def instantaneous_forward_rate(self, ttm: FloatArrayLike) -> FloatArrayLike:
    arr = np.asarray(ttm, dtype=float)
    return np.zeros_like(arr) if arr.ndim > 0 else 0.0

discount_factor

discount_factor(ttm)
Source code in quantflow/rates/no_discount.py
def discount_factor(self, ttm: FloatArrayLike) -> FloatArrayLike:
    arr = np.asarray(ttm, dtype=float)
    return np.ones_like(arr) if arr.ndim > 0 else 1.0

jacobian

jacobian(ttm)

Analytical Jacobian of discount factors w.r.t. model parameters.

Returns None if no analytical Jacobian is available (default). Shape when not None: (len(ttm), n_params).

PARAMETER DESCRIPTION
ttm

Times to maturity in years.

TYPE: FloatArrayLike

Source code in quantflow/rates/yield_curve.py
def jacobian(
    self, ttm: Annotated[FloatArrayLike, Doc("Times to maturity in years.")]
) -> FloatArray | None:
    """Analytical Jacobian of discount factors w.r.t. model parameters.

    Returns None if no analytical Jacobian is available (default).
    Shape when not None: (len(ttm), n_params).
    """
    return None

continuously_compounded_rate

continuously_compounded_rate(ttm)

Calculate the continuously compounded rate for a given time to maturity.

The continuously compounded rate is related to the discount factor by the following formula:

\[\begin{equation} r(\tau) = -\frac{\ln D(\tau)}{\tau} \end{equation}\]

where \(D(\tau)\) is the discount factor for a given time to maturity \(\tau\).

Accepts a scalar float or a float array. Returns a scalar float for scalar input and a numpy float array for array input.

PARAMETER DESCRIPTION
ttm

Time to maturity in years

TYPE: ArrayLike

Source code in quantflow/rates/yield_curve.py
def continuously_compounded_rate(
    self, ttm: Annotated[ArrayLike, Doc("Time to maturity in years")]
) -> FloatArrayLike:
    r"""Calculate the continuously compounded rate for a given time to maturity.

    The continuously compounded rate is related to the discount factor
    by the following formula:

    \begin{equation}
        r(\tau) = -\frac{\ln D(\tau)}{\tau}
    \end{equation}

    where $D(\tau)$ is the discount factor for a given time to maturity $\tau$.

    Accepts a scalar float or a float array. Returns a scalar float for scalar
    input and a numpy float array for array input.
    """
    ttm_ = np.asarray(ttm, dtype=float)
    df = np.asarray(self.discount_factor(ttm_), dtype=float)
    result = np.where(
        ttm_ <= 0, self.instantaneous_forward_rate(0.0), -np.log(df) / ttm_
    )
    return maybe_float(result)

rates

rates(ttm, frequency=2)

Calculate zero rates compounded at the given frequency.

The continuously compounded rate \(r_c(\tau)\) is converted to a rate compounded \(m\) times per year via:

\[\begin{equation} r_m(\tau) = m\,(e^{r_c(\tau)/m} - 1) \end{equation}\]

When frequency=0 the result is continuously compounded (same as continuously_compounded_rate).

PARAMETER DESCRIPTION
ttm

Time to maturity in years

TYPE: ArrayLike

frequency

Compounding periods per year (e.g. 2 for semi-annual). Pass 0 for continuously compounded.

TYPE: int DEFAULT: 2

Source code in quantflow/rates/yield_curve.py
def rates(
    self,
    ttm: Annotated[ArrayLike, Doc("Time to maturity in years")],
    frequency: Annotated[
        int,
        Doc(
            "Compounding periods per year (e.g. 2 for semi-annual). "
            "Pass 0 for continuously compounded."
        ),
    ] = 2,
) -> FloatArrayLike:
    r"""Calculate zero rates compounded at the given frequency.

    The continuously compounded rate $r_c(\tau)$ is converted to a
    rate compounded $m$ times per year via:

    \begin{equation}
        r_m(\tau) = m\,(e^{r_c(\tau)/m} - 1)
    \end{equation}

    When ``frequency=0`` the result is continuously compounded (same as
    [continuously_compounded_rate][..continuously_compounded_rate]).
    """
    rc = self.continuously_compounded_rate(ttm)
    if frequency <= 0:
        return rc
    return frequency * np.expm1(rc / frequency)

plot

plot(ttm_max=10.0, n=200, **kwargs)

Plot the continuously compounded rate vs time to maturity.

Requires plotly to be installed.

PARAMETER DESCRIPTION
ttm_max

Maximum time to maturity in years

TYPE: float DEFAULT: 10.0

n

Number of points to evaluate

TYPE: int DEFAULT: 200

Source code in quantflow/rates/yield_curve.py
def plot(
    self,
    ttm_max: Annotated[float, Doc("Maximum time to maturity in years")] = 10.0,
    n: Annotated[int, Doc("Number of points to evaluate")] = 200,
    **kwargs: Any,
) -> Any:
    """Plot the continuously compounded rate vs time to maturity.

    Requires plotly to be installed.
    """
    return plot.plot_yield_curve(self, ttm_max=ttm_max, n=n, **kwargs)

register_curve_types classmethod

register_curve_types(*curve_classes)

Register a yield curve subclass for deserialization.

The registry key is the curve_type discriminator value rather than the class name, so the two can be named independently.

Source code in quantflow/rates/yield_curve.py
@classmethod
def register_curve_types(cls, *curve_classes: type[YieldCurve]) -> None:
    """Register a yield curve subclass for deserialization.

    The registry key is the ``curve_type`` discriminator value rather than
    the class name, so the two can be named independently.
    """
    for curve_cls in curve_classes:
        name = curve_cls.model_fields["curve_type"].default
        if not isinstance(name, str):
            raise TypeError(
                f"{curve_cls.__name__} must define a string curve_type default"
            )
        if current_type := _CURVE_TYPES.pop(name, None):
            _TYPES_TO_NAMES.pop(current_type, None)
        _CURVE_TYPES[name] = curve_cls
        _TYPES_TO_NAMES[curve_cls] = name

curve_types classmethod

curve_types()

Return the registered curve types.

Source code in quantflow/rates/yield_curve.py
@classmethod
def curve_types(cls) -> tuple[str, ...]:
    """Return the registered curve types."""
    return tuple(sorted(_CURVE_TYPES))

get_curve_class classmethod

get_curve_class(curve_type)

Get the yield curve class for a given curve type.

Source code in quantflow/rates/yield_curve.py
@classmethod
def get_curve_class(cls, curve_type: str) -> type[YieldCurve] | None:
    """Get the yield curve class for a given curve type."""
    return _CURVE_TYPES.get(curve_type)