Skip to main content

Typed, extensible toolkit for implied volatility surface calibration

Project description

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 typedmypy --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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

volsurface-0.2.0.tar.gz (45.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

volsurface-0.2.0-py3-none-any.whl (34.1 kB view details)

Uploaded Python 3

File details

Details for the file volsurface-0.2.0.tar.gz.

File metadata

  • Download URL: volsurface-0.2.0.tar.gz
  • Upload date:
  • Size: 45.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for volsurface-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4cf374597dd727b76ecf3737a047f396cdb4986b937a685ae0220eb542d199a9
MD5 07ad8b981356e7243d6b661eb631b14c
BLAKE2b-256 42ed46319d6fbab518b5f705054c3952b4dbcd781a0818b6f080609687dcce61

See more details on using hashes here.

File details

Details for the file volsurface-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: volsurface-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 34.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for volsurface-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 018846dfc24b36ec76c5180e828f12dbe39232322f7cc16797e31e9ea47ae099
MD5 c76d3fa606f7682f714805fbd4aaf7ab
BLAKE2b-256 8a8a155f19c21fd6a55d07da08cea9c7f16c658b2ac981af730b0f6d75453f85

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page