Skip to main content

volsurface

CI Python 3.10+ License: MIT Typed

A typed, extensible Python toolkit for implied volatility surface calibration. Fits Raw SVI per expiry or SSVI across the full surface, checks for arbitrage, estimates forwards from put-call parity, and renders publication-quality plots — all with mypy --strict compliance throughout.

Features

  • Raw SVI parameterisation — per-expiry L-BFGS-B calibration with data-scaled multi-start initial guesses
  • SSVI surface model — globally consistent surface with shared (ρ, η, γ) parameters; free of calendar arbitrage by construction
  • Put-call parity forward estimation — estimates the implied forward at each expiry from mid-prices, with robust median aggregation and sanity bounds
  • Arbitrage checks — butterfly (convexity), calendar spread, and SVI parameter-level no-arbitrage conditions
  • Fit diagnostics — per-slice and aggregate RMSE, max/mean absolute error, and side-by-side surface comparison tables
  • Yahoo Finance integration — fetch, filter by liquidity, and clean live option chains in one call
  • Surface interpolation — linear interpolation in total-variance space between fitted expiries
  • Visualisation — 3D surface plots, heatmaps, and per-expiry smile overlays
  • Fully typed — mypy --strict compliant with py.typed marker
  • Extensible — implement VolModel to add any new parameterisation

Installation

pip install volsurface            # core only
pip install volsurface[yahoo]     # + Yahoo Finance data
pip install volsurface[plot]      # + matplotlib plotting
pip install volsurface[all]       # everything

Quick Start

Fetch, fit, query

from volsurface.market_data import fetch_chain
from volsurface.calibration import calibrate_surface
from volsurface.models import RawSVI
from volsurface.plotting import plot_surface

# Fetch SPY option chains and estimate forwards from put-call parity
slices = fetch_chain("SPY", min_volume=30, moneyness_range=(0.8, 1.2))

# Fit Raw SVI independently to each expiry
result = calibrate_surface(slices, RawSVI, ticker="SPY")
print(f"Fitted {result.surface.n_expiries} expiries")
print(f"Arbitrage clean: {result.arbitrage_report.is_clean}")

# Query any (strike, expiry) point — interpolates between fitted expiries
point = result.surface.iv(strike=570.0, expiry_years=0.5)
print(f"IV at K=570, T=0.5y: {point.iv:.4f}")

plot_surface(result.surface, kind="3d")

Fit SSVI and compare

from volsurface.models import SSVI
from volsurface.diagnostics import compare_surfaces
from volsurface.core import VolSurface

# Fit SSVI: global (rho, eta, gamma) calibrated across all expiries at once
ssvi = SSVI()
ssvi_slices, fit_result = ssvi.fit_surface(slices)
print(f"rho={ssvi.global_params.rho:.4f}  eta={ssvi.global_params.eta:.4f}")

# Assemble an SSVI VolSurface for querying and plotting
ssvi_surface = VolSurface(ticker="SPY (SSVI)")
for ms in slices:
    ssvi_surface.slices[ms.expiry_years] = ssvi_slices[ms.expiry_years]
    ssvi_surface.market_data[ms.expiry_years] = ms

comparison = compare_surfaces(result.surface, ssvi_surface, "Raw SVI", "SSVI")
print(comparison.summary_table())

Working with Custom Data

import numpy as np
from volsurface.market_data import clean_chain
from volsurface.models import RawSVI

strikes = np.array([90, 95, 100, 105, 110], dtype=float)
ivs = np.array([0.25, 0.22, 0.20, 0.21, 0.24])

slice_ = clean_chain(strikes, ivs, expiry_years=0.25, forward=100.0, spot=100.0)

model = RawSVI()
result = model.fit(slice_)
print(f"RMSE: {result.rmse:.6f}")
print(f"Params: {result.params}")

Adding a New Model

Implement the VolModel abstract base class — three methods required:

from volsurface.models.base import VolModel
from volsurface.core import FitResult, MarketSlice
import numpy as np

class MyModel(VolModel):
    @property
    def n_params(self) -> int:
        return 4

    def fit(self, market_slice: MarketSlice) -> FitResult:
        # calibrate to market_slice.log_moneyness, market_slice.total_variance
        ...

    def total_variance(self, log_moneyness):
        # return model total variance for given log-moneyness values
        ...

The fitted model plugs directly into calibrate_surface, plot_smile, and diagnose_surface.

Documentation

Full documentation including the Theory & Models reference: https://cjpvanderwouden.github.io/volsurface/

Development

git clone https://github.com/cjpvanderwouden/volsurface.git
cd volsurface
pip install -e ".[dev]"

pytest                  # tests + coverage
ruff check src/ tests/  # linting
mypy --strict src/      # type checking

License

MIT

Metadata

Release files for volsurface 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for volsurface 0.2.0
File Size Uploaded
volsurface-0.2.0.tar.gz 45.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for volsurface 0.2.0
File Interpreter ABI Platform
volsurface-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 79.4 kB

Release files / volsurface-0.2.0.tar.gz

Download URL volsurface-0.2.0.tar.gz
Size 45.3 kB
Tags Source
SHA-256 checksum
How to use checksums
4cf374597dd727b76ecf3737a047f396cdb4986b937a685ae0220eb542d199a9
BLAKE2b-256 checksum
How to use checksums
42ed46319d6fbab518b5f705054c3952b4dbcd781a0818b6f080609687dcce61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / volsurface-0.2.0-py3-none-any.whl

Download URL volsurface-0.2.0-py3-none-any.whl
Size 34.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
018846dfc24b36ec76c5180e828f12dbe39232322f7cc16797e31e9ea47ae099
BLAKE2b-256 checksum
How to use checksums
8a8a155f19c21fd6a55d07da08cea9c7f16c658b2ac981af730b0f6d75453f85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page