financialkit.credit#
Altman’s Z-score and Fisher’s linear discriminant; Merton’s structural
model through financialkit.options.black_scholes_price(), its
calibration to equity and the Crosbie-Bohn distance to default; Black-Cox
first passage and Leland’s endogenous default; piecewise-constant hazard
curves, Jarrow-Turnbull bonds and the Duffie-Singleton, treasury and
face-value recovery conventions (with a CIR intensity through
financialkit.interest_rate_models.CIRModel); O’Kane-Turnbull CDS
legs, par spreads, upfronts and the ISDA-style hazard bootstrap;
CreditMetrics migration and credit VaR, Jarrow-Lando-Turnbull generator
matrices (scipy.linalg.expm and logm) and the Israel-Rosenthal-Wei
regularization; Vasicek’s large homogeneous portfolio, CreditRisk+ by
Panjer’s recursion, Li’s Gaussian copula (through
financialkit.risk.gaussian_copula_sample()), the Basel II IRB
formula and Hull-White tranche pricing.
financialkit.credit: credit scoring, structural and reduced-form default models, CDS, rating migration and portfolio credit risk.
Altman’s Z-score; Merton’s structural model, its calibration to equity
and Crosbie-Bohn distance to default (through
financialkit.options.black_scholes_price()); Black-Cox first
passage and Leland’s endogenous default; Jarrow-Turnbull hazard curves
and Duffie-Singleton recovery of market value; O’Kane-Turnbull CDS legs,
par spreads, upfronts and the ISDA-style hazard bootstrap; CreditMetrics
migration, Jarrow-Lando-Turnbull generators (scipy.linalg.expm())
and Israel-Rosenthal-Wei regularization; Vasicek’s large homogeneous
portfolio, CreditRisk+ by Panjer’s recursion, Li’s Gaussian copula, the
Basel II IRB formula and Hull-White tranche pricing. Time is in years,
rates are continuously compounded and recovery is a fraction of face
value.
- class financialkit.credit.CDSResult(par_spread, protection_leg, rpv01, accrued_on_default, payment_times)[source]#
Bases:
objectLegs of a credit default swap, per unit notional.
- Parameters:
- class financialkit.credit.CreditMetricsResult(values, probabilities)[source]#
Bases:
objectA CreditMetrics value distribution over rating states.
- class financialkit.credit.HazardCurve(knots, hazards)[source]#
Bases:
SurvivalCurvePiecewise-constant default intensity.
The hazard is
hazards[i]on(knots[i-1], knots[i]](withknots[-1] = 0) and stays at the last value beyond the last knot.- Parameters:
Examples
>>> c = HazardCurve([1.0, 3.0], [0.01, 0.03]) >>> round(c.survival(2.0), 6) == round(float(np.exp(-0.04)), 6) True >>> round(c.inverse_cumulative_hazard(0.04), 12) 2.0
- class financialkit.credit.KMVResult(asset_values, asset_volatility, drift, distance_to_default, expected_default_frequency, n_iter, volatility_history)[source]#
Bases:
objectCrosbie and Bohn’s (2003) iterative estimate of the asset path and distance to default.
- Parameters:
- class financialkit.credit.LelandResult(default_barrier, debt, equity, firm_value, leverage, credit_spread, default_probability_weight)[source]#
Bases:
objectLeland’s (1994) perpetual-debt firm with endogenous default.
- Parameters:
- class financialkit.credit.LossDistribution(losses, pmf, method)[source]#
Bases:
objectA portfolio credit loss distribution on a grid of loss levels.
- class financialkit.credit.MertonResult(equity, debt, asset_value, asset_volatility, equity_volatility, default_probability, distance_to_default, credit_spread, recovery_rate)[source]#
Bases:
objectMerton’s (1974) firm: equity as a call on the assets, debt as the rest.
- Parameters:
- class financialkit.credit.SurvivalCurve[source]#
Bases:
ABCA default-time distribution given by its cumulative hazard \(\Lambda(t)\).
Survival is \(Q(t) = e^{-\Lambda(t)}\); Lando (1998) calls this the Cox-process view of default, with the intensity deterministic here.
- abstractmethod cumulative_hazard(t)[source]#
Integrated hazard \(\Lambda(t)\), non-decreasing with \(\Lambda(0) = 0\).
- default_probability(t, s=None)[source]#
\(P(\tau \le t)\), or \(P(t < \tau \le s)\) when
sis given.
- class financialkit.credit.TrancheResult(attachment, detachment, par_spread, expected_loss, times, protection_leg, rpv01)[source]#
Bases:
objectA synthetic CDO tranche priced in the one-factor Gaussian copula.
- Parameters:
- financialkit.credit.altman_ratios(working_capital, retained_earnings, ebit, market_equity, sales, total_assets, total_liabilities)[source]#
Altman’s five ratios \(X_1, \dots, X_5\), stacked on the last axis.
- Parameters:
working_capital (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.retained_earnings (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.ebit (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.market_equity (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.sales (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.total_assets (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.total_liabilities (
ArrayLike) – Balance-sheet and income-statement items, in the same currency unit.
- Return type:
- Returns:
ndarray, shape (…, 5)
- financialkit.credit.altman_z_score(working_capital, retained_earnings, ebit, market_equity, sales, total_assets, total_liabilities)[source]#
Altman’s (1968) Z-score for public manufacturers.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
A firm with total assets 670, working capital 170, retained earnings 300, EBIT 60, sales 2200, equity worth 380 and liabilities of 240:
>>> round(altman_z_score(170, 300, 60, 380, 2200, 670, 240), 2) 5.46
- financialkit.credit.altman_zone(z, lower=1.81, upper=2.99)[source]#
Classify Z-scores into Altman’s distress, grey and safe zones.
- Parameters:
- Return type:
- Returns:
str or ndarray of str
Examples
>>> altman_zone([1.2, 2.5, 3.4]).tolist() ['distress', 'grey', 'safe']
- financialkit.credit.basel_correlation(pd, asset_class='corporate', sales=None)[source]#
Supervisory asset correlation \(R\) for an asset class.
"corporate"(also sovereign and bank): 12% to 24%, interpolated with weight \((1 - e^{-50 PD})/(1 - e^{-50})\), less the SME adjustment \(0.04 (1 - (S - 5)/45)\) when annualsales\(S\) (EUR millions, clipped to 5-50) are given;"mortgage": 15%;"revolving": 4%;"retail"(other retail): 3% to 16% with weight factor 35.
- financialkit.credit.basel_irb_capital(pd, lgd=0.45, maturity=2.5, asset_class='corporate', sales=None, alpha=0.999, pd_floor=0.0003)[source]#
IRB capital requirement \(K\) per unit of exposure at default.
Retail classes have no maturity adjustment. A defaulted exposure (
pd = 1) gets zero unexpected-loss capital.- Parameters:
pd (
ArrayLike) – One-year default probability (floored at 0.03%).lgd (
ArrayLike) – Loss given default (45% is the foundation-IRB senior unsecured value).maturity (
ArrayLike) – Effective maturity in years.asset_class (
str)sales (
ArrayLike|None) – Annual sales in EUR millions for the SME correlation adjustment.alpha (
float) – Confidence level, 99.9% in the framework.pd_floor (
float)
- Return type:
- Returns:
float or ndarray
Examples
A corporate loan with PD 1%, LGD 45% and M = 2.5:
>>> round(12.5 * basel_irb_capital(0.01), 4) 0.9232
- financialkit.credit.basel_maturity_adjustment(pd, maturity=2.5)[source]#
Maturity adjustment \((1 + (M - 2.5) b) / (1 - 1.5 b)\), with \(b = (0.11852 - 0.05478 \ln PD)^2\).
- financialkit.credit.basel_risk_weight(pd, lgd=0.45, maturity=2.5, asset_class='corporate', sales=None, scaling=1.0)[source]#
Risk weight \(12.5 \cdot K\) (times the 1.06 scaling factor if passed).
- Parameters:
pd (
ArrayLike) – As inbasel_irb_capital().lgd (
ArrayLike) – As inbasel_irb_capital().maturity (
ArrayLike) – As inbasel_irb_capital().asset_class (
str) – As inbasel_irb_capital().sales (
ArrayLike|None) – As inbasel_irb_capital().scaling (
float) – The framework multiplies IRB risk-weighted assets by 1.06; the Committee’s illustrative tables use 1.
- Return type:
- Returns:
float or ndarray
Examples
Basel II framework, illustrative IRB risk weights (corporate, LGD 45%, M = 2.5):
>>> [round(100 * basel_risk_weight(p), 2) for p in (0.0003, 0.01)] [14.44, 92.32]
- financialkit.credit.black_cox_default_probability(V, K, t, mu, sigma, gamma=0.0, T=None)[source]#
First-passage default probability,
1 - black_cox_survival(...).- Parameters:
V (
ArrayLike) – As inblack_cox_survival().K (
ArrayLike) – As inblack_cox_survival().t (
ArrayLike) – As inblack_cox_survival().mu (
ArrayLike) – As inblack_cox_survival().sigma (
ArrayLike) – As inblack_cox_survival().gamma (
float) – As inblack_cox_survival().T (
ArrayLike|None) – As inblack_cox_survival().
- Return type:
- Returns:
float or ndarray
- financialkit.credit.black_cox_survival(V, K, t, mu, sigma, gamma=0.0, T=None)[source]#
Probability that assets stay above the Black-Cox barrier up to time
t.- Parameters:
V (
ArrayLike) – Current asset value (above the barrier).K (
ArrayLike) – Barrier level atT.t (
ArrayLike) – Horizon.mu (
ArrayLike) – Asset drift (r - payoutunder the pricing measure).sigma (
ArrayLike) – Asset volatility.gamma (
float) – Growth rate of the barrier;0gives a flat barrierK.T (
ArrayLike|None) – Date at which the barrier equalsK; defaults tot.
- Return type:
- Returns:
float or ndarray
Examples
With zero log-drift, the reflection principle doubles the probability of ending below the barrier:
>>> q = black_cox_survival(100.0, 70.0, 1.0, 0.02, 0.2) >>> p_end = float(stats.norm.cdf(np.log(70 / 100) / 0.2)) >>> round(1 - q, 10) == round(2 * p_end, 10) True
- financialkit.credit.bootstrap_hazard_curve(maturities, spreads, r, recovery=0.4, frequency=4, steps_per_year=52)[source]#
Bootstrap a piecewise-constant hazard curve from CDS par spreads.
Solves the maturities in increasing order with
scipy.optimize.brentq(), each new hazard segment chosen so the CDS to that maturity reprices at its quote while the earlier segments stay fixed, as in the ISDA standard model.- Parameters:
maturities (
ArrayLike) – Increasing CDS maturities, e.g.[1, 3, 5, 7, 10].spreads (
ArrayLike) – Par spreads.r (
float) – As incds_legs().recovery (
float) – As incds_legs().frequency (
int) – As incds_legs().steps_per_year (
int) – As incds_legs().
- Return type:
- Returns:
HazardCurve
Examples
>>> c = bootstrap_hazard_curve([1, 3, 5], [0.006, 0.009, 0.012], 0.03) >>> [round(1e4 * cds_par_spread(c, T, 0.03), 6) for T in (1, 3, 5)] [60.0, 90.0, 120.0]
- financialkit.credit.cds_legs(curve, maturity, r, recovery=0.4, frequency=4, steps_per_year=52)[source]#
Value both legs of a CDS per unit notional, starting today.
- Parameters:
curve (
SurvivalCurve) – Risk-neutral survival curve of the reference entity.maturity (
float) – Years to maturity.r (
float) – Flat continuously compounded risk-free rate.recovery (
float) – Recovery rate \(R\).frequency (
int) – Premium payments per year (4 for the standard quarterly schedule).steps_per_year (
int) – Grid for the default integrals; each premium period gets at least one step.
- Return type:
- Returns:
CDSResult
Examples
Hull’s (10th ed., Section 25.2) setting: hazard 2%, recovery 40%, rate 5%, annual payments, defaults half-way through each year.
>>> res = cds_legs(HazardCurve.flat(0.02), 5.0, 0.05, 0.4, frequency=1, steps_per_year=1) >>> round(res.rpv01 - res.accrued_on_default, 4), round(res.protection_leg, 4) (4.0728, 0.0506) >>> round(1e4 * res.par_spread, 1) 123.0
- financialkit.credit.cds_par_spread(curve, maturity, r, recovery=0.4, frequency=4, steps_per_year=52)[source]#
Par spread of a CDS,
protection_leg / rpv01.- Parameters:
curve (
SurvivalCurve) – As incds_legs().maturity (
float) – As incds_legs().r (
float) – As incds_legs().recovery (
float) – As incds_legs().frequency (
int) – As incds_legs().steps_per_year (
int) – As incds_legs().
- Return type:
- Returns:
float
Examples
The credit triangle \(s = \lambda(1 - R)\) is the continuous-premium limit:
>>> round(1e4 * cds_par_spread(HazardCurve.flat(0.02), 5.0, 0.05, 0.4, frequency=365, steps_per_year=365), 1) 120.0
- financialkit.credit.cds_upfront(curve, maturity, coupon, r, recovery=0.4, frequency=4, steps_per_year=52)[source]#
Upfront paid by the protection buyer of a fixed-coupon CDS, per unit notional.
protection_leg - coupon * rpv01; negative when the buyer is paid.- Parameters:
curve (
SurvivalCurve) – As incds_legs().maturity (
float) – As incds_legs().r (
float) – As incds_legs().recovery (
float) – As incds_legs().frequency (
int) – As incds_legs().steps_per_year (
int) – As incds_legs().coupon (
float) – Standard running coupon, e.g.0.01or0.05.
- Return type:
- Returns:
float
- financialkit.credit.check_transition_matrix(P, atol=1e-08)[source]#
Validate a stochastic matrix: square, non-negative, rows summing to one.
- financialkit.credit.conditional_default_probability(p, rho, M)[source]#
Default probability given the common factor, \(p(M)\).
- financialkit.credit.copula_default_times(curves, rho, n, n_names=None, df=None, rng=None)[source]#
Simulate correlated default times with a Gaussian (or t) copula.
- Parameters:
curves (
SurvivalCurve|Sequence[SurvivalCurve]) – One curve per name, or one curve shared byn_namesnames.rho (
float|ArrayLike) – Common pairwise correlation or a full correlation matrix.n (
int) – Number of scenarios.n_names (
int|None) – Number of names when a single curve is given.df (
float|None) – Degrees of freedom of a t copula; Gaussian if omitted.
- Return type:
- Returns:
ndarray, shape (n, d) – Default times (
inffor a name with zero hazard that never defaults).
- financialkit.credit.creditmetrics_bbb_values()[source]#
One-year forward values of CreditMetrics’ five-year 6% BBB bond in each rating.
- Return type:
- Returns:
ndarray, shape (8,) – Values per 100 face, ordered as
RATINGS(default value is the mean senior-unsecured recovery, 51.13).
- financialkit.credit.creditmetrics_bond(probabilities, values)[source]#
Value distribution of one bond over its possible ratings in a year.
- Parameters:
- Return type:
- Returns:
CreditMetricsResult
Examples
CreditMetrics Technical Document, Chapter 3: the five-year BBB bond has mean value 107.09 and standard deviation 2.99.
>>> from financialkit.credit.utils.matrices import creditmetrics_bbb_values, creditmetrics_matrix >>> res = creditmetrics_bond(creditmetrics_matrix(normalize=False)[3], creditmetrics_bbb_values()) >>> round(res.mean, 2), round(res.std, 2) (107.09, 2.99)
- financialkit.credit.creditmetrics_matrix(normalize=True)[source]#
The CreditMetrics one-year rating transition matrix.
- Parameters:
normalize (
bool) – Rescale rows to sum to one exactly (the published percentages are rounded, and two rows sum to 99.99% and 100.01%).- Return type:
- Returns:
ndarray, shape (8, 8) – Row
iholds the probabilities of moving fromRATINGS[i]to each rating.
Examples
>>> P = creditmetrics_matrix() >>> round(float(P[3, 7]), 4) 0.0018
- financialkit.credit.creditmetrics_portfolio(P, ratings, values, rho, n_sims, rng=None)[source]#
Monte Carlo CreditMetrics value distribution of a bond portfolio.
- Parameters:
- Return type:
- Returns:
CreditMetricsResult – Equally weighted portfolio-value scenarios.
- financialkit.credit.creditrisk_plus(exposures, pds, sigma=0.0, unit=1.0, n_max=None)[source]#
Single-sector CreditRisk+ loss distribution.
- Parameters:
exposures (
ArrayLike) – Loss given default of each obligor, in currency.pds (
ArrayLike) – Default probabilities (used as Poisson rates).sigma (
float) – Relative standard deviation of the sector default rate;0for independent (Poisson) defaults.unit (
float) – Loss unit; exposures are rounded to multiples of it (at least one unit).n_max (
int|None) – Largest loss, in units; defaults to the mean plus 15 standard deviations.
- Return type:
- Returns:
LossDistribution – Losses in currency, on the grid
unit * arange(n_max + 1).
Examples
The expected loss is the sum of rounded exposures times default probabilities:
>>> res = creditrisk_plus([100.0, 200.0, 300.0], [0.01, 0.02, 0.03], sigma=0.5, unit=100.0) >>> round(res.mean, 4) 14.0
- financialkit.credit.default_correlation(p1, p2, rho)[source]#
Correlation of the two default indicators implied by copula correlation
rho.
- financialkit.credit.distance_to_default(V, D, mu, sigma_V, T=1.0)[source]#
Crosbie and Bohn’s distance to default under the physical drift.
\[DD = \frac{\ln(V/D) + (\mu - \sigma_V^2/2) T}{\sigma_V\sqrt{T}}\]With
mu = rit is Merton’s \(d_2\).- Parameters:
V (
ArrayLike) – Asset value, default point, asset drift, asset volatility and horizon.D (
ArrayLike) – Asset value, default point, asset drift, asset volatility and horizon.mu (
ArrayLike) – Asset value, default point, asset drift, asset volatility and horizon.sigma_V (
ArrayLike) – Asset value, default point, asset drift, asset volatility and horizon.T (
ArrayLike) – Asset value, default point, asset drift, asset volatility and horizon.
- Return type:
- Returns:
float or ndarray
Examples
>>> round(distance_to_default(100.0, 60.0, 0.08, 0.25, 1.0), 4) 2.2383
- financialkit.credit.duffie_singleton_cir_zero(T, r, lambda0, kappa, theta, sigma, recovery=0.4)[source]#
Recovery-of-market-value bond with a CIR default intensity and a flat riskless rate.
The loss-adjusted intensity \(L\lambda\) (with \(L = 1 - R\)) follows a CIR process with long-run mean \(L\theta\) and volatility \(\sigma\sqrt{L}\), so
\[P^d(0, T) = e^{-rT} A(T) e^{-B(T) L \lambda_0},\]with \(A, B\) from
financialkit.interest_rate_models.CIRModel.- Parameters:
T (
ArrayLike) – Maturities.r (
float) – Flat risk-free rate.lambda0 (
float) – Initial intensity, mean-reversion speed, long-run mean and volatility of the intensity.kappa (
float) – Initial intensity, mean-reversion speed, long-run mean and volatility of the intensity.theta (
float) – Initial intensity, mean-reversion speed, long-run mean and volatility of the intensity.sigma (
float) – Initial intensity, mean-reversion speed, long-run mean and volatility of the intensity.recovery (
float) – Recovery as a fraction of market value.
- Return type:
- Returns:
float or ndarray
Examples
With the intensity starting at its mean and almost no volatility, the bond is the constant-hazard one:
>>> a = duffie_singleton_cir_zero(5.0, 0.05, 0.02, 1.0, 0.02, 1e-3, 0.4) >>> b = risky_zero_coupon(5.0, 0.05, 0.02, 0.4, "rmv") >>> abs(a - b) < 1e-6 True
- financialkit.credit.expected_tranche_loss(attachment, detachment, p, rho, recovery=0.4, n_names=None, n_nodes=96)[source]#
Expected tranche loss (fraction of tranche notional) at one horizon.
- Parameters:
- Return type:
- Returns:
float
Examples
A [0, 100%] tranche loses the pool’s expected loss whatever the correlation:
>>> round(expected_tranche_loss(0.0, 1.0, 0.05, 0.3, 0.4), 10) 0.03
- financialkit.credit.first_passage_crossing_probability(paths, barrier, sigma, dt)[source]#
Brownian-bridge probability that each discretely sampled path crossed the barrier.
Between two log-asset values above a log barrier \(b\) a Brownian bridge crosses with probability \(\exp(-2 (x_i - b)(x_{i+1} - b) / (\sigma^2 \Delta t))\), which removes the bias of only checking the barrier on the grid.
- financialkit.credit.generator_from_transition(P, t=1.0)[source]#
Candidate generator \(\log(P) / t\) (principal matrix logarithm).
The result has zero row sums but may have negative off-diagonal entries; see
regularize_generator().
- financialkit.credit.hazard_from_spread(spread, recovery=0.4)[source]#
Credit triangle: the hazard rate implied by a spread, \(\lambda = s / (1 - R)\).
Exact for a continuously paid CDS premium and a flat hazard and rate.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(hazard_from_spread(0.012, 0.4), 6) 0.02
- financialkit.credit.is_valid_generator(Q, atol=1e-12)[source]#
Whether
Qhas zero row sums and non-negative off-diagonal entries.
- financialkit.credit.jarrow_turnbull_zero(T, r, curve, recovery=0.0)[source]#
Defaultable zero-coupon bond under recovery of treasury.
\[P^d(0, T) = e^{-rT}\,[\delta + (1 - \delta) Q(T)]\]- Parameters:
T (
ArrayLike) – Maturities.r (
ArrayLike) – Flat risk-free rate.curve (
SurvivalCurve) – Risk-neutral survival curve.recovery (
float) – Fraction \(\delta\) of the riskless bond paid at default.
- Return type:
- Returns:
float or ndarray
Examples
>>> round(jarrow_turnbull_zero(5.0, 0.05, HazardCurve.flat(0.02), 0.4), 6) 0.734333
- financialkit.credit.jlt_risky_zero(T, r, Q, rating, recovery=0.4, premia=None)[source]#
Jarrow-Lando-Turnbull zero-coupon bond of an issuer with a given rating.
Under the pricing measure the generator is \(\tilde Q = U Q\) with \(U = \operatorname{diag}(\mu)\) the risk premia (
premia, ones if omitted). With recovery of treasury \(\delta\),\[P^d(0, T) = e^{-rT}\,[\delta + (1 - \delta)(1 - \tilde P_{i,D}(T))].\]- Parameters:
- Return type:
- Returns:
float or ndarray
- financialkit.credit.joint_default_probability(p1, p2, rho)[source]#
Probability that both names default by the horizon under a Gaussian copula.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(joint_default_probability(0.05, 0.05, 0.0), 6) 0.0025
- financialkit.credit.joint_migration_probabilities(row1, row2, rho)[source]#
Joint one-year migration probabilities of two obligors with asset correlation
rho.Each cell is a bivariate-normal rectangle probability between the two obligors’ thresholds, computed with
scipy.stats.multivariate_normal.
- financialkit.credit.kmv_default_point(short_term_debt, long_term_debt, weight=0.5)[source]#
Moody’s KMV default point: short-term debt plus half of long-term debt.
- financialkit.credit.kmv_iterate(equity, D, r, T=1.0, dt=0.003968253968253968, sigma0=None, rtol=1e-08, max_iter=1000)[source]#
Crosbie and Bohn’s (2003) iterative estimate of asset values and volatility.
Start from a guess of \(\sigma_V\), invert the Merton equity equation each day for \(V_t\) (Newton’s method, vectorised over days), re-estimate \(\sigma_V\) from the implied asset log returns, and repeat until it stops changing. The drift is the mean log return plus \(\sigma_V^2/2\).
- Parameters:
equity (
ArrayLike) – Equity market values, one per period.D (
ArrayLike) – Default point(s), scalar or one per period.r (
float) – Risk-free rate.T (
float) – Horizon of the distance to default (and the option maturity).dt (
float) – Spacing of the observations in years.sigma0 (
float|None) – Starting asset volatility; defaults to equity volatility timesE / (E + D).rtol (
float) – Relative change in \(\sigma_V\) that stops the iteration.max_iter (
int)
- Return type:
- Returns:
KMVResult
- financialkit.credit.leland_model(V, coupon, r, sigma, tax_rate=0.35, bankruptcy_cost=0.5, default_barrier=None)[source]#
Debt, equity and firm value in Leland’s (1994) model.
- Parameters:
V (
float) – Unlevered asset value.coupon (
float) – Perpetual coupon per year.r (
float) – Risk-free rate.sigma (
float) – Asset volatility.tax_rate (
float) – Corporate tax rate \(\tau\).bankruptcy_cost (
float) – Fraction \(\alpha\) of assets lost in bankruptcy.default_barrier (
float|None) – Exogenous barrier; the endogenous \(V_B\) if omitted.
- Return type:
- Returns:
LelandResult
Examples
At the endogenous barrier equity is exactly worthless:
>>> res = leland_model(100.0, 5.0, 0.06, 0.2) >>> round(res.default_barrier, 4) 40.625 >>> leland_model(res.default_barrier, 5.0, 0.06, 0.2).equity < 1e-12 True
- financialkit.credit.leland_optimal_coupon(V, r, sigma, tax_rate=0.35, bankruptcy_cost=0.5)[source]#
The coupon that maximises firm value, by
scipy.optimize.minimize_scalar().- Parameters:
V (
float) – As inleland_model().r (
float) – As inleland_model().sigma (
float) – As inleland_model().tax_rate (
float) – As inleland_model().bankruptcy_cost (
float) – As inleland_model().
- Return type:
- Returns:
coupon (float)
result (LelandResult) – The firm at the optimal coupon.
- financialkit.credit.linear_discriminant(x_good, x_bad)[source]#
Fisher’s linear discriminant between two groups of ratio vectors.
The weights \(w = S_W^{-1}(\bar x_g - \bar x_b)\) maximise the separation of the group means relative to the pooled within-group covariance \(S_W\); the cutoff is the midpoint of the projected means (equal priors). Weights are only defined up to scale.
- financialkit.credit.merton_calibrate(E, sigma_E, D, T, r)[source]#
Solve for the asset value and volatility implied by equity and its volatility.
Solves \(E = C_{BS}(V)\) and \(\sigma_E E = N(d_1)\sigma_V V\) with
scipy.optimize.least_squares().- Parameters:
- Return type:
- Returns:
MertonResult
Examples
Hull, 10th ed., Example 24.3:
>>> m = merton_calibrate(3.0, 0.80, 10.0, 1.0, 0.05) >>> round(m.asset_value, 2), round(m.asset_volatility, 4), round(m.debt, 2) (12.4, 0.2123, 9.4)
- financialkit.credit.merton_model(V, D, T, r, sigma_V)[source]#
Value equity and debt in Merton’s (1974) model.
- Parameters:
- Return type:
- Returns:
MertonResult
Examples
Hull, 10th ed., Example 24.3: assets 12.40, volatility 21.23%, debt of 10 due in a year, 5% rate.
>>> m = merton_model(12.3954, 10.0, 1.0, 0.05, 0.2123) >>> round(m.equity, 2), round(m.default_probability, 3) (3.0, 0.127)
- financialkit.credit.migration_thresholds(row)[source]#
Asset-return thresholds that reproduce one row of a transition matrix.
States are ordered best first and default last, as in the matrix. An asset return \(Z\) below
thresholds[-1]means default, betweenthresholds[j]andthresholds[j - 1]means ratingj.- Parameters:
row (
ArrayLike) – Migration probabilities from the current rating.- Return type:
- Returns:
ndarray, shape (k + 1,) – Decreasing thresholds, from
+infdown to-inf.
Examples
A BBB obligor defaults below about -2.91 standard deviations:
>>> from financialkit.credit.utils.matrices import creditmetrics_matrix >>> round(float(migration_thresholds(creditmetrics_matrix()[3])[-2]), 2) -2.91
- financialkit.credit.normal_factor_quadrature(n=64)[source]#
Gauss-Hermite nodes and weights for \(E[g(M)]\), \(M \sim N(0, 1)\).
- Parameters:
n (
int) – Number of nodes.- Return type:
- Returns:
nodes, weights (ndarray, shape (n,)) – Weights sum to one.
Examples
>>> x, w = normal_factor_quadrature(20) >>> round(float(w @ x**4), 10) 3.0
- financialkit.credit.nth_to_default_probability(times, k, T)[source]#
Probability that at least
knames default byT, from simulated default times.
- financialkit.credit.one_factor_loss_distribution(p, rho, n_obligors, lgd=1.0, n_nodes=128)[source]#
Exact loss distribution of a finite homogeneous portfolio, by factor quadrature.
\(P(K = k) = E_M[\binom{n}{k} p(M)^k (1 - p(M))^{n - k}]\), the expectation over \(M\) computed with Gauss-Hermite quadrature.
- financialkit.credit.panjer_recursion(severity, a, b, f0, n_max)[source]#
Aggregate-loss probabilities of an (a, b, 0) claim count with integer severities.
- Parameters:
severity (
ArrayLike) –severity[j]is the probability that one claim has sizej.a (
float) – Panjer class parameters: Poisson(0, mu), negative binomial(p, (alpha - 1) p)with success probability1 - p.b (
float) – Panjer class parameters: Poisson(0, mu), negative binomial(p, (alpha - 1) p)with success probability1 - p.f0 (
float) – Probability of a zero aggregate loss.n_max (
int) – Largest aggregate loss computed.
- Return type:
- Returns:
ndarray, shape (n_max + 1,)
Examples
A Poisson count with unit claims is just the Poisson law:
>>> from scipy import stats >>> f = panjer_recursion([0.0, 1.0], 0.0, 2.0, float(np.exp(-2.0)), 10) >>> np.allclose(f, stats.poisson.pmf(np.arange(11), 2.0)) True
- financialkit.credit.regularize_generator(Q, method='diagonal')[source]#
Turn a candidate generator into a valid one.
"diagonal"(Israel, Rosenthal and Wei 2001): set negative off-diagonal entries to zero and absorb the change in the diagonal."weighted"(Kreinin and Sidelnikova 2001): set them to zero and take the excess from every remaining non-zero entry of the row in proportion to its absolute value.
- financialkit.credit.risky_zero_coupon(T, r, hazard, recovery=0.4, convention='rmv')[source]#
Defaultable zero-coupon bond with a constant hazard rate, under three recovery conventions.
"rmv"(recovery of market value): \(e^{-(r + \lambda (1 - R)) T}\);"rt"(recovery of treasury): \(e^{-rT}[R + (1 - R) e^{-\lambda T}]\);"rfv"(recovery of face value, paid at default): \(e^{-(r + \lambda) T} + R \frac{\lambda}{r + \lambda}(1 - e^{-(r + \lambda) T})\).
- Parameters:
T (
ArrayLike) – Maturity, flat risk-free rate, constant intensity and recovery fraction.r (
ArrayLike) – Maturity, flat risk-free rate, constant intensity and recovery fraction.hazard (
ArrayLike) – Maturity, flat risk-free rate, constant intensity and recovery fraction.recovery (
ArrayLike) – Maturity, flat risk-free rate, constant intensity and recovery fraction.convention (
str)
- Return type:
- Returns:
float or ndarray
Examples
With no recovery the three conventions agree:
>>> [round(risky_zero_coupon(5.0, 0.05, 0.02, 0.0, c), 6) for c in ("rmv", "rt", "rfv")] [0.704688, 0.704688, 0.704688]
- financialkit.credit.simulate_migrations(P, ratings, rho, n_sims, rng=None)[source]#
Simulate end-of-year ratings with one-factor Gaussian asset correlation.
Asset returns are \(Z_i = \sqrt\rho\, M + \sqrt{1 - \rho}\,\varepsilon_i\).
- financialkit.credit.simulate_one_factor_defaults(p, rho, n_obligors, n_sims, rng=None)[source]#
Number of defaults per scenario in a homogeneous one-factor portfolio.
Draws the factor, then the conditionally independent defaults as a binomial count.
- financialkit.credit.spread_from_hazard(hazard, recovery=0.4)[source]#
Inverse credit triangle, \(s = \lambda (1 - R)\).
- financialkit.credit.tranche_loss(L, attachment, detachment)[source]#
Tranche loss as a fraction of tranche notional.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> tranche_loss([0.01, 0.05, 0.2], 0.03, 0.07).tolist() [0.0, 0.5, 1.0]
- financialkit.credit.tranche_spread(attachment, detachment, curve, rho, maturity, r, recovery=0.4, frequency=4, n_names=None)[source]#
Par spread of a synthetic CDO tranche (Hull and White 2004).
Premiums accrue on the expected outstanding tranche notional, averaged over each period, and protection pays the increase in expected tranche loss, discounted from mid-period.
- Parameters:
attachment (
float)detachment (
float)curve (
SurvivalCurve) – Survival curve shared by every name in the pool.rho (
float) – Copula correlation.maturity (
float)r (
float) – Flat risk-free rate.recovery (
float)frequency (
int) – Premium payments per year.n_names (
int|None) – Pool size; the large homogeneous pool if omitted.
- Return type:
- Returns:
TrancheResult
- financialkit.credit.transition_from_generator(Q, t=1.0)[source]#
Transition matrix \(P(t) = e^{Qt}\) by
scipy.linalg.expm().- Parameters:
- Return type:
- Returns:
ndarray, shape (k, k) or (n_t, k, k)
Examples
>>> Q = np.array([[-0.1, 0.1], [0.0, 0.0]]) >>> bool(np.isclose(transition_from_generator(Q, 2.0)[0, 1], 1 - np.exp(-0.2))) True
- financialkit.credit.upfront_to_spread(upfront, coupon, maturity, r, recovery=0.4, frequency=4, steps_per_year=52)[source]#
ISDA quote conversion: the flat-hazard par spread equivalent to an upfront.
Finds the flat hazard rate that reproduces
upfrontatcouponand returns that curve’s par spread.- Parameters:
upfront (
float) – Upfront per unit notional.coupon (
float) – As incds_upfront().maturity (
float) – As incds_upfront().r (
float) – As incds_upfront().recovery (
float) – As incds_upfront().frequency (
int) – As incds_upfront().steps_per_year (
int) – As incds_upfront().
- Return type:
- Returns:
float
- financialkit.credit.vasicek_cdf(x, p, rho)[source]#
Distribution function of the large-portfolio default fraction.
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
The median default rate lies below the mean
p(the law is right-skewed):>>> vasicek_cdf(0.02, 0.02, 0.2) > 0.5 True
- financialkit.credit.vasicek_pdf(x, p, rho)[source]#
Density of the large-portfolio default fraction.
- financialkit.credit.vasicek_quantile(alpha, p, rho)[source]#
Quantile of the large-portfolio default fraction (the worst-case default rate).
\[N\left(\frac{N^{-1}(p) + \sqrt\rho\, N^{-1}(\alpha)}{\sqrt{1 - \rho}}\right)\]- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> x = vasicek_quantile(0.999, 0.01, 0.2) >>> round(vasicek_cdf(x, 0.01, 0.2), 10) 0.999