Skip to content

daibeal/wayfault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wayfault

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

PyPI Python License: MIT Docs Playground

📖 Documentation · 🎮 Live playground · 📊 Worked example · 📄 Paper (PDF)

wayfault dashboard

wayfault quantifies the adverse dependence between exposure and counterparty credit quality — the risk that exposure rises precisely when the counterparty deteriorates (WWR), and its favourable mirror, Right-Way Risk (RWR). 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 α = WWR-CVA / 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.

🎮 Live playground

An interactive browser playground runs the real wayfault wheel via WebAssembly (Pyodide) — no install, no server. Adjust the dependence model and parameters and watch the CVA, alpha multiplier, and exposure charts recompute live.

Architecture

wayfault follows a strict hexagonal (ports & adapters) design. The dependency rule points inward: adapters → application → ports → domain. The domain, application, and ports layers import only the standard library and numpy. Optional adapters import their heavy dependencies lazily and raise a clear MissingDependencyError if the extra is not installed.

Install

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

For local development (editable install with the dev tooling):

pip install -e '.[io,ml,viz,dev]'

Optional extras:

Extra Enables Pulls in
[io] CSV/Parquet source adapters pandas, pyarrow
[ml] scikit-learn survival calibrator scikit-learn
[viz] diagnostic plots matplotlib
[dev] tests, type-checking, linting pytest, mypy, …

Quickstart

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  # scenarios x tenors
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)

A full runnable example lives in examples/quickstart.py and uses only the in-memory adapters (zero extras).

CLI

python -m wayfault estimate \
    --exposure cube.csv --credit curve.csv \
    --model hullwhite --b 0.5 --out result.json

(--exposure/--credit CSV ingestion requires the [io] extra.)

Dependence models

Model Knob WWR when Notes
IndependentModel conditional EE ≡ unconditional EE
HullWhiteHazardModel b b > 0 λ(t) = exp(a(t) + b·V(t)) (Hull–White)
GaussianCopulaModel ρ ρ > 0 one-factor Gaussian copula
ClaytonCopulaModel θ θ > 0 Archimedean, lower-tail dependence
FrankCopulaModel θ θ > 0 Archimedean, symmetric (sign sets direction)

All models are numpy-only and fully vectorised across tenors.

Calibration

  • RegressionCalibrator — numpy-only OLS estimate of the Hull–White b.
  • SklearnSurvivalCalibrator[ml] covariate-hazard surrogate.

Inverse solvers (prescriptive)

Invert the relationship — target-driven calibration and reverse-stress:

from wayfault import calibrate_to_alpha, find_breakpoint
hw = lambda b: HullWhiteHazardModel(b=b)

calibrate_to_alpha(exposure, credit, hw, target_alpha=1.25, lo=-1.5, hi=1.5)  # which b -> alpha 1.25?
find_breakpoint(exposure, credit, hw, threshold=1.40, lo=0.0, hi=3.0)          # b where alpha breaches 1.40

Visualization

The [viz] extra adds a beautiful matplotlib plotting module (wayfault.adapters.outbound.viz) — lazily imported, so the core stays numpy-only. Regenerate the gallery with pip install 'wayfault[viz]' then python examples/gallery.py.

Exposure profiles — EPE vs conditional EE, shaded WWR adjustment EE ratio — per-tenor conditional/unconditional
Exposure profiles EE ratio
Alpha sweep — alpha & CVA vs the dependence knob Dashboard — everything at a glance
Alpha sweep Dashboard
from wayfault.adapters.outbound import viz
fig = viz.plot_dashboard(result, bs=bs, alphas=alphas, wwr_cvas=wwr_cvas)
viz.save(fig, "dashboard.png")

Performance

Two acceleration techniques, both numpy-only:

  • Vectorised re-weighting — every model re-weights the whole exposure cube in a single numpy expression (no per-tenor Python loop).
  • Parallel batch & sweepwayfault.application.parallel fans independent estimates across workers with deterministic, input-order results:
from wayfault.application.parallel import sweep_models
from wayfault.adapters.outbound.dependence_hullwhite import HullWhiteHazardModel

results = sweep_models(
    exposure, credit,
    [HullWhiteHazardModel(b=b) for b in np.linspace(-1.2, 1.2, 25)],
    max_workers=8,            # threads (numpy releases the GIL); or pass a ProcessPoolExecutor
)
alphas = [r.alpha for r in results]

See the Performance docs.

Development

pytest                 # tests
mypy --strict src      # type checks
ruff check             # lint

Reference: Hull & White, CVA and Wrong-Way Risk (2012).

License

MIT.

About

Wrong-Way Risk (WWR) estimation for counterparty credit risk - a minimal, hexagonal, numpy-only Python library (CVA, alpha multiplier, Hull-White & copula models).

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors