Core mechanism
At each future date the model estimates positive exposure, weights it by incremental default probability and loss given default, then discounts the contribution. Summing dates gives the counterparty-credit adjustment.
TeaVM runs the actual netting-set exposure simulation, then assembles the flat-discount textbook CVA from Java EPE, marginal default probability and LGD for comparison.
Three trades net in one uncollateralised USD set: the editable payer swap, a fixed offsetting receiver, and a bought EUR forward. Credit hazard is piecewise flat across the three displayed buckets.
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.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.CollateralAgreement;
import java.util.List;
public final class CvaFirstPrinciplesRiskStudio {
public static void main(String[] args) {
double r0 = 0.020000000000, a = 0.030000000000, sigma = 0.010000000000, hs = 0.0090000000000,
hm = 0.015000000000, hl = 0.017000000000, recovery = 0.40000000000, notional = 100000000.00;
int steps = 20;
long paths = 3000L, seed = 20260902L;
CreditName cpty = 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(cpty)
.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(CollateralAgreement.uncollateralised())
.build();
CvaMarket market = CvaMarket.of()
.r0(r0)
.hwLevel(r0)
.hwMeanReversion(a)
.hwSigma(sigma)
.hazardShort(hs)
.hazardMid(hm)
.hazardLong(hl)
.recovery(recovery)
.fxSpot(1.1)
.fxVol(.12)
.fxForeignRate(.024)
.build();
ExposureSimulation sim = ExposureSimulation.of(ns, steps)
.on("cpu")
.fp64(true);
CvaResult result = sim.run(market, paths, seed);
double[] times = result.epeProfile()
.times(), epe = result.epeProfile()
.values();
double dt = sim.stepYears(), survival = 1, previous = 0, approx = 0, mean = 0;
for (int k = 1; k <= steps; k++) {
double t = k * dt, from = previous, to = t;
double dh = hs * Math.max(0, Math.min(to, 2) - Math.max(from, 0)) + hm * Math.max(0, Math.min(to,
5) - Math.max(from, 2)) + hl * Math.max(0, to - Math.max(from, 5));
double marginal = survival * (1 - Math.exp(-dh)), discount = Math.exp(-r0 * t), contribution = epe[k - 1] * discount * marginal * (1 - recovery);
approx += contribution;
mean += epe[k - 1];
System.out.println("STEP|" + t + "|" + epe[k - 1] + "|" + discount + "|" + marginal + "|" + contribution);
survival *= Math.exp(-dh);
previous = t;
}
System.out.println("RESULT|" + result.value() + "|" + result.standardError() + "|" + (mean / steps)
+ "|" + approx + "|" + times.length + "|" + result.buildSeconds() + "|" + result.sweepSeconds());
}
}
Unilateral CVA combines exposure, counterparty default probability and loss given default on a common time grid.
At each future date the model estimates positive exposure, weights it by incremental default probability and loss given default, then discounts the contribution. Summing dates gives the counterparty-credit adjustment.
Model legal netting and collateral first, generate market scenarios, price future exposure consistently, calibrate the credit curve and retain contribution-by-date diagnostics.
At each exposure date, unilateral CVA combines discounted expected positive exposure with the counterparty’s incremental default probability and loss given default. The time profile matters: a trade with the same terminal exposure can have a different CVA when its exposure is concentrated early rather than late.
Expected exposure is not simply today’s mark. Future market states are simulated, each trade in the netting set is revalued at each grid date, and legally enforceable netting is applied before positive exposure is taken. Collateral then changes the remaining exposure according to threshold, call timing and margin-period assumptions.
The computational shape is paths multiplied by future dates multiplied by trades. A full bump of one market factor repeats that exposure simulation, which is why adjoint extraction is useful: recording the connected CVA valuation once can return the vector of sensitivities in one reverse sweep rather than one resimulation per factor.
The page’s contribution-by-date output is a diagnostic, not just presentation. It lets a reviewer see whether the result is driven by early exposure, a credit-curve interval, a collateral gap or discounting. That decomposition should be reconciled before relying on a headline CVA.
Important omitted effects include bilateral credit effects, funding adjustments, disputes, close-out conventions and explicit wrong-way risk. Those omissions should be understood as scope boundaries, not hidden approximations.
A teaching grid omits many real effects, including disputes, margin period of risk, wrong-way risk, funding and close-out conventions.