# Credit Risk

> 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.

- Skill: `majiayu000/credit-risk` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/credit-risk`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/credit-risk/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/credit-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/`

