SSVI Volatility Surface¶
quantflow.options.ssvi.SSVI
pydantic-model
¶
Bases: BaseModel
eSSVI (extended Surface SVI) parametrisation of the implied volatility surface.
The SSVI surface of Gatheral and Jacquier (2014) is extended with a maturity dependent correlation, following Hendriks and Martini (2019). Each maturity slice is described by three parameters stored at the node maturities:
- the total ATM variance \(\theta_\tau\)
- the curvature \(\psi_\tau\)
- the correlation \(\rho_\tau\)
The total implied variance \(w(k) = \sigma^2(k) \cdot \tau\) at log-strike \(k = \log(K/F)\) is
where the shape function is the ratio of curvature and total ATM variance:
The parameters have direct smile interpretations. The total ATM variance sets the level of the slice, \(w(0, \tau) = \theta_\tau\). The curvature and correlation set the slope and convexity of the total variance at the money:
The sign of the correlation therefore tilts the smile (negative for the left skew typical of equities) while the curvature scales both the tilt and the bend around the money.
In the wings the total variance grows linearly, \(w \to \frac{\psi_\tau (1 \pm \rho_\tau)}{2} |k|\) as \(k \to \pm\infty\), so the curvature also sets the wing slopes. Lee's moment formula caps these slopes at 2, which is exactly the first butterfly condition \(\psi_\tau (1 + |\rho_\tau|) < 4\) of no_butterfly_arbitrage.
The shape function acts as a moneyness rescaling: the log-strike enters the formula only through the product \(\varphi_\tau k\), so \(\varphi_\tau\) measures how far a strike is from the money relative to the width of the smile at that maturity. Short maturities combine a small \(\theta\) with a large \(\varphi\) (the whole smile lives within a few percent of the forward), long maturities the opposite.
Absence of calendar spread arbitrage requires both \(\theta\) and \(\psi\) to be non decreasing in maturity, and the nodes are validated accordingly. Between nodes, the quantities \(\theta\), \(\psi\) and \(\rho \psi\) are interpolated linearly, the natural interpolation of Corbetta et al. (2019), which preserves the absence of static arbitrage of the interpolated slices. Outside the node range the surface extrapolates flat.
Use fit_surface to calibrate the slice parameters with the star calibration algorithm of the same paper, which enforces the absence of butterfly and calendar spread arbitrage by construction.
Fields:
-
ttm(list[DecimalNumber]) -
theta(list[DecimalNumber]) -
psi(list[DecimalNumber]) -
rho(list[DecimalNumber])
theta
pydantic-field
¶
Total ATM variances at each maturity, same length as ttm, positive and non decreasing
psi
pydantic-field
¶
Curvatures \(\psi_i = \theta_i \varphi_i\) at each maturity, same length as ttm, positive and non decreasing
rho
pydantic-field
¶
Correlations at each maturity, same length as ttm, each strictly inside the interval (-1, 1)
atm_variance
¶
Interpolated total ATM variance \(\theta(\tau)\).
| PARAMETER | DESCRIPTION |
|---|---|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
curvature
¶
Interpolated curvature \(\psi(\tau)\).
| PARAMETER | DESCRIPTION |
|---|---|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
correlation
¶
Interpolated correlation \(\rho(\tau)\).
The interpolation is linear in \(\rho \psi\) and \(\psi\), so the correlation is their ratio rather than a direct linear interpolation.
| PARAMETER | DESCRIPTION |
|---|---|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
Source code in quantflow/options/ssvi.py
phi
¶
Shape function \(\varphi_\tau = \psi_\tau / \theta_\tau\).
| PARAMETER | DESCRIPTION |
|---|---|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
Source code in quantflow/options/ssvi.py
total_variance
¶
Total implied variance \(w(k, \tau)\).
Returns an array broadcast from the shapes of \(k\) and \(\tau\).
| PARAMETER | DESCRIPTION |
|---|---|
k
|
Log-moneyness log(K/F), scalar or array
TYPE:
|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
Source code in quantflow/options/ssvi.py
iv
¶
Implied volatility \(\sigma(k, \tau) = \sqrt{w(k, \tau) / \tau}\).
Returns an array of the same shape as \(k\). The eSSVI total variance is strictly positive for \(|\rho| < 1\), so no clipping is required.
| PARAMETER | DESCRIPTION |
|---|---|
k
|
Log-moneyness log(K/F), scalar or array
TYPE:
|
ttm
|
Time to maturity in years, scalar or array
TYPE:
|
Source code in quantflow/options/ssvi.py
no_butterfly_arbitrage
¶
True if the slice satisfies the sufficient conditions for absence of butterfly arbitrage.
The conditions, from Theorem 4.2 of Gatheral and Jacquier (2014), expressed in terms of the curvature \(\psi = \theta \varphi\), are:
| PARAMETER | DESCRIPTION |
|---|---|
ttm
|
Optional maturity to check. All nodes are checked when omitted
TYPE:
|
Source code in quantflow/options/ssvi.py
no_calendar_arbitrage
¶
True if the surface nodes satisfy the conditions for absence of calendar spread arbitrage.
The conditions, necessary and sufficient from Hendriks and Martini (2019), require \(\theta\) and \(\psi\) non decreasing (enforced by the node validation) together with, for consecutive maturities \(\tau_1 < \tau_2\):
Because \(\theta\), \(\psi\) and \(\rho \psi\) interpolate linearly, node conditions extend to the whole interpolated surface (Section 5 of Corbetta et al. (2019)).
Source code in quantflow/options/ssvi.py
no_static_arbitrage
¶
fit_surface
classmethod
¶
Calibrate the eSSVI surface slice by slice with the star calibration algorithm of Corbetta et al. (2019).
Slices are sorted by maturity and calibrated going forward. The star calibration provides the starting point of each slice: the slice is anchored to the quote closest to the ATM forward, which eliminates the total ATM variance analytically (\(\theta = \theta^* - \rho \psi k^*\)), and the remaining pair \((\rho, \psi)\) is found by sampling the correlation on a progressively refined grid and, for each sample, minimising over the curvature with a bounded one dimensional search. The curvature bounds enforce the butterfly conditions and the calendar conditions with respect to the previously calibrated slice.
The three parameters are then refined jointly with a direct search that releases the ATM anchor, letting all quotes set the level of the slice, while rejecting any candidate that violates the butterfly or calendar conditions, so the fitted surface remains free of static arbitrage by construction.
The objective is the mean absolute implied variance error with an exponential weight per quote:
where \(\lambda\) is the weight_decay parameter and \(d\) is the convexity adjusted moneyness evaluated at the ATM total variance \(\theta^*\).
The weight is centered at \(d = 0\), the median of the risk-neutral distribution, and concentrates the fit around the money where quotes are most reliable, while its slow exponential decay keeps far from the money quotes contributing to the fit.
The error is measured in implied variance rather than implied volatility, which gives the smile wings, where the variance is largest, a stronger pull on the fit.
| PARAMETER | DESCRIPTION |
|---|---|
slices
|
One (log-moneyness, implied volatilities, time to maturity) tuple per maturity slice
TYPE:
|
weight_decay
|
Exponential decay rate of the quote weights with distance from the money, measured by the convexity adjusted moneyness. 0, the default, weights all quotes equally, larger values concentrate the fit at the money. Must be non negative
TYPE:
|
Source code in quantflow/options/ssvi.py
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | |
fit_vol_surface
classmethod
¶
Fit an eSSVI model to a volatility surface.
| PARAMETER | DESCRIPTION |
|---|---|
surface
|
Volatility surface with calculated implied volatilities and converged options
TYPE:
|
weight_decay
|
Exponential decay rate of the quote weights with distance from the money, see fit_surface
TYPE:
|