# CCAR/DFAST Stress Testing

Multi-scenario stress testing using causal propagation of macroeconomic
shocks through the credit DAG.

## Overview

The CCAR pipeline:

1. Loads the credit ontology and fits a PD model
2. Defines macroeconomic scenarios (baseline, adverse, severely adverse)
3. Propagates shocks through the DAG using do-calculus (interventional queries)
4. Computes stressed PD and ECL under each scenario

## Run It

```bash
python examples/ccar_stress_test.py
```

## Predefined Scenarios

Orxaq ships with three Fed CCAR scenarios:

| Variable | Baseline | Adverse | Severely Adverse |
|----------|----------|---------|------------------|
| Unemployment | 4.0% | 7.0% | 10.0% |
| Fed Funds Rate | 3.0% | 1.0% | 0.5% |
| GDP Growth | 2.0% | -1.0% | -4.0% |
| HPI | 220 | 180 | 150 |
| CPI | 260 | 275 | 280 |

## Key Concepts

### Causal Stress Propagation

Unlike correlation-based stress tests, Orxaq propagates macro shocks
through causal pathways:

```
unemployment ↑ → income ↓ → dti ↑ → PD ↑
         └→ credit_score ↓ → PD ↑
```

The `ScenarioEngine.apply_scenario()` method uses the DAG's topological
ordering to propagate shocks downstream, ensuring causal consistency.

### Interventional PD

The `stress_pd()` method computes `P(default | do(unemployment=0.10))` — the
probability of default *if we set* unemployment to 10%, not merely *given that*
unemployment is 10%. This distinction matters for policy analysis.

## Python API

```python
from orxaq.credit.causal_pd import CausalPDModel
from orxaq.credit.ontology import build_credit_ontology
from orxaq.credit.scenarios import BASELINE, ADVERSE, SEVERELY_ADVERSE, ScenarioEngine
from orxaq.credit.synthetic import CreditDataGenerator

# Setup
dag = build_credit_ontology()
gen = CreditDataGenerator(dag, seed=42)
data = gen.generate(500)
model = CausalPDModel(dag)
model.fit(data)

# Run stress test
engine = ScenarioEngine(dag)
base_pd = engine.stress_pd(model, BASELINE, data)
stress_pd = engine.stress_pd(model, SEVERELY_ADVERSE, data)

print(f"Baseline PD:  {base_pd:.4%}")
print(f"Stressed PD:  {stress_pd:.4%}")
print(f"Delta:        {stress_pd - base_pd:+.4%}")
```

### Custom Scenarios

```python
custom_scenario = {
    "unemployment_rate": 0.12,
    "fed_funds_rate": 0.001,
    "gdp_growth": -0.06,
    "hpi": 130.0,
    "cpi": 290.0,
}

stressed_data = engine.apply_scenario(custom_scenario, data)
```

### Stressed ECL

```python
from orxaq.credit.cecl import CECLEngine

cecl = CECLEngine(model, dag)
stressed_data = engine.apply_scenario(SEVERELY_ADVERSE, data)
result = cecl.expected_credit_loss(stressed_data)
print(f"Stressed ECL: ${result.total_ecl:,.2f}")
```

## CLI

```bash
# Stress test with default scenario
orxaq --json credit ecl

# Full pipeline status
orxaq status
```

## Source

- Example script: [`examples/ccar_stress_test.py`](https://github.com/Orxaq/orxaq/blob/main/examples/ccar_stress_test.py)
- Scenario engine: `src/orxaq/credit/scenarios.py`
- Scenario data: `data/validation/scenarios/`
