Counterparty Risk and CVA › Computing Expected Exposure via Monte Carlo
Quantitative FinanceAll chapters Counterparty Risk and CVA › Computing Expected Exposure via Monte CarloOne section · Sections in this chapter5 / 16 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Calculating expected exposure profiles requires projecting the derivative's value across many future scenarios. This is a forward-looking exercise that demands simulation because, unlike current exposure, future exposure depends on how market conditions might evolve. Monte Carlo simulation, which we covered in Part III, Chapter 10, is the standard approach for this task.
The process involves simulating the underlying risk factors, calculating the derivative's value on each path at each time step, and taking the expected value of positive exposures. This approach is flexible enough to handle complex derivatives and can accommodate multiple risk factors, path dependencies, and optionality. Let's implement this for an interest rate swap.
import numpy as np
# Set random seed for reproducibility
np.random.seed(42)
# Simulation parameters
n_paths = 10000
n_steps = 40 # Quarterly steps for 10-year swap
dt = 0.25
T = 10.0
times = np.linspace(0, T, n_steps + 1)
# Interest rate model parameters (Vasicek)
r0 = 0.03 # Initial short rate
kappa = 0.15 # Mean reversion speed
theta = 0.04 # Long-term mean
sigma_r = 0.01 # Volatility
# Simulate short rate paths using Vasicek model
def simulate_vasicek(r0, kappa, theta, sigma, dt, n_steps, n_paths):
"""Simulate interest rate paths under Vasicek model."""
rates = np.zeros((n_paths, n_steps + 1))
rates[:, 0] = r0
for t in range(n_steps):
dW = np.random.standard_normal(n_paths) * np.sqrt(dt)
rates[:, t + 1] = (
rates[:, t] + kappa * (theta - rates[:, t]) * dt + sigma * dW
)
return rates
rate_paths = simulate_vasicek(r0, kappa, theta, sigma_r, dt, n_steps, n_paths)The Vasicek model, covered in Part III, Chapter 14, gives analytically tractable short-rate dynamics suitable for swap valuation. Each path represents a possible evolution of interest rates over the swap's life. The model's mean-reverting property ensures that simulated rates remain economically plausible over long horizons, neither exploding to infinity nor becoming deeply negative (though negative rates are possible in this model, which reflects the reality of modern interest rate markets).
import numpy as np
def swap_value(rate_path, t_idx, fixed_rate, notional, payment_times, dt):
"""
Calculate swap value (receive fixed) at time t_idx.
Simplified: assumes flat yield curve at current short rate.
"""
current_time = t_idx * dt
r_current = rate_path[t_idx]
# Remaining payment times
remaining_payments = payment_times[payment_times > current_time]
if len(remaining_payments) == 0:
return 0.0
# Discount future cash flows
value = 0.0
for payment_time in remaining_payments:
tau = payment_time - current_time
df = np.exp(-r_current * tau)
# Fixed leg pays fixed_rate * dt * notional
# Floating leg pays r_current * dt * notional (simplified)
fixed_cf = fixed_rate * dt * notional
floating_cf = r_current * dt * notional
# Receive fixed, pay floating
net_cf = fixed_cf - floating_cf
value += net_cf * df
return value
# Swap parameters
notional = 1_000_000 # $1 million notional
fixed_rate = 0.035 # 3.5% fixed rate
payment_times = np.arange(dt, T + dt, dt)
# Calculate exposure at each time step for each path
exposures = np.zeros((n_paths, n_steps + 1))
for t_idx in range(n_steps + 1):
for path in range(n_paths):
swap_val = swap_value(
rate_paths[path], t_idx, fixed_rate, notional, payment_times, dt
)
exposures[path, t_idx] = max(swap_val, 0) # Positive exposure onlyFor each simulation path and time step, we compute the swap's mark-to-market value and record the positive part as the exposure. This builds our exposure distribution at each future date. Note that we take the maximum of the swap value and zero, which reflects the basic idea that counterparty risk only materializes when the derivative has positive value to us.
import numpy as np
# Calculate exposure statistics
expected_exposure = exposures.mean(axis=0)
pfe_95 = np.percentile(exposures, 95, axis=0)
pfe_99 = np.percentile(exposures, 99, axis=0)
The exposure profile displays the characteristic hump shape for interest rate swaps. Early in the swap's life, exposure grows as rates can deviate from their initial values, creating potential gains on the position. Later, exposure declines as fewer payments remain, reducing the maximum possible value regardless of rate movements. The difference between EE and PFE reflects the uncertainty in future exposures; under adverse scenarios (high PFE), the counterparty could owe substantially more than the average. This gap between expected and potential exposure informs credit limit setting, where worst-case scenarios matter more than averages.
