Skip to content

Calibration

A calibrator estimates dependence parameters from historical (portfolio_value, credit_factor) samples. Each implements the Calibrator port and returns a dict of parameters that round-trips directly into a dependence model.

Calibrator Extra Dependencies
RegressionCalibrator none numpy
SklearnSurvivalCalibrator [ml] scikit-learn

Regression calibrator (numpy only)

Since the Hull-White intensity is \(\lambda = \exp(a + b\,V)\), \(\log\lambda\) is linear in the portfolio value with slope \(b\). The credit_factor samples are interpreted as realised hazard rates; their logs are regressed on the portfolio value (ordinary least squares) to recover \(b\) and the intercept \(a\).

import numpy as np
from wayfault.adapters.outbound.calibrator_regression import RegressionCalibrator
from wayfault.adapters.outbound.dependence_hullwhite import HullWhiteHazardModel

rng = np.random.default_rng(7)
true_b, true_a = 0.6, np.log(0.02)
v = rng.normal(size=5000)
hazard = np.exp(true_a + true_b * v)         # simulated credit-factor history

params = RegressionCalibrator().fit(v, hazard)
# {'a': -3.912..., 'b': 0.600...}

model = HullWhiteHazardModel(b=params["b"])  # round-trip into a model

Round-trip guarantee

The calibrator recovers a known b from data simulated with that b (within tolerance) — this is an acceptance test.

Sklearn survival calibrator ([ml])

Fits a covariate-driven (log-)hazard surrogate (gradient-boosted regression) and linearises the fitted response over the observed support to a single Hull-White \(b\) that a dependence model can consume.

from wayfault.adapters.outbound.calibrator_sklearn import SklearnSurvivalCalibrator

params = SklearnSurvivalCalibrator(n_estimators=200).fit(v, hazard)
model = HullWhiteHazardModel(b=params["b"])

If scikit-learn is not installed, calling .fit(...) raises a clear MissingDependencyError — never an ImportError at import time.

pip install 'wayfault[ml]'

Inverse solvers (prescriptive)

Calibrators estimate parameters from historical data. The inverse solvers go the other way — they answer target-driven and reverse-stress questions by root-finding on the monotone metric-vs-parameter curve (robust bisection, numpy-only):

  • calibrate_to_alpha — find the dependence parameter that reproduces a target alpha (e.g. a desk/regulator number).
  • calibrate_to_cva — find the parameter that reproduces a target WWR-CVA (e.g. a historically observed CVA).
  • find_breakpoint — find the dependence level at which a metric (alpha, WWR-CVA, or EAD) crosses a threshold: "how much wrong-way correlation until we breach?".

You supply a model_factory mapping a scalar to a dependence model, so the same solver works for Hull-White b, Gaussian ρ, or a copula θ.

from wayfault import calibrate_to_alpha, find_breakpoint
from wayfault.adapters.outbound.dependence_hullwhite import HullWhiteHazardModel

hw = lambda b: HullWhiteHazardModel(b=b)

# 1) Calibrate: which b reproduces a desk target alpha of 1.25?
sol = calibrate_to_alpha(exposure, credit, hw, target_alpha=1.25, lo=-1.5, hi=1.5)
print(sol.param, sol.converged)        # -> ~the b giving alpha = 1.25

# 2) Reverse stress: at what b does alpha breach 1.40?
brk = find_breakpoint(exposure, credit, hw, threshold=1.40, lo=0.0, hi=3.0)
print(brk.param, brk.result.classification)

Each returns a SolveResult carrying the solved param, the achieved metric, iterations, a converged flag, and the full result (WWRResult) at the solution. The solve is deterministic. If [lo, hi] does not bracket the target, a clear ValidationError is raised.