feat: add the Heston SLV process, its leverage calibration and Python bindings - #37
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Heston stochastic-local volatility model was only half present: a Monte Carlo pricer plus a free-function leverage calibration, no
ProcessExtprocess (the processes page already listed aHestonSlvthat did not exist), noCalibrator, no Python surface, and a particle method that deviated from its reference (Silverman's rule on the log-spot sample, one Euler step per local-vol maturity, the middle strike used as the spot). This PR completes the model and brings the calibration onto the literature.Model
HestonSlvprocess instochastic-rs-stochastic::volatility::heston_slv: log-spot Euler with the crate's absorption scheme for the variance, the leverage anFn2Dof(t, S). Written as anExprit runs on a device through the new Euler familyHestonSlv(code 120, programpv); a closure, a Python callable or a tabulated grid keeps it on the host throughdevice_fallback. Witheta = 1andL ≡ 1it reproducesHestonLogbit for bit at the same seed, which a test pins.Grid2DandFn2D::Gridinstochastic-rs-distributions: a(t, x)lookup table, bilinear inside and flat outside, so a calibratedLeverageSurfaceconverts into a process coefficient (From<LeverageSurface> for Fn2D).Calibration
calibrate_leveragenow follows Guyon & Henry-Labordère exactly, with the constants as restated by Cozma, Mariapragassam & Reisinger (arXiv:1701.06001 §3.4): Gaussian kernel in spot, bandwidth1.5 · S₀ · σ_LV(S₀, t) · √max(t, 0.25) · N^(−1/5), the sorted-cloud windowΔK,L(s, 0) = σ_LV(s, 0) / √v₀, a leverage row at every Euler step, and the cloud's spots at every requested maturity so the caller can reprice the grid without a second simulation.calibrate_leverage_fokker_planckandheston_slv_density: the finite-volume solution of the forward Kolmogorov equation after Wyns & Du Toit (arXiv:1611.02961) — vertex-centred fluxes with zero boundary flux so the mass is conserved, the four-corner mixed flux with ghost values and the first-order fix abovev = 0, the cell-average Dirac start on meshes clustered at the spot and atv₀, Hundsdorfer–Verwer atθ = ½ + √3/6with Rannacher half-steps, and the inner iteration on the non-linearity (Q = 2).HestonSlvCalibrator(Calibrator,CalibrationResult,ToModel): a call-price grid in, a Heston fit (or pinned parameters), a Dupire local volatility (or a supplied grid), the leverage by eitherLeverageMethod, and the fit repriced from the calibration's own density.to_modelbuilds aHestonSlvPriceranchored to the calibration rates and refuses others; the mixing fractionetais an input, as in the literature.HestonSlvPricerreads the leverage at each step's start rather than its end.Python
PyHestonSlv,LeverageSurface,HestonSlvPricer,HestonSlvCalibrator(withmethod="particle"or"fokker_planck") andHestonSlvCalibrationResult, with smoke tests.Docs
New sections on the quant and processes pages backed by doctests, the GPU support matrix and counts,
CLAUDE.mdcounts, the CUDA notebook cell, and a design spec kept locally underdocs/as the earlier ones are.Breaking changes (pre-release API)
calibrate_leverage(params, s0, r, q, &Grid2D, &maturities, &ParticleMethod) -> Result<ParticleCalibration>replaces the old grid-and-seed signature and returns the cloud snapshots beside the surface.calibrate_from_dupireis removed;HestonSlvCalibratoris the market-data entry point and takes the spot explicitly.HestonSlvCalibrator::methodis aLeverageMethod.Verification
eta = 1, gives a median|L − 1|below 0.05 over the central strikes and reprices the surface with an RMSE below 0.25 on a spot of 100, by both the particle and the finite-volume route;eta = 0.5reprices as well.L ≡ 1: worst call-price error 0.003 against the closed-form Heston calls at the default mesh, mass conserved to 1e-11, the forward to 1e-6.eta = 0at the long-run variance: the particle leverage equals the closed formσ_LV / √v₀to 1e-9.HestonSlvPricerunderL ≡ 1matches the closed-form Heston call within three standard errors plus the Euler bias.--no-default-features, the docs audit, the full workspace battery (3143 passed, 0 failed) before the Fokker–Planck commit and the quant crate suite after it, and the Python test suite.Not done, left as follow-ups: Cozma et al.'s control variates for the particle method, a device path for a tabulated leverage (a 2-D table block in the engine), and a Python
Exprbuilder so the process can reach a device from Python.Validation round 2
HestonSLVFDMModelon its ownNoExceptLocalVolSurfaceof the analytic Heston Black surface, the same local volatility handed to our routes so only the leverage calibration is compared): the finite-volume leverage agrees with QuantLib's to a mean of 0.001–0.006 and a worst of 0.003–0.010 over a 4 × 5 probe grid, the particle leverage to a mean of 0.003–0.005 and a worst of 0.009–0.012, at η = 1 and η = 0.5. QuantLib's own FDM and MC models agree with each other to a mean of 0.005–0.010 and a worst of 0.010–0.024 on the same probes. Vanilla prices under the calibrated models: QuantLib's FD engine within 0.035 of the market, our Monte Carlo pricer within 0.04 (200k paths). Kept asstochastic-rs-py/tests/test_slv_quantlib.py, skipped unless QuantLib is installed.with_local_vol, which is the recommended input where a smooth surface exists.L = σ_LV / √V_texactly instead of estimating a deterministic variance from the density.HestonSlvpaths under a calibrated surface agrees withHestonSlvPricerwithin the two Monte Carlo errors.