Counterparty Risk and CVA › Computing Expected Exposure via Monte Carlo

Michael BrenndoerferDecember 23, 20256 min read
Quantitative FinanceAll chapters
Part 10 of 15Risk Management and Financial Risk Controls
Chapter 56Counterparty Risk and CVA › Computing Expected Exposure via Monte Carlo
Reading section 5 of 16 · Computing Expected Exposure via Monte Carlo · 6 minSection 5 of 16 · 6 min

Book contents

Section 5 of 16 · 6 min read

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.

In[5]:
Code
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).

In[6]:
Code
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 only

For 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.

In[7]:
Code
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)
Out[8]:
Visualization
Three hump-shaped interest-rate-swap exposure curves over ten years: expected exposure peaks near 50 thousand dollars, 95 percent PFE near 175 thousand, and 99 percent PFE near 250 thousand before all decline to zero at maturity.
Exposure profile for a 10-year interest rate swap showing expected exposure (EE) and potential future exposure at 95% and 99% confidence levels. The characteristic hump shape reflects the balance between potential rate movements and decreasing time to maturity.

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.

Out[9]:
Visualization
Histogram of simulated swap exposures at year five showing a right-skewed distribution truncated at zero, with vertical lines marking the expected exposure and 95th and 99th percentile PFE levels.
Distribution of swap exposures at year 5 (mid-life) across all simulation paths. The distribution is right-skewed because exposure equals max(V, 0), truncating negative values. The expected exposure is the mean of this distribution.