Stochastic volatility inspired parametrizations of the implied volatility surface in Python!
Project description
svi-py
Stochastic volatility inspired (SVI) parametrizations of the implied volatility surface in Python.
Given a panel of contemporaneous European call and put option prices across strikes and maturities, svi-py calibrates smooth, arbitrage-aware total variance surfaces using the SVI family of models. It handles the full pipeline: implied vol extraction, forward estimation via put-call parity, OTM leg selection, and per-slice calibration with configurable no-arbitrage constraints.
Installation
pip install svi-py
Requires Python >= 3.13.
Quick start
You need a DataFrame with columns for strike prices, implied volatilities (or raw option prices from which to compute them), time to maturity, and an implied forward price. A typical workflow:
import numpy as np
import pandas as pd
from pysvi import SVI, get_model, calibrate_slice, apply_slice, ArbitrageFreedom
# Suppose df_slice is a single-maturity cross-section with columns:
# strike, iv, maturity, implied_forward
model = get_model("svi")
params = calibrate_slice(df_slice, model)
# Generate fitted IVs and residuals
fitted = apply_slice(df_slice, params, model)
print(fitted[["strike", "iv", "fitted_iv", "residual_iv"]])
For SSVI/eSSVI, pass the ATM total variance as an extra argument:
model = get_model("ssvi")
theta = float(np.nanmin(df_slice["iv"] ** 2 * df_slice["maturity"]))
params = calibrate_slice(df_slice, model, theta=theta)
For jump-wings, pass the time to expiry:
model = get_model("jw")
T = float(df_slice["maturity"].iloc[0])
params = calibrate_slice(df_slice, model, T=T)
Where do the inputs come from?
svi-py expects you to already have implied volatilities and forward prices. If you're starting from raw option prices, the library provides helpers:
compute_ivs_vectorizedcomputes Black-Scholes-Merton implied vols from option mid-prices viapy_vollib.calculate_implied_forwardestimates the forward price from put-call parity: $F = K + e^{rT}(C - P)$.choose_legselects the OTM leg (calls for $K \geq F$, puts for $K < F$) for cleaner vol quotes.
You need a panel of contemporaneous call and put option prices across multiple strikes for at least one maturity. The richer the strike grid, the better the calibration.
Parametrizations
All parametrizations work in total variance space: $w(k) = \sigma^2(k) \cdot T$, where $k = \log(K/F)$ is log-moneyness.
Raw SVI
The original Gatheral (2004) parametrization with 5 free parameters:
$$w(k) = a + b\left[\rho(k - m) + \sqrt{(k - m)^2 + \sigma^2}\right]$$
| Parameter | Meaning | Constraint |
|---|---|---|
| $a$ | overall variance level | $a \geq 0$ |
| $b$ | slope / curvature scale | $b > 0$ |
| $\rho$ | skew (correlation) | $|\rho| < 1$ |
| $m$ | log-moneyness shift | unconstrained |
| $\sigma$ | vol-of-vol (smile width) | $\sigma > 0$ |
Maximum flexibility (5 degrees of freedom per slice). No automatic arbitrage guarantees beyond soft parameter bounds.
SSVI (Surface SVI)
Gatheral & Jacquier (2014). Reduces to 2 free parameters per slice by fixing the ATM total variance $\theta = \sigma_{\text{ATM}}^2 T$:
$$w(k;\theta) = \frac{\theta}{2}\left[1 + \rho,\varphi(\theta),k + \sqrt{\left(\varphi(\theta),k + \rho\right)^2 + 1 - \rho^2}\right]$$
where $\varphi(\theta) = \eta / \sqrt{\theta}$ is the curvature function.
| Parameter | Meaning | Constraint |
|---|---|---|
| $\theta$ | ATM total variance (fixed input) | $\theta > 0$ |
| $\rho$ | skew | $|\rho| < 1$ |
| $\eta$ | curvature scale | $\eta > 0$ |
Guarantees no butterfly arbitrage by construction for each fixed $\theta$.
eSSVI (Extended SSVI)
Extends SSVI with maturity-dependent skew via a $\rho(\theta)$ term structure:
$$\rho(\theta) = \text{clip}!\left(\rho_0 + \rho_1 \left(\frac{\theta}{\theta_{\text{ref}}}\right)^\alpha,; -1,; 1\right)$$
The total variance formula is the same as SSVI but with $\rho \to \rho(\theta)$. This adds 4 parameters globally ($\rho_0, \rho_1, \alpha, \eta$) and enables realistic calendar skew evolution across maturities. $\theta_{\text{ref}}$ is a reference ATM total variance (typically the median across slices) that normalises the power law.
Jump-Wings
The jump-wings parametrization (Gatheral 2004) re-expresses SVI in terms of financially interpretable quantities:
| Parameter | Meaning |
|---|---|
| $v_t$ | ATM variance $\sigma_{\text{ATM}}^2$ |
| $\psi_t$ | ATM skew |
| $p_t$ | left (put) wing slope, $p_t \geq 0$ |
| $c_t$ | right (call) wing slope, $c_t \geq 0$ |
| $\tilde{v}_t$ | minimum implied variance, $\tilde{v}_t > 0$ |
These map to raw SVI $(a, b, \rho, m, \sigma)$ via a bijection:
$$b = \frac{p_t + c_t}{2}, \quad \rho = 1 - \frac{p_t}{b}, \quad \beta = \rho - \frac{2\psi_t\sqrt{T}}{b}$$
$$\alpha = \text{sgn}(\beta)\sqrt{\frac{1}{\beta^2} - 1}, \quad m = \frac{(v_t - \tilde{v}_t),T}{b\left[-\rho + \text{sgn}(\alpha)\sqrt{1 + \alpha^2} - \alpha\sqrt{1 - \rho^2}\right]}$$
$$\sigma = |\alpha \cdot m|, \quad a = \tilde{v}_t \cdot T - b,\sigma\sqrt{1 - \rho^2}$$
Same 5 degrees of freedom as raw SVI but with parameters that have direct market interpretation (wing slopes, ATM level, minimum variance).
Arbitrage freeness
Every parametrization accepts an arbitrage_condition argument controlling how strictly no-arbitrage is enforced during calibration. The options are flags that can be combined with |:
from pysvi import ArbitrageFreedom
# Default: soft parameter bounds only
model = get_model("svi") # ArbitrageFreedom.QUASI
# Enforce no butterfly arbitrage (non-negative density)
model = get_model("svi", ArbitrageFreedom.NO_BUTTERFLY)
# Enforce no calendar spread arbitrage (non-decreasing total variance in T)
model = get_model("ssvi", ArbitrageFreedom.NO_CALENDAR)
# Enforce both
model = get_model("svi", ArbitrageFreedom.NO_BUTTERFLY | ArbitrageFreedom.NO_CALENDAR)
QUASI (default)
Soft parameter-bound constraints only: $b > 0$, $|\rho| < 1$, $\sigma > 0$. Enforced via bounded optimisation and penalty terms. Fast, and usually sufficient for liquid underlyings.
NO_BUTTERFLY
Enforces non-negative call price density $g(k) \geq 0$ across strikes, where:
$$g(k) = \left(1 - \frac{k,w'(k)}{2,w(k)}\right)^2 - \frac{w'(k)^2}{4}\left(\frac{1}{w(k)} + \frac{1}{4}\right) + \frac{w''(k)}{2}$$
Butterfly arbitrage exists whenever $g(k) < 0$ for some $k$. The calibrator evaluates $g$ on a fine grid and penalises violations. Note that SSVI and eSSVI already guarantee $g(k) \geq 0$ by their functional form; this flag adds an explicit numerical check.
NO_CALENDAR
Enforces non-decreasing total variance in maturity: $w(k, T_2) \geq w(k, T_1)$ for $T_2 > T_1$ at every $k$. This is a cross-slice condition. Pass the prior (shorter-maturity) slice's total variance via the w_prev keyword argument to calibrate:
# After calibrating the first slice:
w_prev = model.total_variance(k_grid, params_first_slice)
# Calibrate the next slice with calendar constraint:
params_next = model.calibrate(k, w_target, w_prev=w_prev)
Calibration details
All models calibrate via L-BFGS-B (bounded quasi-Newton) with automatic Nelder-Mead fallback. The pipeline for a single maturity slice is:
prepare_slice: extracts $T$, $F$, computes $k = \log(K/F)$ and $w = \sigma_{\text{mkt}}^2 T$, filters invalid data, clips extreme moneyness.model.calibrate: minimises MSE$(w_{\text{model}}, w_{\text{target}})$ plus penalty terms.apply_slice: evaluates the fitted surface, computes $\sigma_{\text{fit}} = \sqrt{w/T}$ and residuals.
The factory function get_model(name) accepts "svi", "ssvi", "essvi", "jumpwings" (or "jw").
Contributing
Contributions, bug reports, and feature requests are welcome. Open an issue or submit a PR on GitHub.
Wanted: the original Gamma-Vanna-Volga paper. The Gamma-Vanna-Volga parametrization is something of a holy grail in the quant vol surface literature and would be a great addition to this library. If you have a copy of the original paper, please send it to marwin.steiner@gmail.com.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file svi_py-0.2.1.tar.gz.
File metadata
- Download URL: svi_py-0.2.1.tar.gz
- Upload date:
- Size: 66.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a14d7a7c44a214d1fb27c6c88e4f1d53308a7a203b87a02bf242dfc96db4aa86
|
|
| MD5 |
0079510811974f3458d3b900efc3c1dc
|
|
| BLAKE2b-256 |
8012e97ede6cc59848f7e07e3a340a918c48f035a3579d01105d353334fb7e78
|
Provenance
The following attestation bundles were made for svi_py-0.2.1.tar.gz:
Publisher:
python-publish.yml on marwinsteiner/pysvi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
svi_py-0.2.1.tar.gz -
Subject digest:
a14d7a7c44a214d1fb27c6c88e4f1d53308a7a203b87a02bf242dfc96db4aa86 - Sigstore transparency entry: 976664795
- Sigstore integration time:
-
Permalink:
marwinsteiner/pysvi@ca226f648012d7703016379d8192e48926bb4b8e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/marwinsteiner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ca226f648012d7703016379d8192e48926bb4b8e -
Trigger Event:
push
-
Statement type:
File details
Details for the file svi_py-0.2.1-py3-none-any.whl.
File metadata
- Download URL: svi_py-0.2.1-py3-none-any.whl
- Upload date:
- Size: 16.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
318d27f8f208166ad14c4cf07505a49b4f7402c9e241e60ef6273e181f2b2ce4
|
|
| MD5 |
c1a9daa14ac5010958a3ec6a7e65a00c
|
|
| BLAKE2b-256 |
b7ed5463c89a2f83efff5178b25c434eb4fab35c3b23b0444569fb123ba0613e
|
Provenance
The following attestation bundles were made for svi_py-0.2.1-py3-none-any.whl:
Publisher:
python-publish.yml on marwinsteiner/pysvi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
svi_py-0.2.1-py3-none-any.whl -
Subject digest:
318d27f8f208166ad14c4cf07505a49b4f7402c9e241e60ef6273e181f2b2ce4 - Sigstore transparency entry: 976664799
- Sigstore integration time:
-
Permalink:
marwinsteiner/pysvi@ca226f648012d7703016379d8192e48926bb4b8e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/marwinsteiner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ca226f648012d7703016379d8192e48926bb4b8e -
Trigger Event:
push
-
Statement type: