Volatility Smile Modelling
Project description
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 prices —
OptionChainstores 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 transforms —
SmileDatais 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
SmileData:
$$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 implied vol inversion via
black76_call,black76_put, andblack76_implied_vol. - Plotting — All chain types have a
.plot()method for bid/ask error-bar charts (requiresqsmile[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, SmileData, SmileMetadata, StrikeArray, SVIModel, 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_smile_data() # (FixedStrike, Volatility)
sd_unit = sd.transform(XCoord.StandardisedStrike, YCoord.TotalVariance) # → unitised
# Fit SVI directly from SmileData
result = fit(sd, model=SVIModel)
print(result.params) # Fitted SVIModel
print(result.rmse) # Root mean square error
From mid implied vols
import numpy as np
import pandas as pd
from qsmile import SmileData, SmileMetadata, SVIModel, fit
meta = SmileMetadata(
date=pd.Timestamp("2024-01-01"),
expiry=pd.Timestamp("2024-07-01"),
forward=100.0,
)
sd = SmileData.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.params) # 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 |
SmileData |
Unified coordinate-labelled container with .transform(x, y) and .from_mid_vols() factory |
Coordinate transforms
OptionChain ─── .to_smile_data() ──→ SmileData ─── .transform(x, y) ──→ SmileData
SmileData.from_mid_vols(...) ──→ SmileData ───────────────────────────┘
| 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 SmileData — generic entry point |
SmileModel |
Protocol for pluggable smile models (native coords, bounds, evaluate, etc.) |
AbstractSmileModel |
Abstract base dataclass with default to_array()/from_array() derived from param_names |
SmileResult |
Fitted result with .params, .residuals, .rmse, .success, .evaluate(x) |
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) |
Implied vol inversion via Brent's method |
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
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 qsmile-0.0.1rc4.tar.gz.
File metadata
- Download URL: qsmile-0.0.1rc4.tar.gz
- Upload date:
- Size: 536.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1f74c1202c394e5238a056fdaed3cb3089cf1702e48f7d2dc54544914832e7b
|
|
| MD5 |
beb2d3807dd7eee6bfbb5d58cd5cae29
|
|
| BLAKE2b-256 |
e1eaab5caf28675d554d57461b38bdd4d817d52101ea90089696f645c397d550
|
Provenance
The following attestation bundles were made for qsmile-0.0.1rc4.tar.gz:
Publisher:
rhiza_release.yml on markrichardson/qsmile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qsmile-0.0.1rc4.tar.gz -
Subject digest:
b1f74c1202c394e5238a056fdaed3cb3089cf1702e48f7d2dc54544914832e7b - Sigstore transparency entry: 1281851768
- Sigstore integration time:
-
Permalink:
markrichardson/qsmile@aee932a5d47967b37a13db75aa8eab88dd3f7812 -
Branch / Tag:
refs/tags/v0.0.1-rc.4 - Owner: https://github.com/markrichardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@aee932a5d47967b37a13db75aa8eab88dd3f7812 -
Trigger Event:
push
-
Statement type:
File details
Details for the file qsmile-0.0.1rc4-py3-none-any.whl.
File metadata
- Download URL: qsmile-0.0.1rc4-py3-none-any.whl
- Upload date:
- Size: 31.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d11305f03a6adf920cddc2212f4d677be6797405b044f605017a04f05fe866ee
|
|
| MD5 |
0a651532164b79ceed95c52ae2247bc2
|
|
| BLAKE2b-256 |
17f8c05672031e10ce47ea7014af8930fddc18cb6197b0d2912572fdd26861bb
|
Provenance
The following attestation bundles were made for qsmile-0.0.1rc4-py3-none-any.whl:
Publisher:
rhiza_release.yml on markrichardson/qsmile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qsmile-0.0.1rc4-py3-none-any.whl -
Subject digest:
d11305f03a6adf920cddc2212f4d677be6797405b044f605017a04f05fe866ee - Sigstore transparency entry: 1281851800
- Sigstore integration time:
-
Permalink:
markrichardson/qsmile@aee932a5d47967b37a13db75aa8eab88dd3f7812 -
Branch / Tag:
refs/tags/v0.0.1-rc.4 - Owner: https://github.com/markrichardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@aee932a5d47967b37a13db75aa8eab88dd3f7812 -
Trigger Event:
push
-
Statement type: