A neural network options hedger trained end-to-end in TensorFlow that learns to outperform Black-Scholes delta hedging under stochastic volatility and transaction costs.
When a dealer sells a call option, they are exposed to the risk that the underlying price rises above the strike at expiry. The standard industry solution is Black-Scholes delta hedging — continuously buying and selling the underlying stock in amounts prescribed by the BS formula.
BS delta hedging has two known weaknesses:
- It assumes constant volatility — in practice, vol changes throughout the day
- It rebalances every period without considering transaction costs
This project trains a deep hedging neural network to discover a better hedging policy from simulated market data, without being told the model or the true volatility. The network must infer the current vol regime from observable price history and adapt its hedging accordingly.
Each of 5,000 synthetic price paths is generated using the Heston stochastic volatility model:
dS = μ·S·dt + σₜ·S·dW₁
dσ = κ·(θ - σ)·dt + ξ·σ·dW₂
dW₁·dW₂ = ρ·dt
| Parameter | Value | Meaning |
|---|---|---|
| κ (kappa) | 5.0 | Mean reversion speed — vol snaps back to its starting level |
| ξ (xi) | 0.3 | Vol-of-vol — how much vol itself moves each step |
| ρ (rho) | −0.7 | Price-vol correlation (leverage effect) |
| σ₀ | Uniform(0.2, 1.2) | Per-path starting vol, drawn fresh each path |
Each path starts with a different initial volatility σ₀. The Black-Scholes hedger is given only σ₀ and uses it frozen for all 375 timesteps. The true vol drifts away via Heston — so BS becomes increasingly wrong as the day progresses.
| Agent | What it knows |
|---|---|
| Black-Scholes | σ₀ only (frozen at day start) |
| Deep Hedger | Observable path features at every step (see below) |
The DH cannot observe the true vol directly either — but it can infer the current vol regime from the rolling realised volatility computed from recent price returns.
The model is a single-step delta predictor called recurrently at each of 375 timesteps:
Inputs (6 features per timestep):
1. log(Sₜ / K) — log-moneyness
2. τₜ — time to expiry (years)
3. BS_deltaₜ — Black-Scholes delta (frozen-vol baseline)
4. δₜ₋₁ — previous hedge position (recurrent state)
5. realised_volₜ — 10-min rolling annualised vol (vol regime signal)
6. BS_gammaₜ — BS gamma (hedge sensitivity signal)
Architecture:
Concatenate(6) → Dense(64, tanh) → LayerNorm
→ Dense(64, tanh) → LayerNorm
→ Dense(32, tanh)
→ Dense(1, sigmoid) # delta ∈ (0, 1)
LayerNorm is used instead of BatchNorm because the model is called 376 times per training step during recurrent unrolling. BatchNorm accumulates running statistics across calls and explodes; LayerNorm normalises within each single forward pass and is stable.
The model is trained to minimise Conditional Value at Risk at α = 10%:
CVaR₁₀% = -E[ PnL | PnL ≤ VaR₁₀% ]
This directly targets the worst 10% of hedging outcomes rather than the average, making the learned policy risk-aware.
PnL = -Payoff(Sᵀ) + Σₜ δₜ·(Sₜ₊₁ - Sₜ) - Σₜ c·Sₜ·|Δδₜ|
└─ option liability ─┘ └─ trading gains ─┘ └─ transaction costs ─┘
Transaction cost rate c = 0.001 (10 bps per unit of stock traded). All prices are normalised by S₀ internally to keep PnL values O(0.01–0.1) for numerical stability.
| Setting | Value |
|---|---|
| Paths | 5,000 (80/20 train/test split) |
| Timesteps per path | 375 (1 trading day at 1-minute frequency) |
| Batch size | 64 |
| Epochs | 250 |
| Learning rate | Warmup (5 epochs, 0 → 3×10⁻⁴) then cosine decay to 10⁻⁵ |
| Gradient clipping | Global norm ≤ 5.0 |
| Optimiser | Adam |
| Checkpointing | Saved to Google Drive after every epoch |
deep-hedger-heston/
│
├── deep_hedger_heston.ipynb # Main notebook — run in Google Colab
├── requirements.txt # Python dependencies
├── README.md # This file
Trained for 250 epochs on 4,000 paths, evaluated on 1,000 held-out test paths. All P&L values are normalised by S₀ = 1 (so −0.021 means a loss of 2.1% of the initial stock price).
| Metric | Deep Hedger | Black-Scholes | DH Improvement |
|---|---|---|---|
| Mean P&L (test) | −0.02110 | −0.02462 | +14.3% |
| Std P&L (test) | 0.00705 | 0.00769 | −8.3% variance |
| VaR 10% (test) | −0.03047 | −0.03478 | +12.4% |
| CVaR 10% (test) | −0.03283 | −0.03754 | +12.5% |
The deep hedger achieves a 14.3% improvement in mean P&L and a 12.5% improvement in CVaR on unseen test paths. It also has a tighter P&L distribution (8.3% lower standard deviation), meaning it hedges more consistently as well as more profitably.
No overfitting: train and test CVaR differ by only 1.8%, and train/test mean P&L differ by 1.1% — the model generalises cleanly.
| Metric | Train | Test | Gap |
|---|---|---|---|
| Mean P&L | −0.02133 | −0.02110 | 1.1% |
| CVaR 10% | −0.03343 | −0.03283 | 1.8% |
The deep hedger outperforms Black-Scholes in all four volatility regime buckets on the test set:
| Vol bucket | n | DH CVaR | BS CVaR | DH advantage | Avg intraday drift |
|---|---|---|---|---|---|
| Low σ₀ < 0.4 | 199 | −0.01484 | −0.01993 | +25.5% | 0.0048 |
| Mid 0.4–0.7 | 326 | −0.02255 | −0.02644 | +14.7% | 0.0084 |
| High 0.7–1.0 | 282 | −0.02985 | −0.03498 | +14.7% | 0.0117 |
| Very high σ₀ ≥ 1.0 | 193 | −0.03651 | −0.04120 | +11.4% | 0.0170 |
The DH advantage is largest in the low volatility bucket (+25.5%) and remains significant even at very high volatility (+11.4%). The advantage is consistent, not concentrated in a single regime — the model learned a broadly better hedging policy rather than exploiting one specific market condition.
The low-vol advantage is particularly meaningful: on calm paths, BS still rebalances aggressively every minute and pays full transaction costs. The DH has learned to trade less when gamma is small, saving meaningfully on costs when it matters most.
See requirements.txt. Core libraries: TensorFlow 2.x, NumPy, SciPy, scikit-learn, Matplotlib, Seaborn.