Prerequisites
  • IR-01, whose SOFR curve prices this swap, and XVA-00. You need mark-to-market; nothing else.
  • IR-08 (draft) derives the repricer, but xvafoundations.pricing.reprice_swap_paths already ships, so $V(t, \omega)$ is built here.

By the end, you can compute EE, EPE, PFE and EEPE from a simulated mark-to-market matrix.

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.

The corner at zero that creates exposure Upper panel: your loss if the counterparty defaults, plotted against the mark-to-market V at year five, both axes in US dollars and on the same scale. A grey dashed line is V itself, at forty-five degrees through the origin. A thick crimson line is the maximum of V and zero: flat along the horizontal axis for negative V, then rising at forty-five degrees for positive V, with a filled dot at the corner. The left half is labelled you owe them, your loss is zero; the right half is labelled they owe you, you are exposed. Two of the thirty sample paths are marked at year five. Sample path 22 is worth minus 640,972 dollars and sits on the flat part, so the loss is zero. Sample path 12 is worth plus 631,315 dollars and sits on the rising part, so the whole 631,315 is at risk. Two moves of almost the same size, one loss. Lower panel: the same corner mirrored, E minus of t equals the maximum of minus V and zero, which is the quantity that feeds DVA. YOUR LOSS IF THEY DEFAULT AT YEAR 5 you owe them your loss is zero they owe you you are exposed the kink 631,315 path 12 path 22 MtM = V(5) 0 −640,972 0 +631,315 V(5), mark-to-market to you (USD) E(5) = max(V, 0), USD THE OTHER SIDE, FOR DVA V < 0 V > 0 E(t) = max(−V, 0) Same corner, other side. This is what feeds DVA.
Figure 1 · XVA-01 Exposure 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:

Expected Exposure $$\text{EE}(t) = \mathbb{E}^{\mathbb{Q}}\!\left[\max(V(t),\, 0)\;\middle|\;\mathcal{F}_0\right]$$
$\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)$.

Every profile here is 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 and repriced under Hull-White 1F, $N = 5{,}000$ paths, quarterly to 10.25 years. The parameters $a = 0.05$, $\sigma = 0.007$ are asserted: IR-05 and IR-06 are unwritten.

Expected Positive Exposure is the time-average of that profile:

Expected Positive Exposure (EPE) $$\text{EPE} = \frac{1}{T} \int_0^T \text{EE}(t)\, dt$$
$T$: trade maturity. Trapezoid over the grid, which runs one quarter past the swap's ACT/360 maturity of 10.144444.
One time slice: the 5,000 simulated values of V at year 5 A histogram of the mark-to-market of the swap at year 5 across 5,000 simulated paths, in 40 bins of 121,096 dollars each. The mass to the left of zero is drawn hatched and greyed: the positive part sets this whole half to zero, 41 per cent of the paths. Five markers are dropped onto the value axis, each with a leader line to a label. ENE at year 5 is 220,767 dollars. The risk-neutral mean of V is 126,979 dollars. EPE, the average of EE over all dates, is 238,138 dollars. EE at year 5 is 347,746 dollars. PFE at 97.5 per cent is 1,459,984 dollars, with the 2.5 per cent tail beyond it shaded and 125 of the 5,000 paths lying above it. A note below records the identity: EE minus ENE, 347,746 minus 220,767, equals 126,979, the risk-neutral expectation of V. ONE SLICE: THE 5,000 VALUES OF V(5) Year 5, where EE peaks. Bar width 121,096 USD. 200 0 paths per bin max(V, 0) zeroes this whole half: 41% of the paths 2.5% −2M −1M 0 +1M +2M ENE(5) = 220,767 the mean, 126,979 EPE = 238,138 EE(5) = 347,746 on average PFE(5, 97.5%) = 1,459,984 125 of the 5,000 paths lie beyond it EE(5) − ENE(5) = 347,746 − 220,767 = 126,979 = EQ[V(5)]. The two parts add back to the MtM.
Figure 2 · XVA-01 EE, 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:

Effective Expected Exposure (EEE) $$\text{EEE}(t_j) = \max_{k \leq j}\, \text{EE}(t_k)$$

Effective EPE averages EEE over the first year, or maturity if shorter (BCBS 279):

Effective EPE (BCBS 279, Basel III IMM) $$\text{EEPE} = \frac{1}{\min(1,T)} \int_0^{\min(1,T)} \text{EEE}(t)\, dt$$
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:

Potential Future Exposure $$\text{PFE}(t,\, q) = Q_{q}\!\left[\max(V(t),\, 0)\right]$$
$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.
Five exposure metrics on one profile, and the sawtooth the textbooks smooth away Upper panel: exposure in US dollars against time in years, for a ten-year payer swap on ten million dollars over 5,000 simulated paths on a quarterly grid. PFE at 97.5 per cent, in navy, rises to 1,459,984 dollars at year 5 and falls to zero at maturity. EE, in crimson with a soft fill beneath it, rises to 347,746 dollars on the same date and falls to zero. EEE, a crimson dashed line, tracks EE upward and then freezes flat at 347,746 dollars for the rest of the trade. EPE, a gold dashed horizontal line at 238,138 dollars, is the flat level enclosing the same area as EE over the whole 10.25 years. EEPE is a gold band covering the first year only, with a bold gold segment at 120,867 dollars inside it: the average of EEE over that one-year window, which is why it is roughly half of EPE even though EEE never falls below EE. Lower panel: the same EE profile zoomed, against a grey dashed textbook curve proportional to the square root of t times T minus t, scaled to the same peak. The textbook curve is smooth and peaks at year 3.4; the library profile is a sawtooth that peaks at year 5, with a dot marking each of the seven annual coupon settlements at years 3.25 through 9.25, where the coupon leaves the trade and exposure drops. FIVE METRICS, ONE PROFILE 10Y payer swap, 10,000,000 USD, 5,000 paths. 1,459,984 347,746 238,138 120,867 0 PFE(t, 97.5%) EEE(t), running max EE(t) EPE EEPE: first year only EE(t) AGAINST THE TEXTBOOK HUMP 347,746 0 EE(t) textbook k√t (T − t) 024 6810 Time (years) Each dot is an annual coupon leaving the trade.
Figure 3 · XVA-01 Five 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.

Which is why large desks do not choose. They run two simulation engines in parallel: a risk-neutral one for CVA, DVA and FVA pricing, and a real-world one for IMM capital and credit-limit monitoring. Two calibrations, two sets of exposure profiles, one trade population. The code in this series is the risk-neutral engine.

Exposure profiles by product type

What a notional re-exchange does to a hump
The spike reaches 1,298,947 USD against a hump peak of 347,746.
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
The swap peaks mid-life and amortises away; the FX forward rises to its single settlement date.
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 unwritten. 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)$:

Expected Negative Exposure $$\text{ENE}(t) = \mathbb{E}^{\mathbb{Q}}\!\left[\max(-V(t),\, 0)\;\middle|\;\mathcal{F}_0\right]$$
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
EE and ENE separate from about year 2.5, EE running 46 percent higher.
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

XVA-02 extends this to a netting set with collateral.