Source code for merton.core.spread
"""Implied credit spread from a Merton-model PD."""
from __future__ import annotations
import numpy as np
from .._typing import ArrayLike, FloatArray
from ..exceptions import MertonInputError
[docs]
def implied_credit_spread(
pd: ArrayLike,
T: ArrayLike,
lgd: ArrayLike = 0.6,
*,
in_bps: bool = True,
) -> FloatArray:
r"""Continuous-compounding credit spread implied by a horizon PD.
.. math::
s = -\frac{1}{T} \ln\!\bigl(1 - \text{PD} \cdot \text{LGD}\bigr)
Parameters
----------
pd
Cumulative probability of default over the horizon (decimal).
T
Horizon in years (must be > 0).
lgd
Loss given default (decimal, default 0.6 ≡ 60 % loss / 40 % recovery).
in_bps
Return basis points (``True``, default) instead of decimal.
Returns
-------
FloatArray
Annualised credit spread.
Examples
--------
>>> float(round(implied_credit_spread(0.01, 1.0, lgd=0.6), 2))
60.18
"""
pd_arr = np.asarray(pd, dtype=np.float64)
T_arr = np.asarray(T, dtype=np.float64)
lgd_arr = np.asarray(lgd, dtype=np.float64)
if np.any(T_arr <= 0):
raise MertonInputError("T must be strictly positive")
if np.any(lgd_arr < 0) or np.any(lgd_arr > 1):
raise MertonInputError("lgd must lie in [0, 1]")
if np.any(pd_arr < 0) or np.any(pd_arr > 1):
raise MertonInputError("pd must lie in [0, 1]")
product = np.clip(pd_arr * lgd_arr, 0.0, 1.0 - 1e-16)
spread = -np.log1p(-product) / T_arr
return spread * 10_000.0 if in_bps else spread
__all__ = ["implied_credit_spread"]