Core mechanism
Adjoint differentiation exposes price changes with respect to market and credit inputs in one connected calculation. Candidate hedges are then assessed by how they alter that sensitivity vector and by their basis risk.
TeaVM runs the actual netting-set exposure simulation and reads the hedgeable market risk vector from its single adjoint sweep.
This exact source runs in TeaVM. Form changes update its Java literals and reset manual edits.
import com.nablatensor.cva.CreditName;
import com.nablatensor.cva.CvaMarket;
import com.nablatensor.cva.CvaResult;
import com.nablatensor.cva.CvaRiskFactors;
import com.nablatensor.cva.ExposureSimulation;
import com.nablatensor.cva.FxForward;
import com.nablatensor.cva.HazardCurve;
import com.nablatensor.cva.InterestRateSwap;
import com.nablatensor.cva.NettingSet;
import com.nablatensor.cva.SaCvaSensitivities;
import com.nablatensor.engine.AadExecutable;
import com.nablatensor.risk.RiskFactor;
import com.nablatensor.risk.Sensitivities;
import java.util.List;
public final class HedgingCvaRiskStudio {
public static void main(String[] args) {
double r0 = 0.0200000000000, a = 0.0300000000000, sigma = 0.0100000000000, hs = 0.00900000000000,
hm = 0.0150000000000, hl = 0.0170000000000, recovery = 0.400000000000, fxSpot = 1.10000000000,
notional = 100000000.000;
int steps = 20;
long paths = 3000L, seed = 20260902L;
CreditName cp = CreditName.of()
.id("CPTY-A")
.curve(HazardCurve.fromFlatSpread(150.0, recovery, 10.0))
.recovery(recovery)
.rating(CreditName.RatingEnum.BBB)
.sector(CreditName.SectorEnum.FINANCIAL)
.build();
NettingSet ns = NettingSet.of()
.id("NS-CPTY-A")
.counterparty(cp)
.trades(List.of(InterestRateSwap.of()
.id("A-SWAP-PAY")
.side(InterestRateSwap.SideEnum.PAY_FIXED)
.notional(notional)
.fixedRate(.02)
.startYears(0)
.maturityYears(7)
.accrualYears(.5)
.build(), InterestRateSwap.of()
.id("A-SWAP-REC")
.side(InterestRateSwap.SideEnum.RECEIVE_FIXED)
.notional(40000000)
.fixedRate(.036)
.startYears(0)
.maturityYears(5)
.accrualYears(.5)
.build(), FxForward.of()
.id("A-FX-FWD")
.side(FxForward.SideEnum.BUY_FOREIGN)
.foreignNotional(20000000)
.strike(1.05)
.settlementYears(4)
.build()))
.collateral(com.nablatensor.cva.CollateralAgreement.uncollateralised())
.build();
CvaMarket market = CvaMarket.of()
.r0(r0)
.hwLevel(r0)
.hwMeanReversion(a)
.hwSigma(sigma)
.hazardShort(hs)
.hazardMid(hm)
.hazardLong(hl)
.recovery(recovery)
.fxSpot(fxSpot)
.fxVol(.12)
.fxForeignRate(.024)
.build();
ExposureSimulation sim = ExposureSimulation.of(ns, steps)
.on("cpu")
.fp64(true);
CvaResult result = sim.run(market, paths, seed);
CvaMarket g = result.gradient();
double lgd = 1 - recovery;
double bp = 1e-4, dHazard = bp / lgd;
double irBump = (sim.run(market(r0 + bp, a, sigma, hs, hm, hl, recovery, fxSpot), paths, seed)
.value() - sim.run(market(r0 - bp, a, sigma, hs, hm, hl, recovery, fxSpot), paths, seed)
.value()) / 2;
double csBump = (sim.run(market(r0, a, sigma, hs + dHazard, hm, hl, recovery, fxSpot), paths,
seed)
.value() - sim.run(market(r0, a, sigma, hs - dHazard, hm, hl, recovery, fxSpot), paths,
seed)
.value()) / 2;
System.out.println("BUMP|IR delta (1bp curve shift)|" + irBump);
System.out.println("BUMP|CS01 short (0-2y)|" + csBump);
System.out.println("RESULT|" + result.value() + "|" + result.standardError() + "|" + result.sweepSeconds());
System.out.println("ROW|IR delta (1bp curve shift)|" + ((g.r0() + g.hwLevel()) * 1e-4));
System.out.println("ROW|IR vega (dCVA/dsigma . sigma)|" + (g.hwSigma() * result.market()
.hwSigma()));
System.out.println("ROW|CS01 short (0-2y)|" + ((g.hazardShort() / lgd) * 1e-4));
System.out.println("ROW|CS01 mid (2-5y)|" + ((g.hazardMid() / lgd) * 1e-4));
System.out.println("ROW|CS01 long (5y+)|" + ((g.hazardLong() / lgd) * 1e-4));
System.out.println("ROW|FX delta (1% spot move)|" + (g.fxSpot() * result.market()
.fxSpot() * .01));
System.out.println("ROW|Recovery sensitivity (dCVA/dR)|" + g.recovery());
}
private static CvaMarket market(double r, double a, double sigma, double hs, double hm, double hl,
double recovery, double fxSpot) {
return CvaMarket.of()
.r0(r)
.hwLevel(r)
.hwMeanReversion(a)
.hwSigma(sigma)
.hazardShort(hs)
.hazardMid(hm)
.hazardLong(hl)
.recovery(recovery)
.fxSpot(fxSpot)
.fxVol(.12)
.fxForeignRate(.024)
.build();
}
}
A CVA desk manages changes in exposure, market factors and counterparty credit, rather than only the level of the CVA adjustment.
Adjoint differentiation exposes price changes with respect to market and credit inputs in one connected calculation. Candidate hedges are then assessed by how they alter that sensitivity vector and by their basis risk.
Define hedge instruments and factor mapping, calculate CVA and hedge sensitivities on the same conventions, optimise subject to liquidity and limits, then monitor residual risk.
CVA hedging begins with a factor vector, not a single number. Market moves change future exposure, while credit-spread moves change both default probabilities and the price of credit hedges. A hedge candidate should therefore be measured against the same risk-factor definitions used to produce the CVA sensitivities.
The exposure engine records interest-rate, FX, credit-curve and recovery inputs as active quantities. Its reverse sweep can return IR delta and vega, counterparty credit-spread sensitivity by tenor bucket, recovery sensitivity and FX risk together. The useful comparison is then between the unhedged vector, hedge vector and residual vector.
Credit hedges are imperfect: a CDS can reduce a counterparty-spread sensitivity while leaving exposure, basis, liquidity and jump-to-default risk. Index hedges add a further mapping problem because index constituents and the counterparty do not move one-for-one.
For validation, selected sensitivities should be reconciled with common-random-number bump-and-revalue runs. That isolates derivative-propagation issues from ordinary Monte-Carlo noise and makes units—such as a one-basis-point credit spread move—explicit.
A hedge programme also requires limits, liquidity assessment, accounting treatment and governance. The page demonstrates the risk-vector calculation; it does not optimise or approve a trading strategy.
Sensitivity matching is not a complete hedge strategy: jump-to-default, model risk, liquidity and counterparty-specific constraints remain.