use copula_core::prelude::*;
use nalgebra::DMatrix;
fn main() -> copula_core::Result<()> {
println!("=== Copulas in Risk Management ===\n");
let mut rng = rand::rng();
let n_scenarios = 10000;
println!("Scenario 1: Portfolio Risk Analysis");
println!(" Two assets with different dependence structures\n");
println!(" 1a. Clayton Copula (θ = 4.0) - Lower tail dependence");
let clayton = ClaytonCopula::new(4.0)?;
let clayton_samples = clayton.sample(n_scenarios, &mut rng)?;
println!(" 1b. Gumbel Copula (θ = 3.0) - Upper tail dependence\n");
let gumbel = GumbelCopula::new(3.0)?;
let gumbel_samples = gumbel.sample(n_scenarios, &mut rng)?;
println!(" Analyzing tail events (extreme losses):");
let threshold = 0.05;
let mut clayton_joint_extreme = 0;
let mut gumbel_joint_extreme = 0;
for i in 0..n_scenarios {
if clayton_samples[(i, 0)] < threshold && clayton_samples[(i, 1)] < threshold {
clayton_joint_extreme += 1;
}
if gumbel_samples[(i, 0)] < threshold && gumbel_samples[(i, 1)] < threshold {
gumbel_joint_extreme += 1;
}
}
let clayton_joint_pct = 100.0 * clayton_joint_extreme as f64 / n_scenarios as f64;
let gumbel_joint_pct = 100.0 * gumbel_joint_extreme as f64 / n_scenarios as f64;
println!(" Joint extreme losses (both < 5% quantile):");
println!(" Clayton: {:.2}% of scenarios", clayton_joint_pct);
println!(" Gumbel: {:.2}% of scenarios", gumbel_joint_pct);
println!(
" Clayton shows {} higher joint crash risk!\n",
if clayton_joint_pct > gumbel_joint_pct {
"SIGNIFICANTLY"
} else {
"moderately"
}
);
println!("Scenario 2: Value-at-Risk (VaR) Computation");
println!(" Equal-weighted portfolio of two assets\n");
use statrs::distribution::{ContinuousCDF, Normal};
let normal = Normal::new(0.0, 1.0).unwrap();
let mut clayton_portfolio_returns = Vec::with_capacity(n_scenarios);
for i in 0..n_scenarios {
let r1 = normal.inverse_cdf(clayton_samples[(i, 0)]);
let r2 = normal.inverse_cdf(clayton_samples[(i, 1)]);
let portfolio_return = 0.5 * r1 + 0.5 * r2; clayton_portfolio_returns.push(portfolio_return);
}
clayton_portfolio_returns.sort_by(|a, b| a.partial_cmp(b).unwrap());
let var_95_idx = (0.05 * n_scenarios as f64) as usize;
let var_99_idx = (0.01 * n_scenarios as f64) as usize;
let var_95 = -clayton_portfolio_returns[var_95_idx];
let var_99 = -clayton_portfolio_returns[var_99_idx];
println!(" Portfolio VaR (lower tail dependence model):");
println!(
" 95% VaR: {:.4} (5% chance of loss exceeding this)",
var_95
);
println!(
" 99% VaR: {:.4} (1% chance of loss exceeding this)\n",
var_99
);
println!("Scenario 3: Stress Testing");
println!(" What if dependence suddenly increases during a crisis?\n");
let normal_copula = ClaytonCopula::new(1.5)?;
let crisis_copula = ClaytonCopula::new(6.0)?;
println!(" Normal market conditions (θ = 1.5):");
let normal_samples = normal_copula.sample(n_scenarios, &mut rng)?;
let mut normal_joint_extreme = 0;
for i in 0..n_scenarios {
if normal_samples[(i, 0)] < threshold && normal_samples[(i, 1)] < threshold {
normal_joint_extreme += 1;
}
}
println!(
" Joint extreme events: {:.2}%",
100.0 * normal_joint_extreme as f64 / n_scenarios as f64
);
println!(" Crisis scenario (θ = 6.0):");
let crisis_samples = crisis_copula.sample(n_scenarios, &mut rng)?;
let mut crisis_joint_extreme = 0;
for i in 0..n_scenarios {
if crisis_samples[(i, 0)] < threshold && crisis_samples[(i, 1)] < threshold {
crisis_joint_extreme += 1;
}
}
println!(
" Joint extreme events: {:.2}%",
100.0 * crisis_joint_extreme as f64 / n_scenarios as f64
);
let crisis_multiplier = crisis_joint_extreme as f64 / normal_joint_extreme.max(1) as f64;
println!(
" Crisis increases joint crash risk by {:.1}x\n",
crisis_multiplier
);
println!("Scenario 4: Comparing Risk Models");
println!(" Different copulas → different risk assessments\n");
let gaussian_corr = DMatrix::from_row_slice(2, 2, &[1.0, 0.7, 0.7, 1.0]);
let gaussian = GaussianCopula::new(gaussian_corr)?;
let frank = FrankCopula::new(6.0)?;
println!(" Risk Comparison Table:");
println!(" ┌───────────────────┬──────────────┬──────────────┐");
println!(" │ Model │ Joint Extreme│ Conditional │");
println!(" │ │ (Both < 5%) │ Risk* │");
println!(" ├───────────────────┼──────────────┼──────────────┤");
for (name, samples) in [
("Clayton (θ=4)", clayton.sample(n_scenarios, &mut rng)?),
("Gaussian (ρ=0.7)", gaussian.sample(n_scenarios, &mut rng)?),
("Frank (θ=6)", frank.sample(n_scenarios, &mut rng)?),
] {
let mut joint_count = 0;
let mut conditional_count = 0;
let mut first_extreme_count = 0;
for i in 0..n_scenarios {
let u = samples[(i, 0)];
let v = samples[(i, 1)];
if u < threshold && v < threshold {
joint_count += 1;
}
if u < threshold {
first_extreme_count += 1;
if v < threshold {
conditional_count += 1;
}
}
}
let joint_pct = 100.0 * joint_count as f64 / n_scenarios as f64;
let cond_pct = if first_extreme_count > 0 {
100.0 * conditional_count as f64 / first_extreme_count as f64
} else {
0.0
};
println!(
" │ {:<17} │ {:>11.2}% │ {:>11.2}% │",
name, joint_pct, cond_pct
);
}
println!(" └───────────────────┴──────────────┴──────────────┘");
println!(" * P(Asset2 < 5% | Asset1 < 5%)\n");
println!("Scenario 5: Diversification Benefit");
println!(" Comparing concentrated vs. diversified portfolios\n");
let perfect_samples = gumbel.sample(1000, &mut rng)?; let mut perfect_portfolio = Vec::new();
for i in 0..1000 {
let r1 = normal.inverse_cdf(perfect_samples[(i, 0)]);
let r2 = normal.inverse_cdf(perfect_samples[(i, 1)]);
perfect_portfolio.push(0.5 * (r1 + r2));
}
perfect_portfolio.sort_by(|a, b| a.partial_cmp(b).unwrap());
let low_corr_matrix = DMatrix::from_row_slice(2, 2, &[1.0, 0.3, 0.3, 1.0]);
let low_corr = GaussianCopula::new(low_corr_matrix)?;
let low_corr_samples = low_corr.sample(1000, &mut rng)?;
let mut diversified_portfolio = Vec::new();
for i in 0..1000 {
let r1 = normal.inverse_cdf(low_corr_samples[(i, 0)]);
let r2 = normal.inverse_cdf(low_corr_samples[(i, 1)]);
diversified_portfolio.push(0.5 * (r1 + r2));
}
diversified_portfolio.sort_by(|a, b| a.partial_cmp(b).unwrap());
let perfect_var = -perfect_portfolio[(0.05 * 1000.0) as usize];
let diversified_var = -diversified_portfolio[(0.05 * 1000.0) as usize];
println!(" High correlation portfolio:");
println!(" 95% VaR: {:.4}", perfect_var);
println!(" Diversified portfolio:");
println!(" 95% VaR: {:.4}", diversified_var);
println!(
" Diversification benefit: {:.1}% reduction in VaR\n",
100.0 * (perfect_var - diversified_var) / perfect_var
);
println!("✓ Risk management analysis completed!\n");
println!("Key Insights:");
println!(" • Clayton copulas model joint crashes (contagion risk)");
println!(" • Gumbel copulas model joint booms");
println!(" • Gaussian copulas may underestimate tail risk");
println!(" • Copula choice significantly impacts VaR estimates");
println!(" • Stress testing reveals hidden tail dependencies\n");
Ok(())
}