This document provides the mathematical formulations, discretization schemes, and functional Python API examples for the sixteen volatility and asset pricing models implemented in the DeepVol framework.
The Heston model (1993) represents the asset price
where:
-
$\kappa > 0$ is the mean-reversion speed. -
$\theta > 0$ is the long-term variance. -
$\sigma > 0$ is the volatility of volatility. -
$\rho \in [-1, 1]$ is the correlation between the asset and variance shocks (the leverage effect). -
$v_0 > 0$ is the initial variance.
Option prices are computed by integrating the model's characteristic function
where:
import numpy as np
from deepvol.models.heston import HestonEngine
# Instantiate the engine
engine = HestonEngine()
# Model parameters
params = {
"kappa": 2.0,
"theta": 0.04,
"sigma": 0.3,
"rho": -0.7,
"v0": 0.04
}
# Grid definitions
T_grid = np.array([0.5, 1.0])
K_grid = np.array([-0.1, 0.0, 0.1]) # Log-moneyness
# Compute implied volatility surface
iv_surface = engine.price_surface(params, T_grid, K_grid, S0=100.0)
print("IV Surface:\n", iv_surface)The Rough Heston model (El Euch & Rosenbaum 2019) replaces the standard mean-reversion drift with a fractional integral of Hurst parameter
To make path simulations computationally tractable, the fractional kernel is approximated by a sum of
import torch
from deepvol.surrogates.fno_model import MirrorPaddedFNO2d
from deepvol.calibration.calibrate_fast import _make_spatial_input
device = "cuda" if torch.cuda.is_available() else "cpu"
model = MirrorPaddedFNO2d(param_dim=6).to(device)
model.load_state_dict(torch.load("artifacts/weights/fno_v3_final_prod.pth", map_location=device))
model.eval()
# Normalized parameters: [kappa, theta, sigma, rho, v0, H]
theta = torch.tensor([[2.0, 0.04, 0.5, -0.7, 0.04, 0.08]], dtype=torch.float32, device=device)
spatial = _make_spatial_input(T_grid=np.linspace(0.1, 2.0, 8), K_grid=np.linspace(-0.5, 0.5, 11), device=device)
with torch.no_grad():
normalized_output = model(spatial, theta)
print("Output Shape:", normalized_output.shape)The Rough Bergomi model (Bayer, Friz & Gatheral 2016) is a lognormal rough volatility model where the variance process is defined as:
where
Paths are simulated on a discrete time grid
import numpy as np
from deepvol.models.rbergomi_gpu import rBergomiEngine
engine = rBergomiEngine()
T_grid = np.array([0.5, 1.0])
K_grid = np.array([-0.1, 0.0, 0.1])
# Run path simulations and compute implied volatilities on GPU/CPU
ivs = engine.price_surface(
v0=0.04, H=0.1, eta=1.5, rho=-0.7,
T_grid=T_grid, K_grid=K_grid, N_paths=10000
)
print("Rough Bergomi IV Surface:\n", ivs)The Local Stochastic Volatility (LSV) model formulated as a McKean-Vlasov SDE adjusts the stochastic volatility process with a leverage function
According to Dupire's equation, the Leverage function
The conditional expectation is evaluated using a particle system of size
where
import torch
from deepvol.models.mlsv_gpu import MLSVSolverGPU
# Define Dupire local volatility function
def dupire_vol(t, s):
return torch.full_like(s, 0.2)
solver = MLSVSolverGPU(
S0=100.0, r=0.0, q=0.0, v0=0.04, kappa=2.0, theta=0.04, xi=0.3, rho=-0.7,
T=1.0, steps_per_unit=50, N_paths=2000, dupire_vol_fn=dupire_vol
)
# Simulate McKean-Vlasov particle system on GPU
solver.simulate(method="nadaraya_watson")
option_prices = solver.price_european_option(strike=torch.tensor([90.0, 100.0, 110.0]), maturity=np.array([0.5, 1.0]))
print("Simulated LSV Option Prices:\n", option_prices)The SABR model (Hagan et al. 2002) is a two-factor stochastic volatility model:
where
The Displaced SABR extension replaces
import numpy as np
from deepvol.models.sabr import sabr_iv_surface
# Generate IV surface under SABR model
sabr_surface = sabr_iv_surface(
F=100.0,
T_grid=np.array([0.5, 1.0]),
k_grid=np.array([-0.1, 0.0, 0.1]),
alpha=0.2, beta=0.5, rho=-0.5, nu=0.4,
iv_type="lognormal"
)
print("SABR IV Surface:\n", sabr_surface)The Surface SVI (SSVI) model (Gatheral & Jacquier 2011) parameterizes the implied volatility surface using total variance slices
where
No-arbitrage conditions require
import numpy as np
from deepvol.models.sabr import ssvi_iv_surface
T_grid = np.array([0.25, 0.5, 1.0])
k_grid = np.array([-0.2, 0.0, 0.2])
theta_grid = 0.04 * T_grid # ATM variance linear in maturity
ssvi_surface = ssvi_iv_surface(T_grid, k_grid, theta_grid, rho=-0.4, eta=1.2, gamma=0.5)
print("SSVI IV Surface:\n", ssvi_surface)Dupire's local volatility (1994) represents volatility as a deterministic function of time
Using an implied volatility surface
import numpy as np
from deepvol.models.local_vol import DupireLocalVolSolver
# Generate dummy input IV surface
T_grid = np.array([0.5, 1.0])
K_grid = np.array([-0.2, 0.0, 0.2])
iv_surface = np.full((len(T_grid), len(K_grid)), 0.20)
solver = DupireLocalVolSolver(T_grid, K_grid, S0=100.0, r=0.05, q=0.0)
lv_surface = solver.solve(iv_surface)
print("Local Volatility Surface:\n", lv_surface)A Neural Stochastic Differential Equation (Neural SDE) parameterizes the drift
Training is completed by defining a loss function torchsde:
import torch
import torch.nn as nn
class DiffusionMLP(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(nn.Linear(2, 32), nn.Softplus(), nn.Linear(32, 1), nn.Softplus())
def forward(self, t, x):
# Returns state-dependent diffusion coefficient
inputs = torch.cat([t.unsqueeze(-1), x.unsqueeze(-1)], dim=-1)
return self.net(inputs).squeeze(-1)
net = DiffusionMLP()
t = torch.tensor(0.5)
x = torch.tensor(100.0)
print("Neural Diffusion at (t=0.5, S=100):", net(t, x).item())The Signature Volatility model expresses the asset's variance
where
import torch
from deepvol.models.signature_vol import compute_signature_paths
# Generate paths of shape (batch, time, features)
paths = torch.randn(10, 100, 2)
signatures = compute_signature_paths(paths, depth=3)
print("Signature Features Shape:", signatures.shape)The Schwartz-Smith (2000) model represents the log commodity spot price
The state-space model transition is defined as:
where
import numpy as np
from deepvol.models.schwartz_smith import SchwartzSmithEngine
engine = SchwartzSmithEngine(
kappa=1.2, mu_y=0.05, sigma_x=0.3, sigma_y=0.15, rho_xy=-0.3
)
# Price commodity futures options using analytical Black-76 mapping
option_price = engine.price_option(
spot=100.0, strike=105.0, maturity=0.5, risk_free_rate=0.05, is_call=True
)
print("Schwartz-Smith Futures Option Price:", option_price)The PI-M-FNO surrogate integrates the Dupire local volatility partial differential equation directly into operator training:
During extreme market stress or regime shifts, the base operator weights
import torch
from deepvol.surrogates.meta_fno import MetaLearnerFNO
from deepvol.surrogates.pde_loss import compute_pde_loss_grid
device = "cuda" if torch.cuda.is_available() else "cpu"
meta_fno = MetaLearnerFNO(param_dim=6, inner_lr=1e-3).to(device)
# Parameter vector and spatial evaluation grid
theta = torch.tensor([[2.5, 0.05, 0.6, -0.75, 0.05, 0.09]], device=device)
spatial_grid = torch.randn(1, 8, 11, 2, device=device)
# Perform online inner adaptation step (< 10 ms)
adapted_surface = meta_fno.adapt_online(theta, spatial_grid, num_inner_steps=2)
print("Adapted Volatility Surface Shape:", adapted_surface.shape)D-XVA unifies model calibration, implied volatility inversion via PIVOT, and recurrent deep hedging into a single differentiable computational graph:
where
import torch
from deepvol.hedging.deep_hedging import DeepHedgingEngine
from deepvol.hedging.d_xva import compute_dxva_loss
device = "cuda" if torch.cuda.is_available() else "cpu"
hedger = DeepHedgingEngine(input_dim=5, hidden_dim=64, num_layers=2).to(device)
# Batch of simulated asset and implied vol trajectories (batch, time, features)
trajectories = torch.randn(128, 50, 5, device=device)
strike = 100.0
# Compute hedge ratios and differentiable trading loss
deltas = hedger(trajectories)
loss = compute_dxva_loss(deltas, trajectories[:, :, 0], strike=strike, cost_bps=5.0)
print("D-XVA Optimization Loss:", loss.item())The Grey Rough Bergomi model generalizes rough Bergomi by driving the variance process with generalized grey Brownian motion (ggBm)
The characteristic function of ggBm is governed by the two-parameter Mittag-Leffler function
import torch
from deepvol.models.rbergomi_gpu import rBergomiEngine
device = "cuda" if torch.cuda.is_available() else "cpu"
engine = rBergomiEngine()
# Simulate paths on GPU using Wood-Chan circulant embedding
paths = engine.simulate_paths(
v0=0.04, H=0.10, eta=1.4, rho=-0.75,
N_paths=10000, N_steps=100, T=1.0, device=device
)
print("gRB Simulated Asset Paths Shape:", paths.shape)An autocallable structured note defines early redemption stopping times
The multi-fidelity valuation decomposes pricing into an analytical Gatheral boundary PDE base
import torch
from deepvol.surrogates.correction_ensemble import CorrectionEnsemble
from deepvol.mrm.autocall_guardian import AutocallGuardian
device = "cuda" if torch.cuda.is_available() else "cpu"
ensemble = CorrectionEnsemble.load_default(device=device)
guardian = AutocallGuardian(ensemble)
contract = {
"spot": 100.0, "strike": 100.0, "autocall_barrier": 105.0, "dip_barrier": 70.0,
"coupon_rate": 0.08, "volatility": 0.22, "rate": 0.03, "dividend": 0.01,
"maturity": 2.0, "obs_freq": 0.5
}
result = guardian.price_contract(contract)
print(f"Autocall NPV: {result['price']:.4f} ± {result['uncertainty_bps']:.2f} bps")
print(f"Guardian Routing: {result['routing']}, Fallback Active: {result['fallback_active']}")Phoenix notes pay conditional memory coupons at observation dates
Early redemption occurs if
import torch
from deepvol.models.phoenix import price_phoenix_mc
device = "cuda" if torch.cuda.is_available() else "cpu"
# Vector of contract specs: [S0, K, B_autocall, B_coupon, B_dip, coupon, vol, r, q, T, n_obs]
specs = torch.tensor([[100.0, 100.0, 105.0, 80.0, 70.0, 0.07, 0.20, 0.02, 0.01, 1.5, 6]], device=device)
price = price_phoenix_mc(specs, N_paths=50000, device=device)
print(f"Phoenix Note Price: {price.item():.4f}")Worst-of basket autocallables depend on the joint evolution of
The asset index permutation group
import torch
from deepvol.models.wof_autocall import price_wof_autocall_mc
from deepvol.surrogates.wof_autocall_egno import WorstOfAutocallEGNO
device = "cuda" if torch.cuda.is_available() else "cpu"
egno_model = WorstOfAutocallEGNO(n_assets=3, hidden_dim=64).to(device)
# Correlated basket specs: 3 asset spots, volatilities, and correlation matrix
spots = torch.tensor([100.0, 100.0, 100.0], device=device)
vols = torch.tensor([0.25, 0.22, 0.28], device=device)
corr = torch.tensor([[1.0, 0.6, 0.5], [0.6, 1.0, 0.55], [0.5, 0.55, 1.0]], device=device)
# Evaluate via EGNO surrogate
features = torch.cat([spots, vols, corr.flatten()]).unsqueeze(0)
basket_price = egno_model(features)
print(f"WoF Basket Autocall Price: {basket_price.item():.4f}")