Skip to main content

Volatility Smile Modelling

Project description

qsmile

License: MIT Python versions PyPI - Version

Github Linux macOS Code style: ruff uv Hatch project

CI MARIMO CodeFactor

Volatility smile modelling for option trading


Overview

qsmile is a Python library for fitting parametric volatility smile models to option chain data. It provides bid/ask-aware data containers, Black76 pricing, forward/discount-factor calibration, and least-squares SVI calibration out of the box.

Key capabilities

  • Bid/ask option pricesOptionChain stores bid/ask call and put prices, and automatically calibrates the forward and discount factor from put-call parity using quasi-delta weighted least squares.
  • Coordinate transformsVolData is a unified container with .transform(x, y) to freely convert between any combination of X-coordinates (Strike, Moneyness, Log-Moneyness, Standardised) and Y-coordinates (Price, Volatility, Variance, Total Variance) via composable, invertible maps.
  • SVI fitting — Fit the SVI raw parameterisation to VolData:

$$w(k) = a + b\left(\rho(k - m) + \sqrt{(k - m)^2 + \sigma^2}\right)$$

where $k = \ln(K/F)$ is log-moneyness and $w$ is total implied variance.

  • Black76 pricing — Vectorised call/put pricing and explicit closed-form implied vol inversion via black76_call, black76_put, and black76_implied_vol. The inverter uses the inverse-Gaussian quantile representation of Schadner (2026), which recovers implied vol to machine precision in a single non-iterative evaluation.
  • Plotting — All chain types have a .plot() method for bid/ask error-bar charts (requires qsmile[plot]).

Installation

pip install qsmile            # core
pip install "qsmile[plot]"    # with matplotlib plotting

For development:

git clone https://github.com/markrichardson/qsmile.git
cd qsmile
make install

Quick Start

From bid/ask prices (full pipeline)

import numpy as np
import pandas as pd
from qsmile import OptionChain, SmileMetadata, StrikeArray, SVIModel, VolData, XCoord, YCoord, fit

# Bid/ask prices — forward and DF are calibrated automatically
strikes = np.array([80, 90, 95, 100, 105, 110, 120], dtype=float)
idx = pd.Index(strikes, dtype=np.float64)
sa = StrikeArray()
sa.set(("call", "bid"), pd.Series([20.5, 11.8, 7.5, 4.2, 2.0, 0.8, 0.1], index=idx))
sa.set(("call", "ask"), pd.Series([21.5, 12.4, 8.0, 4.6, 2.3, 1.0, 0.2], index=idx))
sa.set(("put", "bid"), pd.Series([0.1, 0.6, 1.5, 3.1, 5.8, 9.6, 18.8], index=idx))
sa.set(("put", "ask"), pd.Series([0.2, 0.8, 1.8, 3.5, 6.2, 10.2, 19.6], index=idx))

prices = OptionChain(
    strikedata=sa,
    metadata=SmileMetadata(date=pd.Timestamp("2024-01-01"), expiry=pd.Timestamp("2024-07-01")),
)
print(prices.metadata.forward)          # Calibrated forward
print(prices.metadata.discount_factor)  # Calibrated discount factor

# Enter the coordinate transform framework
sd = prices.to_vols()                                      # (FixedStrike, Volatility)
sd_unit = sd.transform(XCoord.StandardisedStrike, YCoord.TotalVariance)  # → unitised

# Fit SVI directly from VolData
result = fit(sd, model=SVIModel)
print(result.model)    # Fitted SVIModel
print(result.rmse)     # Root mean square error

From mid implied vols

import numpy as np
import pandas as pd
from qsmile import SmileMetadata, SVIModel, VolData, fit

meta = SmileMetadata(
    date=pd.Timestamp("2024-01-01"),
    expiry=pd.Timestamp("2024-07-01"),
    forward=100.0,
)

sd = VolData.from_mid_vols(
    strikes=np.array([80, 90, 100, 110, 120], dtype=float),
    ivs=np.array([0.28, 0.22, 0.18, 0.17, 0.19]),
    metadata=meta,
)

result = fit(sd, model=SVIModel)
print(result.model)    # Fitted SVIModel
print(result.rmse)     # Root mean square error

API Reference

Data containers

Class Description
OptionChain Bid/ask call and put prices with automatic forward/DF calibration
VolData Unified coordinate-labelled container with .transform(x, y) and .from_mid_vols() factory

Coordinate transforms

OptionChain ─── .to_vols() ──→ VolData ─── .transform(x, y) ──→ VolData
VolData.from_mid_vols(...)         ──→ VolData ───────────────────────────┘
Coordinate type Values
X-coordinates FixedStrike, MoneynessStrike, LogMoneynessStrike, StandardisedStrike
Y-coordinates Price, Volatility, Variance, TotalVariance

Smile fitting

Function / Class Description
fit(chain, model) Fit any SmileModel to VolData — generic entry point
SmileModel Abstract base dataclass for pluggable smile models (native coords, bounds, evaluate, etc.)
SmileResult Fitted result with .model, .residuals, .rmse, .success
SVIModel SVI model and parameter values (a, b, rho, m, sigma) with .evaluate(k) and .implied_vol(k, T)
SABRModel SABR model (alpha, beta, rho, nu) with Hagan (2002) lognormal implied vol .evaluate(k)

Black76 pricing

Function Description
black76_call(F, K, D, σ, T) Vectorised Black76 call price
black76_put(F, K, D, σ, T) Vectorised Black76 put price
black76_implied_vol(price, F, K, D, T) Closed-form implied vol inversion via the inverse-Gaussian quantile (Schadner, 2026)

Development

make install   # Set up environment
make test      # Run tests with coverage
make fmt       # Format and lint
make marimo    # Launch interactive notebooks

License

MIT — see LICENSE for details.

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

qsmile-0.0.2.tar.gz (445.1 kB view details)

Uploaded Source

Built Distribution

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

qsmile-0.0.2-py3-none-any.whl (32.4 kB view details)

Uploaded Python 3

File details

Details for the file qsmile-0.0.2.tar.gz.

File metadata

  • Download URL: qsmile-0.0.2.tar.gz
  • Upload date:
  • Size: 445.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qsmile-0.0.2.tar.gz
Algorithm Hash digest
SHA256 3c981382438e04455cc6c14ff6888234215a65bcb6f9cb1c1bf0498aafd93782
MD5 23ab82329c0be0053383efc480deeaf4
BLAKE2b-256 291c538d8c1f5c3ec0c051a1794591081c1fb18caa89ae6b1efaf6fdd191eff9

See more details on using hashes here.

Provenance

The following attestation bundles were made for qsmile-0.0.2.tar.gz:

Publisher: rhiza_release.yml on markrichardson/qsmile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qsmile-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: qsmile-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 32.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qsmile-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 18bff086de4f5e75494afde806709026a8b2941e8f453c5ca8af19f27ce2ca25
MD5 d436505c3ba6550556ad39e0ef98e9f9
BLAKE2b-256 7be93f03c6dc97c3fb86c3b1a6bf95078f92a1cbc180373444b97fe95a012f08

See more details on using hashes here.

Provenance

The following attestation bundles were made for qsmile-0.0.2-py3-none-any.whl:

Publisher: rhiza_release.yml on markrichardson/qsmile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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