Source code for rpylib.numerical.closedform.cflevycopula

"""
Closed-Form pricing formula for Lévy copula models

    .. note:: The Garreau-Kercheval paper is explained in their paper:
                'A Structural Jump Threshold Framework for Credit Risk'
"""

import numpy as np
import scipy.optimize

from ..tools import interval_I


[docs]class CFLevyCopulaModel:
[docs] def __init__(self, levy_copula_model: "LevyCopulaModel"): self.levy_copula_model = levy_copula_model
def _theta(self, levels_a): dim = self.levy_copula_model.dimension() if dim > 3: raise NotImplementedError( "theta function not implemented for dimension > 3" ) if dim != len(levels_a): raise ValueError( "Expected one level per margin, got: " + str(len(levels_a)) + "instead of: " + str(dim) ) if any(a >= 0 for a in levels_a): raise ValueError("All a-levels must be negative") marginal_tail_integral = self.levy_copula_model.margin_tail_integral diag = np.array( [ model.mass(*interval_I(ai)) for model, ai in zip(self.levy_copula_model.models, levels_a) ] ) lambda_matrix = np.diag(diag) for i in range(dim): for j in range(i + 1, dim): x = levels_a[i], levels_a[j] lambda_matrix[i, j] = -marginal_tail_integral(indices=[i, j], x=x) theta = np.sum(lambda_matrix) if dim == 3: theta -= self.levy_copula_model.tail_integrals(x=levels_a) return theta
[docs] def survival_probability(self, levels_a: list[float], t: float): """Pricing a survival probability in the Garreau-Kercheval framework :param levels_a: thresholds a :param t: time t """ theta = self._theta(levels_a) probability = np.exp(-t * theta) return probability
[docs] def first_to_default_par_spread(self, levels_a: list[float], recovery_rate: float): """Pricing the FtD CDS spread in the Garreau-Kercheval framework :param levels_a: thresholds a :param recovery_rate: CSD recovery rate """ theta = self._theta(levels_a) return (1 - recovery_rate) * theta
[docs] def implied_cds_spread( self, pv: float, level_a: list[float], recovery_rate: float, maturity: float ): theta = self._theta(level_a) r = self.levy_copula_model.models[0].r default_leg = ( (1 - recovery_rate) * (1 - np.exp(-(r + theta) * maturity)) * theta / (r + theta) ) fixed_leg = (1 - np.exp(-(r + theta) * maturity)) / (r + theta) def fun(spread): return default_leg - spread * fixed_leg - pv a, b = ( -10, 10, ) # `a` is negative because very low number of Monte-Carlo paths might yield to a large variance # such that the confidence interval contains 0 res = scipy.optimize.brentq(f=fun, a=a, b=b) return res