Assess credit risk and default probability for bonds using credit spreads, rating transitions, and recovery analysis. Requires numpy>=1.24.0, pandas>=2.0.0, scipy>=1.10.0. Use when evaluating corporate bonds, analyzing credit events, estimating default probabilities, or managing credit portfolio risk.
Scanned 2/12/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: credit-risk
description: Assess credit risk and default probability for bonds using credit spreads, rating transitions, and recovery analysis. Requires numpy>=1.24.0, pandas>=2.0.0, scipy>=1.10.0. Use when evaluating corporate bonds, analyzing credit events, estimating default probabilities, or managing credit portfolio risk.
---
# Credit Risk Assessment and Rating Analysis Skill Development
## Objective
Evaluate and model issuer default risk, downgrade probability, and credit spread behavior using both fundamental analysis and market-implied approaches. Develop systematic frameworks for assessing credit quality and monitoring credit migrations.
## Skill Classification
**Domain**: Fixed Income Credit Analysis
**Level**: Advanced
**Prerequisites**: Bond Pricing, Yield Measures
**Estimated Time**: 20-25 hours
## Focus Areas
### 1. Fundamental vs. Market-Implied Credit Risk
#### Fundamental Credit Analysis
**Approach**: Bottom-up analysis of issuer financials and business fundamentals.
**Key Metrics**:
```python
class CreditMetrics:
"""Fundamental credit analysis metrics."""
@staticmethod
def interest_coverage(ebit, interest_expense):
"""Times Interest Earned (TIE) ratio."""
return ebit / interest_expense
@staticmethod
def debt_to_ebitda(total_debt, ebitda):
"""Leverage ratio."""
return total_debt / ebitda
@staticmethod
def debt_to_equity(total_debt, total_equity):
"""Capital structure ratio."""
return total_debt / total_equity
@staticmethod
def current_ratio(current_assets, current_liabilities):
"""Liquidity ratio."""
return current_assets / current_liabilities
@staticmethod
def free_cash_flow_to_debt(fcf, total_debt):
"""Debt service capacity."""
return fcf / total_debt
```
#### Market-Implied Credit Risk
**Approach**: Extract default probability from market prices.
**Credit Spread**:
```
Credit Spread = YTM_Corporate - YTM_Treasury
```
**Implied Default Probability**:
```python
def implied_default_probability(credit_spread, recovery_rate, years):
"""
Calculate market-implied annual default probability.
Parameters:
-----------
credit_spread : float
Yield spread over risk-free rate (decimal)
recovery_rate : float
Expected recovery in default (decimal, e.g., 0.40 for 40%)
years : float
Time horizon
Returns:
--------
float : Implied annual default probability
"""
loss_given_default = 1 - recovery_rate
annual_pd = credit_spread / loss_given_default
return annual_pd
def cumulative_default_prob(annual_pd, years):
"""
Calculate cumulative default probability.
Returns:
--------
float : Probability of default within 'years' horizon
"""
survival_prob = (1 - annual_pd) ** years
return 1 - survival_prob
```
### 2. Credit Rating Agencies
**Major Rating Agencies**:
- **Moody's**: Aaa to C scale
- **S&P**: AAA to D scale
- **Fitch**: AAA to D scale
**Rating Categories**:
- **Investment Grade**: BBB-/Baa3 and above
- **High Yield (Junk)**: BB+/Ba1 and below
**Rating Equivalence**:
```python
RATING_SCALE = {
'Moody\'s': ['Aaa', 'Aa1', 'Aa2', 'Aa3', 'A1', 'A2', 'A3',
'Baa1', 'Baa2', 'Baa3', 'Ba1', 'Ba2', 'Ba3',
'B1', 'B2', 'B3', 'Caa1', 'Caa2', 'Caa3', 'Ca', 'C'],
'S&P': ['AAA', 'AA+', 'AA', 'AA-', 'A+', 'A', 'A-',
'BBB+', 'BBB', 'BBB-', 'BB+', 'BB', 'BB-',
'B+', 'B', 'B-', 'CCC+', 'CCC', 'CCC-', 'CC', 'C', 'D'],
'Fitch': ['AAA', 'AA+', 'AA', 'AA-', 'A+', 'A', 'A-',
'BBB+', 'BBB', 'BBB-', 'BB+', 'BB', 'BB-',
'B+', 'B', 'B-', 'CCC+', 'CCC', 'CCC-', 'CC', 'C', 'D']
}
def numerical_rating(rating, agency='S&P'):
"""Convert letter rating to numerical score (higher = better)."""
scale = RATING_SCALE[agency]
try:
return len(scale) - scale.index(rating)
except ValueError:
return None
def is_investment_grade(rating, agency='S&P'):
"""Determine if rating is investment grade."""
numeric = numerical_rating(rating, agency)
if agency == 'Moody\'s':
threshold = numerical_rating('Baa3', agency)
else:
threshold = numerical_rating('BBB-', agency)
return numeric >= threshold if numeric else False
```
**Historical Default Rates by Rating**:
```python
# Average 10-year cumulative default rates (%)
HISTORICAL_DEFAULT_RATES = {
'AAA': 0.60, 'AA': 1.50, 'A': 2.91,
'BBB': 7.20, 'BB': 19.00, 'B': 35.00,
'CCC': 54.00
}
```
### 3. Probability of Default (PD) and Loss Given Default (LGD)
**Expected Loss Framework**:
```
Expected Loss = PD × LGD × Exposure at Default
```
**PD Modeling Approaches**:
#### Structural Model (Merton Model)
```python
from scipy.stats import norm
def merton_default_probability(firm_value, debt_face_value,
volatility, risk_free_rate, time_horizon):
"""
Calculate default probability using Merton structural model.
Based on Black-Scholes option pricing framework.
Parameters:
-----------
firm_value : float
Current market value of firm's assets
debt_face_value : float
Face value of debt
volatility : float
Volatility of firm value (annual)
risk_free_rate : float
Risk-free rate
time_horizon : float
Time to debt maturity (years)
Returns:
--------
float : Probability of default
"""
d2 = (np.log(firm_value / debt_face_value) +
(risk_free_rate - 0.5 * volatility**2) * time_horizon) / \
(volatility * np.sqrt(time_horizon))
return norm.cdf(-d2)
```
#### Reduced-Form Model
```python
def hazard_rate_pd(hazard_rate, time_horizon):
"""
Calculate default probability from constant hazard rate.
Parameters:
-----------
hazard_rate : float
Instantaneous default intensity (annual)
time_horizon : float
Time period (years)
Returns:
--------
float : Cumulative default probability
"""
return 1 - np.exp(-hazard_rate * time_horizon)
```
**LGD Estimation**:
```python
class LGDEstimator:
"""Loss Given Default estimation."""
# Historical average recovery rates by seniority
RECOVERY_RATES = {
'Senior Secured': 0.65,
'Senior Unsecured': 0.45,
'Senior Subordinated': 0.35,
'Subordinated': 0.25,
'Junior Subordinated': 0.15
}
@classmethod
def lgd_from_seniority(cls, seniority):
"""Estimate LGD based on debt seniority."""
recovery = cls.RECOVERY_RATES.get(seniority, 0.40)
return 1 - recovery
@staticmethod
def lgd_from_collateral(debt_value, collateral_value,
liquidation_costs=0.20):
"""
Calculate LGD considering collateral.
Parameters:
-----------
debt_value : float
Outstanding debt amount
collateral_value : float
Market value of collateral
liquidation_costs : float
Costs of liquidating collateral (as fraction)
Returns:
--------
float : Loss given default
"""
net_recovery = collateral_value * (1 - liquidation_costs)
loss = max(0, debt_value - net_recovery)
return loss / debt_value
```
### 4. Credit Spreads and Yield Differentials
**Components of Credit Spread**:
```
Credit Spread = Expected Loss + Risk Premium + Liquidity Premium
```
**Spread Analysis**:
```python
def credit_spread_decomposition(corporate_yield, treasury_yield,
expected_loss_component):
"""
Decompose credit spread into components.
Parameters:
-----------
corporate_yield : float
YTM of corporate bond
treasury_yield : float
YTM of comparable Treasury
expected_loss_component : float
Expected loss (PD × LGD)
Returns:
--------
dict : Spread decomposition
"""
total_spread = corporate_yield - treasury_yield
risk_liquidity_premium = total_spread - expected_loss_component
return {
'total_spread': total_spread,
'expected_loss': expected_loss_component,
'risk_premium': risk_liquidity_premium * 0.60, # Estimate
'liquidity_premium': risk_liquidity_premium * 0.40 # Estimate
}
def option_adjusted_spread_credit(z_spread, option_cost):
"""
Calculate credit-adjusted OAS.
Parameters:
-----------
z_spread : float
Zero-volatility spread
option_cost : float
Value of embedded options
Returns:
--------
float : Option-adjusted spread
"""
return z_spread - option_cost
```
**Credit Spread Curve**:
```python
import matplotlib.pyplot as plt
def plot_credit_spread_curve(maturities, spreads, rating, date):
"""
Visualize credit spread term structure.
Parameters:
-----------
maturities : array-like
Bond maturities
spreads : array-like
Credit spreads (basis points)
rating : str
Credit rating
date : str
Observation date
"""
plt.figure(figsize=(10, 6))
plt.plot(maturities, spreads, 'ro-', linewidth=2, markersize=6)
plt.xlabel('Maturity (Years)')
plt.ylabel('Credit Spread (bps)')
plt.title(f'{rating} Credit Spread Curve - {date}')
plt.grid(True, alpha=0.3)
plt.show()
```
### 5. Event Risk and Credit Migration
**Event Risk Categories**:
- **M&A Activity**: Leverage increases from acquisitions
- **Dividend Recapitalization**: Debt-funded special dividends
- **Regulatory Changes**: New compliance costs or restrictions
- **Litigation**: Major legal judgments
**Credit Migration Matrix**:
```python
# One-year transition probabilities (%)
TRANSITION_MATRIX = {
'AAA': {'AAA': 90.81, 'AA': 8.33, 'A': 0.68, 'BBB': 0.06, 'Default': 0.00},
'AA': {'AAA': 0.70, 'AA': 90.65, 'A': 7.79, 'BBB': 0.64, 'Default': 0.02},
'A': {'AAA': 0.09, 'AA': 2.27, 'A': 91.05, 'BBB': 5.52, 'Default': 0.06},
'BBB': {'AAA': 0.02, 'AA': 0.33, 'A': 5.95, 'BBB': 86.93, 'BB': 5.30, 'Default': 0.18},
'BB': {'AAA': 0.03, 'BBB': 0.67, 'BB': 80.53, 'B': 8.84, 'Default': 1.06},
'B': {'BB': 0.43, 'B': 83.46, 'CCC': 4.07, 'Default': 4.89},
'CCC': {'B': 0.69, 'CCC': 58.24, 'Default': 26.92}
}
def expected_rating_migration(current_rating, years=1):
"""
Calculate probability distribution of future ratings.
Returns:
--------
dict : Probabilities for each rating category
"""
return TRANSITION_MATRIX.get(current_rating, {})
def downgrade_probability(current_rating, years=1):
"""
Calculate probability of downgrade within time horizon.
"""
transitions = TRANSITION_MATRIX.get(current_rating, {})
# Sum probabilities of lower ratings
ratings_order = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B', 'CCC']
current_index = ratings_order.index(current_rating)
downgrade_prob = sum(transitions.get(r, 0)
for r in ratings_order[current_index+1:])
downgrade_prob += transitions.get('Default', 0)
return downgrade_prob / 100 # Convert to decimal
```
## Expected Competency
Upon completion, you will be able to:
1. **Perform fundamental credit analysis** using financial ratios
2. **Extract market-implied default probabilities** from credit spreads
3. **Interpret credit ratings** and understand rating methodologies
4. **Model PD and LGD** using structural and reduced-form approaches
5. **Analyze credit spread behavior** and decompose spread components
6. **Assess credit migration risk** using transition matrices
7. **Monitor credit events** and evaluate impact on portfolio
## Deliverables
### 1. credit_risk_model.py
Production Python module containing:
- Fundamental credit metrics calculators
- PD/LGD modeling (Merton, hazard rate)
- Credit spread analysis tools
- Rating migration simulator
- Monte Carlo default scenario generator
- Portfolio credit risk aggregation
### 2. credit_spread_dashboard.md
Analysis documentation including:
- Credit spread tracking vs. Treasuries
- Spread curve analysis by rating tier
- Credit migration monitoring
- Event risk assessment framework
- Portfolio credit exposure report
## Reference Materials
### Foundational Texts
1. Lando, D. *Credit Risk Modeling: Theory and Applications*
2. Duffie, D. & Singleton, K. *Credit Risk: Pricing, Measurement, and Management*
3. CFA Institute. *Fixed Income* - Credit Analysis
### Rating Agency Methodologies
1. **Moody's**: [Rating Methodology](https://www.moodys.com/research/Rating-Methodologies)
2. **S&P**: [Corporate Criteria](https://www.spglobal.com/ratings/en/research-insights/methodology)
3. **Fitch**: [Rating Criteria](https://www.fitchratings.com/research/methodology)
### Data Sources
1. **FRED**: [Corporate Bond Spreads](https://fred.stlouisfed.org/)
2. **FINRA**: [Corporate Bond Data](https://www.finra.org/data)
3. **Bloomberg**: Credit default swap (CDS) spreads
## Validation Framework
### Self-Assessment Checklist
- [ ] Calculate key credit ratios from financial statements
- [ ] Estimate implied default probability from credit spread
- [ ] Interpret rating agency methodologies
- [ ] Model PD using Merton framework
- [ ] Estimate LGD by seniority and collateral
- [ ] Decompose credit spread into components
- [ ] Apply credit migration matrices
- [ ] Assess portfolio credit concentration risk
### Practice Problems
1. Calculate implied PD for BB-rated bond: 300 bps spread, 40% recovery
2. Estimate LGD for senior secured debt with $100M collateral, $150M debt
3. Determine downgrade probability for A-rated issuer over 1 year
4. Decompose 250 bps spread into expected loss and risk premium
## Integration with Other Skills
- **Bond Pricing**: Credit spreads adjust discount rates
- **Yield Measures**: Credit component in yield analysis
- **Benchmarking**: Credit quality comparison vs. peers
- **Portfolio Management**: Credit risk aggregation and limits
## Standards and Compliance
All credit analysis must document:
- Data sources and dates
- Rating sources and effective dates
- Assumptions (recovery rates, transition probabilities)
- Model validation and back-testing results
## Version Control
**Version**: 1.0.0
**Last Updated**: 2025-12-07
**Author**: Ordinis-1 Bond Analysis Framework
**Status**: Production Ready
---
**Previous Skill**: `duration-convexity/`
**Next Skill**: `bond-benchmarking/`
No comments yet. Be the first to comment!