Calibrate to the Market

Calibrate Hull-White Swaptions Through a Root-Find

A closed-form Jamshidian swaption price and bounded Nelder–Mead fit the (a, sigma) pair to a co-terminal diagonal. This numerical route does not use an AAD tape.

Today's curve
Reference market (generates grid)
Solver start
Java source
HullWhiteCalibrationRiskStudio.java

This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.

import com.nablatensor.quant.HullWhiteAnalytic;
import com.nablatensor.quant.YieldCurve;
import java.util.List;

/**
 * nablatensor.com/learn, chapter 7.5: the real {@code
 * HullWhiteCalibrationShowcase}, unchanged. Jamshidian's swaption price needs
 * a bisection root-find for the critical short rate plus a cumulative normal
 * on the result -- a data-dependent loop and a special function, neither one
 * expressible as a fixed sequence of tape nodes -- so this whole page is
 * plain {@code double} code with no {@code ADouble}, no {@code
 * AadRecorder}, and no engine string anywhere.
 */
public final class HullWhiteCalibrationRiskStudio {
  private HullWhiteCalibrationRiskStudio() {}

  public static void main(String[] args) {
    int n = 12;
    double[] pillars = new double[n];
    double[] zeros = new double[n];
    for (int i = 0; i < n; i++) {
      pillars[i] = i + 1.0;
      zeros[i] = 0.026000000000000002 + (0.035 - 0.026000000000000002) * (i / (n - 1.0));
    }
    YieldCurve curve = YieldCurve.of()
        .pillars(pillars)
        .zeroRates(zeros)
        .build();
    double[] expiries = {
      1, 2, 3, 4, 5, 7
    };
    int[] tenors = {
      9, 8, 7, 6, 5, 3
    };
    double accrual = 1.0;
    HullWhiteAnalytic reference = HullWhiteAnalytic.of(curve, 0.1, 0.0095);
    double[] bump = {
      1.03, 0.99, 1.01, 1.00, 0.98, 1.02
    };
    double[] normalVols = new double[expiries.length];
    for (int i = 0; i < expiries.length; i++) {
      int periods = tenors[i];
      double ann = 0.0;
      for (int j = 1; j <= periods; j++) {
        ann += accrual * curve.discountFactor(expiries[i] + j * accrual);
      }
      double fwd = (curve.discountFactor(expiries[i]) - curve.discountFactor(expiries[i] + periods * accrual)) / ann;
      double px = reference.payerSwaption(expiries[i], accrual, periods, fwd);
      normalVols[i] = bump[i] * px / (ann * Math.sqrt(expiries[i] / (2.0 * Math.PI)));
    }
    List<com.nablatensor.quant.HullWhiteCalibration.SwaptionQuote> quotes = com.nablatensor.quant.HullWhiteCalibration.grid(expiries,
        tenors, accrual, normalVols);
    long t0 = System.nanoTime();
    com.nablatensor.quant.HullWhiteCalibration.Result r = com.nablatensor.quant.HullWhiteCalibration.calibrate(curve,
        quotes, 0.03, 0.02);
    double ms = (System.nanoTime() - t0) / 1e6;
    HullWhiteAnalytic hw = r.model(curve);
    double cap = hw.cap(2.0, 1.0, 5, 0.033);
    double modelP10 = hw.bondReconstitution(0.0, 10.0, hw.r0()), curveP10 = curve.discountFactor(10.0);
    System.out.println("RESULT|" + r.a() + "|" + r.sigma() + "|" + r.rmsePrice() + "|" + r.iterations()
        + "|" + r.converged() + "|" + ms + "|" + cap + "|" + modelP10 + "|" + curveP10);
    for (int k = 0; k < quotes.size(); k++) System.out.println("ROW|" + expiries[k] + "|" + tenors[k]
        + "|" + r.targetPrices()[k] + "|" + r.modelPrices()[k]);
  }
}
TeaVM compiles and runs the Java source above in this browser.
Implementation guide

Calibrating through a root-finding problem

Hull–White calibration shows that a solver can sit inside a valuation workflow, with parameter identifiability often more important than raw fit error.

Core mechanism

Mean reversion and volatility are adjusted to reproduce swaption targets. A co-terminal diagonal may allow several parameter combinations to price almost equally well, revealing a flat calibration direction.

Practical workflow

Use sufficiently rich instruments, inspect the objective landscape and parameter confidence, impose economically justified constraints, and validate out-of-sample prices and Greeks.

Key details

*Keywords: hull white one factor java, jamshidian swaption java, hull white calibration java, swaption pricing java, caplet analytic java, ho lee model java*

Feature F6. The existing HullWhite1F Monte-Carlo step block assumes a flat initial forward. This adds the term-structure-consistent analytic model: given today's discount curve and (a, sigma) it reprices the curve exactly and prices bond options, caps/floors and European swaptions in closed form, then calibrates (a, sigma) to a swaption grid.

Each quote's ATM normal vol becomes a target price via Bachelier (feature F2); the model price is the Jamshidian swaption; the sum of squared price residuals is minimised by a bounded Nelder-Mead. The analytic swaption is not recordable (a root-find and N(x)), so this is the numerical rather than the adjoint calibration route — the adjoint route runs against the HullWhite1F Monte-Carlo swaption instead.

Scope and review point

A tiny residual on one calibration set does not establish a unique or stable model. The page intentionally highlights that limitation.