You are here · XVA-01This chapter simulates the paths, reprices them into the mark-to-market matrix and hands on the exposure profile; everything upstream and downstream of it is one machine.
Every XVA number is an integral over exposure. This chapter derives the whole family and computes each from 5,000 simulated paths of one swap.
YHK
Youssouf H. Kerzika
Quantitative Researcher · XVA
Prerequisites
IR-01, whose SOFR curve prices this swap, and XVA-00. You need the IR-01 curve, a short-rate engine and one trade; the mark-to-market is built here.
IR-08 (draft) derives the repricer, but xvafoundations.pricing.reprice_swap_paths already ships.
By the end, you can compute EE, EPE, PFE and EEPE from a simulated mark-to-market matrix; Figure 1, just below, maps exactly what that takes.
Figure 1 · XVA-01Nothing new is modelled here: one corner at zero, then a mean, a quantile and three ways of squeezing the profile.Navy arrows bring objects in, the gold dashed chain is built here, the dotted line is the mirror.Structural map; not library output.
Every EE number below comes from xvafoundations.pricing.reprice_swap_paths on a Hull-White simulation with $a = 0.05$, $\sigma = 0.007$, seed 42, 5,000 paths, quarterly to 10.25 years, for one trade: a 10-year payer swap on 10,000,000 USD, annual ACT/360 fixed leg, struck at its own par rate of $4.1200\%$ off the IR-01 curve. Those two parameters are asserted, not calibrated: IR-05 and IR-06 are not yet published.
What is exposure?
Take a derivative worth $V(t)$ to you, its mark-to-market at $t$: what closing it out would pay you, negative when you owe them. If your counterparty defaults, only one sign of $V(t)$ costs you anything.
Figure 2 · XVA-01Exposure exists only because the loss function has a corner at zero: your gain is at risk, your loss is not.Two paths at year 5, near-equal moves opposite ways. Only one can cost you.Source: CHART_DATA samplePaths, paths 12 and 22.
Above the corner you hold a claim on the estate, recover a fraction $R$, and their default costs you $(1-R)\,V(t)$. Below it your liability survives in full. Exposure on one path is the height of the crimson line; its mirror feeds DVA:
Single-path exposure, and its mirror
$$E(t) = \max(V(t),\, 0), \qquad E^{-}(t) = \max(-V(t),\, 0)$$
Expected Exposure and EPE
Expected Exposure is the risk-neutral expectation of that corner across paths:
$\mathbb{Q}$, the risk-neutral measure, buys exactly one thing: under it any traded price is the expectation of its discounted payoff. That is the fundamental theorem of asset pricing, used here and not proved here.Conditioning on $\mathcal{F}_0$, everything known today, makes EE$(t)$ computable today. The estimator is $N^{-1} \sum_{i} \max(V_i(t_j),0)$.
Expected Positive Exposure is the time-average of that profile:
$T$: trade maturity. Trapezoid over the grid, which runs one quarter past the swap's ACT/360 maturity of 10.144444.
Figure 3 · XVA-01EE, ENE and PFE are three readings of one distribution at one date, not three definitions to memorise.Each is a tick on the value axis, never an area: the hatched half is discarded.Source: CHART_DATA crossSection, 5,000 paths in 40 bins.
Effective EPE and regulatory capital
Under the Internal Model Method, a bank cannot cut capital because EE dips on the grid. Effective Expected Exposure is the running maximum:
The one-year cap stops a long-dated trade earning relief from a quiet first year.EEPE does not in general dominate EPE. EEE$(t) \geq$ EE$(t)$ pointwise, so the effective average dominates over a common window, but EEPE caps its window at one year while EPE runs to $T$: here $120{,}866.59$ USD against $238{,}137.54$.IMM exposure-at-default is $\alpha_{\text{IMM}} \cdot \text{EEPE}$, $\alpha_{\text{IMM}} = 1.4$ (BCBS 279, paragraph 37).
Potential Future Exposure
EE is a mean, and a mean is not conservative enough for limits: a counterparty who exceeds a credit limit 50% of the time is not well-managed. Potential Future Exposure is a high quantile instead:
$Q_q$: the $q$-quantile across paths; $q = 97.5\%$ is the credit-limit standard (Gregory 2020, ch. 7).PFE is a quantile of $\max(V(t),0)$, not a bound on EE$(t)$. The positive part puts an atom at zero, so a quantile just above the median can sit far below the mean: for mean-zero $V(t)$, PFE$(t,\,0.51) = 0.025\,\sigma$ against EE$(t) = 0.399\,\sigma$. PFE exceeds EE at $q = 97.5\%$ on any realistic profile, but not for every $q > 0.5$.PFE$/$EE is scale-free, set by the shape of the cross-section and not the level of $\sigma$; here it runs $3.87$ to $4.90$.Estimator: the $\lceil qN \rceil$-th order statistic, so exactly 125 of the 5,000 paths exceed it.
Figure 4 · XVA-01Five metrics, five ways of squeezing one curve: a point, a running maximum, an area, a one-year window.EEPE averages EEE over year one alone, which is why EPE is larger.Source: CHART_DATA, seed 42. The grey textbook curve is illustrative.
The textbook argument says uncertainty grows as $\sigma\sqrt{t}$ while the annuity outstanding shrinks as $(T-t)$, so exposure peaks at $t = T/3$. That locates the dispersion of $V(t)$, which peaks at year 4. EE peaks a year later, because the remaining swap carries a rising risk-neutral mean.
Risk-neutral vs. real-world measure
The most frequently misunderstood point in XVA
CVA is computed under $\mathbb{Q}$; regulatory capital uses real-world exposure. Different numbers, different models, never to be conflated.
CVA uses $\mathbb{Q}$. It is a derivative price, so its EE profiles are calibrated to implied vols and CDS spreads. Put $\mathbb{P}$ in the CVA integral and you misprice the hedge.
Capital uses $\mathbb{P}$. Basel III requires stress-period historical calibration for the IMM exposure-at-default (BCBS 279, paragraph 32). Banks without IMM approval use SA-CCR instead: replacement cost plus a supervisory add-on, no simulation, far less sensitive to portfolio composition. That insensitivity is the price of not running a model.
PFE for internal limits is a desk choice on which practice is divided: many dealers run it under $\mathbb{P}$ to align with capital, others stay in $\mathbb{Q}$ for consistency with the pricing chain (Gregory 2020, ch. 8). The answers differ, so any limit framework should say which it uses. The code in this series is the risk-neutral engine, the one that prices CVA and DVA; the real-world engine that IMM capital needs is not built here.
Exposure profiles by product type
What a notional re-exchange does to a hump
A cross-currency swap re-exchanges notionals on one date, so peak exposure lands there rather than mid-life: $1{,}298{,}947.36$ USD, $3.7$ times the swap's own peak. Navy adds an illustrative spike, not a repriced CCS.
Hump against ramp, each scaled to its own peak
Hump or ramp turns on whether cashflows amortise. The swap sheds a coupon a year; an FX forward has one settlement date, so exposure grows right up to it. Navy: illustrative $\sqrt{t}$ shape.
A bought European option runs the other way from the intuition that an option bleeds value as expiry approaches. Its value is never negative, so there is no positive part to take, and with no coupons leaving the trade $V(t)/B(t)$ is a martingale: EE$(t) = V(0)/P(0,t)$ under deterministic rates. Exposure rises to expiry, then cliffs to zero. That intuition holds the underlying still; an expectation does not. Sold, $V(t) \leq 0$ and EE is identically zero.
Computing exposure in code
This listing is the whole chain, market data to metric, and it produced every library curve on this page.
Python · PyTorch
from datetime import date
import torch
from xvafoundations.calibration import Stripper
from xvafoundations.conventions import DayCount, year_fraction
from xvafoundations.curves import InterpolationMethod
from xvafoundations.data import sofr
from xvafoundations.instruments import (
build_instruments, initial_rates, pillar_maturities,
)
from xvafoundations.models import HullWhite1F
from xvafoundations.pricing import SwapSpec, reprice_swap_paths
from xvafoundations.xva.exposure import (
compute_ee, compute_ene, compute_eee, compute_eepe, compute_pfe,
)
torch.set_default_dtype(torch.float64)
# ── Market: the IR-01 SOFR curve, bootstrapped from its 14 instruments ──
specs, valuation_date = sofr.INSTRUMENTS, sofr.VALUATION_DATE
stripper = Stripper(
maturities=torch.tensor(pillar_maturities(specs, valuation_date)),
initial_rates=torch.tensor(initial_rates(specs)),
instrument_factory=lambda c: build_instruments(specs, valuation_date, c),
method=InterpolationMethod.LOGLINEAR,
)
stripper.calibrate()
curve = stripper.get_curve()
# ── Model: Hull-White 1F. a and sigma are ASSERTED, not calibrated;
# IR-05 and IR-06 are not yet published. Both sit in the range a USD desk sees.
model = HullWhite1F(a=0.05, sigma=0.007, curve=curve)
# ── Trade: 10Y payer swap, 10,000,000 USD, struck at its own par rate.
# Payment times are ACT/360 year fractions, so the first is 1.013889.
schedule = [
year_fraction(valuation_date,
date(valuation_date.year + 1 + k, 1, 15), DayCount.ACT360)
for k in range(10)
] # 1.013889, 2.027778, ..., 10.144444
par = curve.par_swap_rate(torch.tensor(0.0), torch.tensor(schedule)).item()
spec = SwapSpec(notional=1.0e7, fixed_rate=par,
fixed_payment_times=schedule, pay_fixed=True)
# ── Simulate the short rate, then reprice on every path at every date.
short_rates, times = model.simulate(5000, 41, 10.25, seed=42)
mtm = reprice_swap_paths(model, spec, short_rates, times) # (5000, 42)
ee = compute_ee(mtm) # EE(t_j): shape (42,)
ene = compute_ene(mtm) # ENE(t_j): shape (42,), positive
eee = compute_eee(ee) # EEE(t_j): running maximum
eepe = compute_eepe(ee, times, cap=1.0) # EEPE: scalar, one-year window
pfe = compute_pfe(mtm, alpha=0.975) # PFE(97.5%): shape (42,)
# EPE is the time-average of EE over the full horizon. There is no
# compute_epe: it is one line of the trapezoid rule compute_eepe
# already applies to EEE.
epe = torch.trapezoid(ee, times) / times[-1]
k, m = int(ee.argmax()), int(pfe.argmax())
print(f"par fixed rate {par * 100:.4f}%")
print(f"EPE [0, 10.25Y] {epe.item():>12,.2f}")
print(f"EEPE [0, 1Y] {eepe.item():>12,.2f}")
print(f"peak EE t={times[k].item():5.2f}Y {ee[k].item():>12,.2f}")
print(f"EEE freezes at {eee.max().item():>12,.2f}")
print(f"peak PFE t={times[m].item():5.2f}Y {pfe[m].item():>12,.2f}")
print(f"ENE at the EE peak {ene[k].item():>12,.2f}")
Output
par fixed rate 4.1200%
EPE [0, 10.25Y] 238,137.54
EEPE [0, 1Y] 120,866.59
peak EE t= 5.00Y 347,746.36
EEE freezes at 347,746.36
peak PFE t= 5.00Y 1,459,983.78
ENE at the EE peak 220,767.43
ENE at the EE peak is $220{,}767.43$ against an EE of $347{,}746.36$. That $126{,}978.93$ USD gap is neither noise nor a directional view.
They take any (num_paths, num_times) MtM tensor, and they are pure PyTorch, so CS01 and IR delta come from backpropagating the chain: quotes → curve → HW paths → MtM → EE → CVA.
Negative exposure and DVA
ENE is the expectation of the negative part of $V(t)$:
Notation deviation, stated once. NOTATION_CONVENTIONS.md defines ENE as a single negative number, the time-average of $\mathbb{E}^{\mathbb{Q}}[\min(V(t),0)]$. This series and compute_ene use the time-indexed, sign-flipped form above, so ENE$(t)$ stands to DVA as EE$(t)$ stands to CVA, and is plotted positive. A chart whose sign contradicts its own code is worse than a stated deviation.
ENE is to DVA what EE is to CVA, with your own survival curve in place of theirs (Chapter 03).
EE and ENE, both plotted positive
Full-horizon EE averages $238{,}137.54$ USD against $163{,}293.67$ for ENE, a ratio of $1.46$. ENE is drawn positive, as compute_ene returns it.
That gap is not about how often $V(t)$ is positive: at year 5 the swap is in your favour on $59.06\%$ of paths, a near coin flip, while EE runs $58\%$ above ENE. Since $\max(V,0) - \max(-V,0) = V$,
EE minus ENE is the expected mark-to-market
$$\text{EE}(t) - \text{ENE}(t) = \mathbb{E}^{\mathbb{Q}}\!\left[V(t) \;\middle|\; \mathcal{F}_0\right]$$
They separate to the extent that the remaining trade is expected to be worth something. It is tempting to object that an at-market swap has $V(0) = 0$ with $V(t)/B(t)$ a $\mathbb{Q}$-martingale. It is not. The martingale is the gains process, which adds back the coupons already paid:
The gains process is the martingale, not the trade value
$$G(t) = \frac{V(t)}{B(t)} + \sum_{T_j \leq t} \frac{C_j}{B(T_j)}, \qquad G(0) = \frac{V(0)}{B(0)} = 0$$
$B(t)$: the money market account $\exp\!\int_0^t r(s)\,ds$. $C_j$: the net cashflow settled at $T_j$.$V(t)$ is what is left, so $\mathbb{E}^{\mathbb{Q}}[V(t)/B(t)]$ is minus the time-0 value of coupons already paid, and zero only before the first payment.
That is falsifiable. The four net coupons settled by year 5 are worth $-88{,}445.43$ USD at time 0, so $\mathbb{E}^{\mathbb{Q}}[V(5)/B(5)]$ should be $+88{,}445.43$; the simulation returns $83{,}229$ against a Monte Carlo standard error of $8{,}237$. Nor is this a directional view on rates: the fixed rate is one blend of the whole forward strip, so early net coupons have negative time-0 value and late ones positive.
The difference is $\mathbb{E}^{\mathbb{Q}}[V(t)]$ exactly, climbing to $140{,}085$ USD by year 6. A receiver swap is the mirror image.
References
Gregory, J. (2020). The xVA Challenge: Counterparty Risk, Funding, Collateral, Capital and Initial Margin, 4th ed. Wiley.
Crépey, S. (2015). Bilateral counterparty risk under funding constraints, Part I and II. Mathematical Finance, 25(1).
Brigo, D., Morini, M. & Pallavicini, A. (2013). Counterparty Credit Risk, Collateral and Funding. Wiley.
Basel Committee on Banking Supervision (2014). BCBS 279: The standardised approach for measuring counterparty credit risk exposures.
Pykhtin, M. & Zhu, S. (2007). A guide to modelling counterparty credit risk. GARP Risk Review, July/August.
XVA-02 extends this to a netting set with collateral.
All views expressed are strictly personal and do not represent any past or current employer. Content is educational; nothing here constitutes financial, investment, or trading advice.