Structured Credit Products (CDOs and Securitizations) › Worked Example: Analyzing a \$500M CDO
Michael BrenndoerferNovember 19, 20254 min read
Quantitative FinanceAll chapters Part 4 of 15Swaps and Credit Derivatives
Structured Credit Products (CDOs and Securitizations) › Worked Example: Analyzing a \$500M CDOOne section · Sections in this chapter8 / 12 ↓
Sections in this chapter12
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's work through a complete example of a cash CDO backed by 100 corporate loans.
In[29]:
Code# CDO Parameters
pool_size = 500_000_000 # $500 million
n_loans = 100
loan_size = pool_size / n_loans
avg_default_prob = 0.04 # 4% annual default probability
recovery_rate = 0.40
correlation = 0.25
# Tranche structure
# Defined with explicit attachment/detachment to ensure consistency
cdo_tranches = {
"Senior": {"coupon": 0.015, "attachment": 0.12, "detachment": 1.00},
"Mezzanine A": {"coupon": 0.04, "attachment": 0.07, "detachment": 0.12},
"Mezzanine B": {"coupon": 0.07, "attachment": 0.04, "detachment": 0.07},
"Equity": {"coupon": 0.0, "attachment": 0.00, "detachment": 0.04},
}
# Calculate notionals
for name, spec in cdo_tranches.items():
thickness = spec["detachment"] - spec["attachment"]
spec["notional"] = thickness * pool_sizeIn[30]:
Code# Run Monte Carlo simulation for 5-year horizon
n_simulations = 100000
years = 5
cumulative_default_prob = (
1 - (1 - avg_default_prob) ** years
) # ~18.5% over 5 years
defaults = simulate_correlated_defaults(
n_loans, cumulative_default_prob, correlation, n_simulations
)
pool_losses = (defaults / n_loans) * (
1 - recovery_rate
) # Loss rate after recovery
# Calculate tranche metrics
tranche_metrics = []
for name, spec in cdo_tranches.items():
attach = spec["attachment"]
detach = spec["detachment"]
thickness = detach - attach
# Tranche losses
tranche_loss_rates = np.clip(pool_losses - attach, 0, thickness) / thickness
# Expected loss
expected_loss = tranche_loss_rates.mean()
# Probability of any loss
prob_any_loss = (tranche_loss_rates > 0).mean()
# Probability of total loss
prob_total_loss = (tranche_loss_rates >= 0.999).mean()
tranche_metrics.append(
{
"Tranche": name,
"Notional ($M)": spec["notional"] / 1e6,
"Attachment": f"{attach * 100:.0f}%",
"Detachment": f"{detach * 100:.0f}%",
"Expected Loss (%)": expected_loss * 100,
"P(Any Loss)": prob_any_loss,
"P(Total Loss)": prob_total_loss,
}
)
metrics_df = pd.DataFrame(tranche_metrics)Out[31]:
ConsoleCDO Tranche Analysis - 5 Year Horizon
================================================================================
Pool Size: $500M | Loans: 100 | Correlation: 0.25
5-Year Default Prob: 18.5% | Recovery: 40%
--------------------------------------------------------------------------------
Tranche Notional ($M) Attachment Detachment Expected Loss (%) P(Any Loss) P(Total Loss)
Senior 440.0 12% 100% 3.471 0.360 0.000
Mezzanine A 25.0 7% 12% 47.829 0.604 0.383
Mezzanine B 15.0 4% 7% 68.445 0.779 0.604
Equity 20.0 0% 4% 89.172 0.983 0.779The simulation results quantify the risk segmentation. The Equity tranche, while offering potential upside, faces a high probability of total loss (approx. 60%). In contrast, the Senior tranche remains untouched in the vast majority of scenarios, protected by the subordination of the lower tranches.
Out[32]:
Visualization


