use stochastic_rs_distributions::special::norm_cdf;
#[derive(Debug, Clone)]
pub struct MertonStructural {
pub asset_value: f64,
pub debt_face_value: f64,
pub asset_volatility: f64,
pub risk_free_rate: f64,
pub asset_payout: f64,
}
impl MertonStructural {
pub fn new(
asset_value: f64,
debt_face_value: f64,
asset_volatility: f64,
risk_free_rate: f64,
asset_payout: f64,
) -> Self {
assert!(asset_value > 0.0, "asset_value must be positive");
assert!(debt_face_value > 0.0, "debt_face_value must be positive");
assert!(asset_volatility > 0.0, "asset_volatility must be positive");
Self {
asset_value,
debt_face_value,
asset_volatility,
risk_free_rate,
asset_payout,
}
}
fn d1_d2(&self, tau: f64) -> (f64, f64) {
assert!(tau > 0.0, "tau must be positive");
let sigma_sqrt_t = self.asset_volatility * tau.sqrt();
let d1 = ((self.asset_value / self.debt_face_value).ln()
+ (self.risk_free_rate - self.asset_payout + 0.5 * self.asset_volatility.powi(2)) * tau)
/ sigma_sqrt_t;
let d2 = d1 - sigma_sqrt_t;
(d1, d2)
}
pub fn risk_neutral_default_probability(&self, tau: f64) -> f64 {
let (_, d2) = self.d1_d2(tau);
norm_cdf(-d2)
}
pub fn real_world_default_probability(&self, tau: f64, asset_drift: f64) -> f64 {
assert!(tau > 0.0, "tau must be positive");
let sigma_sqrt_t = self.asset_volatility * tau.sqrt();
let dd = ((self.asset_value / self.debt_face_value).ln()
+ (asset_drift - self.asset_payout - 0.5 * self.asset_volatility.powi(2)) * tau)
/ sigma_sqrt_t;
norm_cdf(-dd)
}
pub fn survival_probability(&self, tau: f64) -> f64 {
1.0 - self.risk_neutral_default_probability(tau)
}
pub fn distance_to_default(&self, tau: f64) -> f64 {
self.d1_d2(tau).1
}
pub fn equity_value(&self, tau: f64) -> f64 {
let (d1, d2) = self.d1_d2(tau);
self.asset_value * (-self.asset_payout * tau).exp() * norm_cdf(d1)
- self.debt_face_value * (-self.risk_free_rate * tau).exp() * norm_cdf(d2)
}
pub fn debt_value(&self, tau: f64) -> f64 {
self.asset_value * (-self.asset_payout * tau).exp() - self.equity_value(tau)
}
pub fn credit_spread(&self, tau: f64) -> f64 {
assert!(tau > 0.0, "tau must be positive");
let b = self.debt_value(tau);
if b <= 0.0 {
return f64::INFINITY;
}
-(b / self.debt_face_value).ln() / tau - self.risk_free_rate
}
pub fn implied_recovery(&self, tau: f64) -> f64 {
let (d1, d2) = self.d1_d2(tau);
let denom = self.debt_face_value * norm_cdf(-d2);
if denom <= 0.0 {
return 0.0;
}
self.asset_value * ((self.risk_free_rate - self.asset_payout) * tau).exp() * norm_cdf(-d1)
/ denom
}
pub fn leverage(&self, tau: f64) -> f64 {
self.debt_face_value * (-self.risk_free_rate * tau).exp() / self.asset_value
}
}