regit-svi
Auditable SVI volatility smiles and surfaces in safe, std-only Rust.
regit-svi models total implied variance with Raw SVI, SVI Jump-Wings, and
SSVI; calibrates slices and SSVI surfaces; evaluates the implied continuous
risk-neutral density; and reports static-arbitrage evidence without turning a
finite numerical scan into a global certificate.
Why this crate exists
Option markets quote a sparse set of implied volatilities, while pricing and risk systems need a continuous smile at each maturity and a coherent surface between maturities. Interpolation or independent slice fits can introduce two economically material defects:
- negative call convexity, equivalently a negative continuous density at some strike; and
- decreasing total variance with maturity at fixed forward log-moneyness.
SVI gives those questions a compact analytic structure. The implementation keeps the model, diagnostics, calibration termination, and strength of the supporting evidence visible to the caller. A theorem, a sufficient condition, a theorem-guided binary64 search, and a bounded grid are represented as different evidence kinds.
Quick start
[]
= "2.0.0"
Upgrading from 1.x? Read the 2.0 migration guide before changing imports or interpreting arbitrage results.
use ;
NoViolationDetected must be read together with its evidence. For a regular
Raw slice it records a theorem-guided numerical search, not a formal analytic
certificate. See examples/quickstart.rs for the
slice, conversion, density, SSVI-calibration, and surface workflow.
Coordinates and parametrisations
SVI uses forward log-moneyness k = ln(K/F) and total Black implied variance
w = sigma_B^2 t. The helper log_moneyness evaluates ln(K) - ln(F) so a
representable strike and forward do not overflow merely because K/F does.
| Parametrisation | Implemented form | Intended use |
|---|---|---|
| Raw SVI | w(k)=a+b[rho(k-m)+sqrt((k-m)^2+sigma^2)] |
Single-slice model, derivatives, calibration, and diagnostics |
| SVI Jump-Wings | (v, psi, p, c, v_tilde) at maturity t |
ATM variance/skew and normalized wing observables |
| SSVI | w(k,theta)=theta/2[1+rho phi k+sqrt((phi k+rho)^2+1-rho^2)] |
A shared smile family over an ATM-variance term structure |
Constructors validate finiteness and the declared model domain. Invariant- bearing fields are private and exposed through accessors. Raw-to-Jump-Wings is defined for a positive ATM variance, including flat smiles, but the inverse is not globally identifiable: flat wings and an ATM minimum are rejected rather than assigned arbitrary Raw parameters. Every positive SSVI slice maps to Raw; a generic Raw slice has no SSVI inverse.
The low-level formula kernels intentionally remain infallible: Raw value and
derivative methods, Phi::eval, Ssvi::total_variance, g, d_plus,
d_minus, and the coordinate helpers accept raw f64. Callers must satisfy
their documented finite/model-domain preconditions; otherwise IEEE-754
NaN or infinity may propagate. Constructors, conversions, diagnostics,
density integration, and surface evaluation provide the validated/fallible
workflow when an error value is required.
Evidence model
Every ArbitrageAssessment couples a three-way status to an evidence value:
| Status | Meaning |
|---|---|
ViolationDetected |
A finite counterexample or failed analytic necessary condition was found. |
NoViolationDetected |
The stated procedure completed without finding a violation in its stated scope. |
Indeterminate |
Invalid support, non-finite evaluation, unresolved numerical boundary, or failure of a merely sufficient test prevents a conclusion. |
| Evidence | Scope |
|---|---|
AnalyticNecessaryAndSufficient |
A cited characterization on its declared domain; binary64 boundary tolerance is still reported. |
AnalyticSufficient |
Passing proves the property; failing is inconclusive. |
AnalyticNecessary |
Failing proves a violation; passing is not sufficient. |
NumericalSearch |
Analytic reductions plus recorded root brackets and compactified numerical optimization. |
NumericalScan |
A bounded, finite-resolution diagnostic only. |
For regular non-flat Raw SVI, assess_raw follows the Martini–Mingone
normalization, Fukasawa interval, two G2 roots, and compactified exterior
search for the critical sigma. The last step is a global dyadic numerical
search in IEEE-754 binary64. The cited paper does not prove the unimodality
needed to promote a local optimizer to a proof, and the implementation does
not use verified interval arithmetic. A clean result therefore carries
NumericalSearch; a negative evaluated g outside the reported uncertainty
is a conclusive witness.
The API treats other boundaries separately:
- a positive flat Raw slice has the analytic Black constant-variance result;
- a non-flat isolated zero is an analytic butterfly violation and a singular density input;
- the strict right-wing slope condition makes calls vanish at infinite strike;
- the separate strict left-wing condition controls unit continuous positive-strike mass; and
- SSVI Theorem 4.2 evidence is sufficient, not necessary.
butterfly_scan and calendar_scan require ordered finite bounds and
evaluate 401 points on the caller's range expanded by one log-moneyness unit on
each side. A violation flag requires a sample below the scale-aware negative
tolerance recorded in ScanEvidence; a minimum inside that boundary band is
unresolved. Their minimum and optional nearby Brent crossing are useful
diagnostics, but no result says anything about strikes outside that finite
grid. ScanEvidence preserves the caller-requested domain, pre-expansion
point count, expanded effective ScanConfig, successful sample count, whether
refinement was attempted, and optional final root bracket/residual/evaluation
evidence. RootEvidence also records the returned root and whether termination
was an exact binary64 zero or bracket-width tolerance. Aggregated surface
evidence adds represented-scan count, total sample count, and the selected scan
whose per-scan configuration and refinement record are retained. The selected
adjacent pair is ranked by semantic class—violation, then unresolved boundary,
then clean—and then by its normalized minimum within that class.
Calibration
The crate exposes three calibration paths:
| Path | Numerical method | Contract |
|---|---|---|
quasi_explicit |
Convex inner weighted least squares in (a,d,c) over the Zeliade polytope; non-convex multi-start Nelder–Mead in (m,sigma) |
Deterministic heuristic; not a global optimizer |
least_squares |
Levenberg–Marquardt with an analytic Raw Jacobian and positive-minimum transform | Local refinement from a seed |
calibration::surface |
Nelder–Mead over hard-feasible Heston-like or modified-power SSVI coordinates | Final sufficient butterfly and necessary-and-sufficient calendar assessments |
The default calibrate_slice runs quasi-explicit calibration, attempts an LM
polish, and keeps the lower-RMSE successful result. Five positive-weight,
sufficiently distinct strikes are required for a Raw fit. SSVI surface fitting
requires strictly ordered maturities and non-decreasing positive ATM total
variance. Heston-like fitting requires at least two positive-weight
observations at distinct strikes; modified-power-law fitting requires at least
three and at least two numerically distinct ATM-variance levels.
ConstraintMode::Constrained requires optimizer convergence plus the
configured feasibility postconditions. ConstraintMode::BestEffort may
return the best finite local result and records its actual termination and
evidence. Reports include algorithm, attempted and selected starts, tolerance,
iteration/evaluation counts, termination, weighted objective/RMSE/max-residual
and quote diagnostics, parameterization/repair policy, recomputed
domain/feasibility margins, and arbitrage assessments. Raw quasi-explicit
reports also expose the selected normalized-design condition estimate.
Iteration exhaustion and stagnation are not convergence.
Surfaces and density
Surface::from_slices accepts already ordered (maturity, RawSvi) pairs and
rejects empty, non-positive, duplicate, or unordered maturities and decreasing
ATM total variance. That ATM check is not a proof of global ordering between
arbitrary Raw slices. Surface::calendar_assessment aggregates the bounded
NumericalScan evidence for Raw backing and returns analytic Theorem 4.1
evidence for SSVI backing.
Inside the maturity grid, slice-backed surfaces interpolate linearly in total
variance at fixed k. Outside the grid they hold implied variance w/t
constant. SSVI-backed surfaces apply the same policy to the validated ATM
total-variance term structure and then evaluate SSVI. These policies preserve
calendar order when the relevant adjacent curves are globally ordered, but do
not by themselves certify butterfly freedom.
Surface::total_variance and Surface::implied_vol return
Result<_, ParamError> and reject non-finite log-moneyness or non-positive /
non-finite maturity.
risk_neutral_density returns the continuous density in log-strike, not the
g factor. integral and density_report use composite Simpson quadrature on
caller-supplied finite bounds. They reject invalid panels and non-finite or
singular evaluations. DensityError distinguishes exact non-positive variance
from a tiny positive, scale-aware IllConditionedVariance.
DensityReport::integration_error is a nested-Simpson Richardson estimate
for quadrature on the declared finite window; it does not estimate truncated
tail mass. Its violation flag requires a sampled g value below its
scale-aware negative evaluation band; a tiny negative minimum density inside
that band is unresolved and need not set the flag.
Architecture
src/
├── market/ validated quotes, units, and conventions
├── smile/ Raw, Jump-Wings, and conversions
├── surface/ SSVI, term structures, and interpolation
├── calibration/ configs, reports, slice and surface fits
├── no_arb/ butterfly/calendar diagnostics and evidence
├── density.rs log-strike density and Simpson quadrature
├── error.rs typed domain/conversion/calibration failures
└── numerics/ crate-private Brent, Cholesky, LM, Nelder–Mead
Principal domain types are re-exported at the crate root; domain modules stay available for explicit imports. Private numerical routines cannot be used to bypass public model validation.
Testing and independent verification
The Rust suite covers formula anchors, Raw/JW conversion degeneracies, SSVI
slice identities and global envelopes, the Axel Vogt counterexample, strict
tail and isolated-zero cases, bounded calendar failures, density mass and
forward moment, published Zeliade curves, noisy and ill-conditioned fits,
deterministic SplitMix64 suites, and proptest invariants. No hand-maintained
test count is promised after release; use the test runner for the current
inventory.
The 2.0.0 release baseline is:
| Suite | Passing tests |
|---|---|
| Inline unit tests | 186 |
| Main integration suite | 32 |
| High-precision oracle integration | 1 |
| Published-source integration | 7 |
| Rustdoc examples | 81 |
tools/oracle independently generates decimal-string fixtures
at 100 decimal digits with mpmath==1.4.1. The generator covers Raw/JW/SSVI,
g, density, Heston small arguments, Martini–Mingone roots/search anchors, and
extreme finite inputs. Rust checks each fixture with recorded absolute,
relative, and ULP tolerances. Root records come from explicit sign-changing
brackets and deterministic bisection; neither the oracle nor higher precision
is presented as a global optimization certificate.
The published source archive includes the oracle so reviewers can reproduce the fixtures independently. It is development tooling only: Cargo never compiles or executes it as part of the library, tests, examples, or benchmarks.
Toolchain and dependencies
- Library MSRV: Rust 1.85; edition 2024; safe,
std-only Rust. - Normal dependencies: none.
- Build dependencies: none.
- Development dependencies only:
approx ^0.5,proptest ^1, andcriterion ^0.8withhtml_reports. - Criterion 0.8 requires Rust 1.86 or newer, so all development targets run on the current stable toolchain while the shipped library is checked at 1.85.
- The independent Python oracle pins
mpmath==1.4.1; Ruff0.15.22is exact-pinned development tooling.
CI separately checks the empty normal/build Cargo graph, the full development
graph with cargo-deny, actual dependency use and unlinked sources with
cargo-shear 1.13.3 --deny-warnings --check-test-targets, strict rustdoc,
current-stable all-target tests, the library MSRV, and a release WASM library
build. See CONTRIBUTING.md for the local gate and
BENCHMARKS.md for measurement policy.
Documentation
- MATH.md — derivations, algorithms, evidence boundaries, and formula-to-test traceability.
- MIGRATION.md — 1.x to 2.x API and semantic migration.
- CHANGELOG.md — release history.
- SECURITY.md — security and numerical-correctness reporting.
- CONTRIBUTING.md — development and review requirements.
- BENCHMARKS.md — Criterion scope and reproduction.
- CODE_OF_CONDUCT.md — contributor conduct.
References
The primary mathematical sources are Gatheral–Jacquier (2014), Martini– Mingone (2022), Martini–De Marco / Zeliade (2012 revision), Lee (2004), and Breeden–Litzenberger (1978). Full citations and a formula-to-module-to-test matrix are in MATH.md.
License
Apache License 2.0. See LICENSE and NOTICE.
Copyright 2026 Regit.io — Nicolas Koenig
Part of Regit OS — the operating system for investment products. From Luxembourg.