A Python framework for computing stochastic Initial Margin (IM) in the context of Counterparty Credit Risk, comparing brute-force Nested Monte Carlo with the Johnson distribution approximation (McWalter et al., 2018).
This project computes exposure metrics (EE, EEE, EEPE) for a portfolio of European options under bilateral CSA with stochastic IM. It implements two approaches to estimate the conditional 99% VaR of P&L changes over the Margin Period of Risk (MPOR):
- Nested Monte Carlo (brute-force) — inner simulations at each outer node to build the empirical P&L distribution
- Johnson approximation — fits a Johnson distribution (SU/SB/SL/SN) using the first four conditional moments, avoiding the costly inner MC loop
The comparison demonstrates that the Johnson method achieves comparable accuracy with a speedup factor proportional to the number of inner scenarios.
- Multi-asset correlated GBM diffusion under the risk-neutral measure (exact discretization via Cholesky)
- Black-Scholes closed-form pricing for European calls and puts
- Nested Monte Carlo IM with configurable inner/outer scenario counts
- Johnson distribution fit using moment matching (skewness/kurtosis plane classification)
- Exposure metrics: Expected Exposure (EE), Effective EE, EEPE, and EAD computation
- Centralized configuration — all parameters in a single file (
config/parameters.py) - Fully vectorized NumPy implementation for performance
| # | Type | Underlying | S₀ | Strike | Maturity | Vol | Position |
|---|---|---|---|---|---|---|---|
| 1 | Call | A | 100 | 105 | 2Y | 20% | Long |
| 2 | Put | A | 100 | 95 | 2Y | 20% | Long |
| 3 | Call | B | 150 | 160 | 2Y | 25% | Long |
| 4 | Put | C | 80 | 75 | 2Y | 30% | Short |
| 5 | Call | C | 80 | 85 | 2Y | 30% | Long |
Correlations: ρ(A,B) = 0.6, ρ(A,C) = 0.4, ρ(B,C) = 0.5
| Parameter | Value | Description |
|---|---|---|
| Risk-free rate | 3% | Continuous compounding |
| MPOR | 10 business days (≈ 2/52 yr) | Standard bilateral CSA |
| EEPE horizon | 1 year | Regulatory standard |
| IM confidence | 99% | VaR quantile level |
| N_outer | 500 | Outer Monte Carlo scenarios |
| N_inner | 500 | Inner MC scenarios (nested only) |
| Time steps | 52 | Weekly grid over 1 year |
├── config/
│ └── parameters.py # All configurable parameters
├── lib/
│ ├── black_scholes.py # BS pricing (call/put)
│ ├── diffusion.py # Correlated multi-asset GBM simulation
│ ├── portfolio.py # Portfolio class and MtM computation
│ ├── margin.py # IM calculation (nested MC + exposure with IM)
│ ├── exposure.py # EE, EEE, EEPE metrics
│ ├── johnson.py # Johnson distribution fit and IM approximation
│ └── utils.py # Helpers, validation, timing
├── notebooks/
│ ├── 01_test_phase1.ipynb # Validation: BS pricing, GBM, exposure
│ ├── 02_test_phase2.ipynb # Validation: nested MC IM
│ └── 03_test_phase3.ipynb # Validation: Johnson approximation
├── tests/ # Unit tests (pytest)
├── report/ # Generated figures and theoretical writeup
├── main.py # Full pipeline: both methods + comparison
└── CLAUDE.md # Project specification
- Python 3.10+
- NumPy, SciPy, Matplotlib
pip install numpy scipy matplotlib# Full run (N_outer=500, N_inner=500)
python main.py
# Fast mode for quick testing (N_outer=200, N_inner=200)
python main.py --fastpytest tests/The pipeline produces:
- Console output: EEPE values, IM statistics, comparison table, timing
- Figures saved in
report/:fig1— Sample GBM paths for each underlyingfig2— EE/EEE profiles without IMfig3— EE/EEE comparison: no IM vs nested MC vs Johnsonfig4— Scatter plots: nested MC IM vs Johnson IMfig5— Mean IM over time + MAE between methodsfig6— IM distribution at t = 0.5Y
The Initial Margin at time t is defined as:
IM(t) = Q_99%(ΔPV | F_t) where ΔPV = PV(t) - PV(t + δ)
A positive ΔPV represents a portfolio loss over the MPOR period δ.
For each outer scenario at each time step:
- Simulate N_inner sub-scenarios over the MPOR
- Price the portfolio at t + δ using Black-Scholes
- Compute P&L changes: ΔPV = PV(t) - PV(t + δ)
- IM = empirical 99th percentile of ΔPV
Replaces the inner MC loop by:
- Estimating the first four conditional moments of ΔPV via polynomial regression
- Fitting a Johnson distribution (SU/SB/SL/SN) based on the (skewness², kurtosis) pair
- Computing the 99% quantile analytically
Residual exposure after IM collateralization:
E_IM(t) = max(0, ΔPV(t, t+δ) - IM(t))
Then EE → EEE → EEPE are computed as usual.
- McWalter, T., Kienitz, J., Nowaczyk, N., Rudd, R., & Acar, S. (2018). Dynamic Initial Margin Estimation Based on Quantiles of Johnson Distributions
- CRR Art. 284 — EEPE computation under the risk-neutral measure
- BCBS/IOSCO — Margin requirements for non-centrally cleared derivatives
Academic project — EXIOM Partners × CentraleSupélec (M2 Quantitative Finance).