# CECL Lifetime Expected Credit Loss

Compute CECL (ASC 326) expected credit loss using causal PD models and
soliton-dynamic forecast horizons.

## Overview

The CECL pipeline:

1. Loads the credit ontology (19-variable causal DAG)
2. Generates a synthetic portfolio
3. Fits a causal PD model using backdoor adjustment
4. Computes lifetime ECL with dynamic horizons based on soliton stability scores

## Run It

```bash
python examples/cecl_pipeline.py
```

## Key Concepts

### Soliton-Dynamic Horizons

Instead of a fixed forecast horizon, Orxaq selects the horizon based on the
stability of causal relationships in the DAG (per ADR-011):

| Soliton Score | Horizon | Interpretation |
|---------------|---------|----------------|
| > 0.80 | 12 quarters (3 years) | Strong causal stability |
| 0.60 – 0.80 | 8 quarters (2 years) | Moderate stability |
| < 0.60 | 4 quarters (1 year) | Weak — shorten horizon |

### ECL Formula

For each loan in the portfolio:

```
ECL = cumulative_PD × LGD × EAD
cumulative_PD = 1 - (1 - PD)^(horizon / 4)
```

Default LGD = 0.45 (Basel II foundation approach).

### Confidence Bands

- **Dynamic horizon**: Band width = `(1 - avg_soliton) × ECL × 0.5`
- **Fixed horizon**: Band width = `ECL × 0.25`

Tighter soliton scores → narrower confidence bands.

## Python API

```python
from orxaq.credit.causal_pd import CausalPDModel
from orxaq.credit.cecl import CECLEngine
from orxaq.credit.ontology import build_credit_ontology
from orxaq.credit.synthetic import CreditDataGenerator

# Build ontology and generate data
dag = build_credit_ontology()
gen = CreditDataGenerator(dag, seed=42)
data = gen.generate(500)

# Fit causal PD model
model = CausalPDModel(dag)
model.fit(data)

# Compute ECL with dynamic horizon
engine = CECLEngine(model, dag)
result = engine.expected_credit_loss(data)

print(f"Total ECL: ${result.total_ecl:,.2f}")
print(f"Horizon: {result.horizon_quarters}Q ({result.horizon_method})")
print(f"Confidence: ${result.confidence_band[0]:,.2f} – ${result.confidence_band[1]:,.2f}")
```

### Fixed Horizon Override

```python
# Force 12-quarter horizon
result = engine.expected_credit_loss(data, horizon_quarters=12)
```

### Feature Importance

```python
# Causal (not correlational) feature importance
importance = model.feature_importance()
for var, effect in list(importance.items())[:5]:
    print(f"  {var}: {effect:+.4f}")
```

## CLI

```bash
# Human-readable output
orxaq credit ecl

# JSON for downstream systems
orxaq --json credit ecl
```

## Source

- Example script: [`examples/cecl_pipeline.py`](https://github.com/Orxaq/orxaq/blob/main/examples/cecl_pipeline.py)
- CECL engine: `src/orxaq/credit/cecl.py`
- Causal PD model: `src/orxaq/credit/causal_pd.py`
- ADR-011: Soliton Horizons
