Credit & Counterparty Risk

Find the Greeks a CVA Desk Actually Trades

TeaVM runs the actual netting-set exposure simulation and reads the hedgeable market risk vector from its single adjoint sweep.

Book
Rates & credit market
Simulation
Java source
HedgingCvaRiskStudio.java

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();
  }
}
TeaVM compiles and runs the Java source above in this browser.
Implementation guide

CVA risk is a hedgeable vector

A CVA desk manages changes in exposure, market factors and counterparty credit, rather than only the level of the CVA adjustment.

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.

Practical workflow

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.

Key details

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.

Scope and review point

Sensitivity matching is not a complete hedge strategy: jump-to-default, model risk, liquidity and counterparty-specific constraints remain.