Skip to content

wayfault

Wrong-Way Risk (WWR) estimation for counterparty credit risk.

PyPI Python License

wayfault quantifies the adverse dependence between exposure and counterparty credit quality — the risk that exposure rises precisely when the counterparty deteriorates (Wrong-Way Risk), and its favourable mirror, Right-Way Risk.

It takes a Monte-Carlo exposure cube and a credit curve as inputs and produces:

  • baseline (independence-assumption) exposure metrics and CVA,
  • a conditional expected exposure given default under a pluggable dependence model,
  • a WWR-adjusted CVA and the empirical alpha multiplier \(\alpha = \text{WWR-CVA} / \text{independent-CVA}\),
  • ML-based calibration of the dependence parameter,
  • WWR/RWR classification and diagnostics.

The library does not generate exposures or bootstrap curves — those are inputs.

Try it live

The Playground runs the real wayfault wheel in your browser (via WebAssembly/Pyodide) with an interactive dashboard — adjust the model and watch the CVA, alpha, and exposure charts update live.

Why wayfault

Design principles

  • Minimal core. The only hard runtime dependency is numpy. Everything else (pandas, scikit-learn, matplotlib) is an optional extra, imported lazily.
  • Hexagonal architecture. A pure domain, surrounded by ports (Protocols) and swappable adapters. The dependency rule points inward.
  • Deterministic. Same seed ⇒ identical results.
  • Typed & tested. mypy --strict clean, ruff clean, ≥ 90 % coverage on the core.

Install

pip install wayfault                 # core (numpy only)
pip install 'wayfault[io,ml,viz]'    # with optional extras

30-second example

import numpy as np
from wayfault import estimate_wwr
from wayfault.adapters.outbound.exposure_inmemory import InMemoryExposureSource
from wayfault.adapters.outbound.credit_flat import FlatHazardCreditCurveSource
from wayfault.adapters.outbound.dependence_hullwhite import HullWhiteHazardModel

cube = np.random.default_rng(0).normal(size=(10_000, 12)) + 1.0
tenors = [i / 4 for i in range(1, 13)]   # quarterly to 3y

result = estimate_wwr(
    exposure=InMemoryExposureSource(cube, tenors),
    credit=FlatHazardCreditCurveSource(hazard=0.02, recovery=0.4),
    model=HullWhiteHazardModel(b=0.5),    # b > 0  ->  wrong-way
)

print(result.baseline_cva, result.wwr_cva, result.alpha, result.classification)

Worked example: wrong-way risk on a 5-year swap

A realistic end-to-end study, computed by the real library (see examples/case_study.py) and rendered below with interactive Plotly charts. The setup: a 5-year receiver interest-rate swap (humped exposure of 20 000 Monte-Carlo paths over 20 quarterly tenors) facing a BB-rated counterparty with an upward-sloping hazard curve, discounted at a flat 3 %.

1. Baseline exposure metrics

From the exposure cube \(V(t)\) we form the independence-assumption profiles. The expected positive / negative exposure and potential future exposure are

\[ \mathrm{EPE}(t) = \mathbb{E}\!\big[V(t)^{+}\big],\qquad \mathrm{ENE}(t) = \mathbb{E}\!\big[(-V(t))^{+}\big],\qquad \mathrm{PFE}_q(t) = \inf\{x : \Pr(V(t)^{+}\le x)\ge q\}, \]

and the effective EPE is the time-weighted running maximum \(\mathrm{EEPE} = \tfrac{1}{T}\sum_i \max_{j\le i}\mathrm{EPE}(t_j)\,\Delta t_i\).

2. Baseline CVA

Unilateral CVA integrates discounted expected exposure against the marginal default probability, with loss-given-default \(1-R\):

\[ \mathrm{CVA} = (1-R)\sum_{i} DF(t_i)\,\mathrm{EE}(t_i)\, \mathrm{PD}(t_{i-1}, t_i),\qquad \mathrm{PD}(t_{i-1}, t_i) = S(t_{i-1}) - S(t_i),\quad S(t) = e^{-\int_0^t \lambda(u)\,du}. \]

The counterparty's hazard \(\lambda(t)\) and survival \(S(t)\):

3. The wrong-way adjustment (Hull–White)

Under wrong-way risk, default intensity rises with exposure. The canonical Hull–White stochastic-hazard model couples them through

\[ \lambda(t) = \exp\!\big(a(t) + b\,V(t)\big), \]

where \(a(t)\) is solved per tenor to reproduce the curve's marginal PDs and \(b\) is the wrong-way knob. The conditional expected exposure given default re-weights each scenario by its model-implied default likelihood, \(w_s \propto e^{\,b\,V_s(t)}\), lifting the exposure profile into the high-exposure tail:

4. Alpha multiplier and EAD

The empirical alpha multiplier and the regulatory exposure-at-default view are

\[ \alpha = \frac{\mathrm{CVA}_{\text{WWR}}}{\mathrm{CVA}_{\text{indep}}}, \qquad \mathrm{EAD} = \alpha \cdot \mathrm{EEPE}. \]

Sweeping \(b\) traces how the adjustment turns on — monotone in \(b\), with \(\alpha \ge 1\) for wrong-way (\(b>0\)) and \(\alpha \le 1\) for right-way (\(b<0\)):

5. Where the risk concentrates

The per-tenor ratio \(\mathrm{EE}_{\text{cond}}(t)/\mathrm{EPE}(t)\) across the coupling \(b\) shows the adjustment is strongest where exposure peaks — the belly of the swap:

6. Model comparison

Different dependence families (Hull–White hazard, Gaussian copula, and the tail-dependent Clayton and Frank copulas) imply different uplifts for the same book:

7. Arbitrage consistency

A calibrated model must, integrated over the exposure distribution, reproduce the curve's marginal PDs — otherwise it mis-prices the unconditional default. The Hull–White model's model-implied PDs sit exactly on the target:

\[ \mathbb{E}_{V}\!\big[\mathrm{PD}_i(V)\big] = \mathrm{PD}(t_{i-1}, t_i) \quad\text{for every tenor } i. \]

Continue with the Getting Started guide, or jump to the API Reference.