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 —
OptionChainPricesstores 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
from qsmile import OptionChainPrices, SmileData, XCoord, YCoord, fit_svi
# Bid/ask prices — forward and DF are calibrated automatically
prices = OptionChainPrices(
strikes=np.array([80, 90, 95, 100, 105, 110, 120], dtype=float),
call_bid=np.array([20.5, 11.8, 7.5, 4.2, 2.0, 0.8, 0.1]),
call_ask=np.array([21.5, 12.4, 8.0, 4.6, 2.3, 1.0, 0.2]),
put_bid=np.array([0.1, 0.6, 1.5, 3.1, 5.8, 9.6, 18.8]),
put_ask=np.array([0.2, 0.8, 1.8, 3.5, 6.2, 10.2, 19.6]),
expiry=0.5,
)
print(prices.forward) # Calibrated forward
print(prices.discount_factor) # Calibrated discount factor
# Enter the coordinate transform framework
sd = prices.to_smile_data() # (FixedStrike, Price)
sd_vols = sd.transform(XCoord.FixedStrike, YCoord.Volatility) # → implied vols
sd_unit = sd_vols.transform(XCoord.StandardisedStrike, YCoord.TotalVariance) # → unitised
# Fit SVI directly from SmileData
result = fit_svi(sd_vols)
print(result.params) # Fitted SVIParams
print(result.rmse) # Root mean square error
From mid implied vols
import numpy as np
from qsmile import SmileData, fit_svi
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]),
forward=100.0,
expiry=0.5,
)
result = fit_svi(sd)
print(result.params) # Fitted SVIParams
print(result.rmse) # Root mean square error
API Reference
Data containers
| Class | Description |
|---|---|
OptionChainPrices |
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
OptionChainPrices ─── .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 |
SVI fitting
| Function / Class | Description |
|---|---|
fit_svi(chain) |
Fit SVI params from SmileData |
SmileResult |
Fitted result with .params, .rmse, .fitted_vols |
SVIParams |
SVI parameters (a, b, rho, m, sigma) |
svi_total_variance(k, params) |
Evaluate SVI total variance at log-moneyness k |
svi_implied_vol(k, params, expiry) |
Evaluate SVI implied vol at log-moneyness 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.1rc3.tar.gz.
File metadata
- Download URL: qsmile-0.0.1rc3.tar.gz
- Upload date:
- Size: 417.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d8a75f0e1065f497a9c3480f78209b9d807d9e9571f070c900c12dca100df361
|
|
| MD5 |
3da2443ab2d640c204f524a3345d6c9d
|
|
| BLAKE2b-256 |
e7efa92872c399d7607e8f05f9845b7f70dde71ce21fecc7646ad45396dfc605
|
Provenance
The following attestation bundles were made for qsmile-0.0.1rc3.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.1rc3.tar.gz -
Subject digest:
d8a75f0e1065f497a9c3480f78209b9d807d9e9571f070c900c12dca100df361 - Sigstore transparency entry: 1208695364
- Sigstore integration time:
-
Permalink:
markrichardson/qsmile@d1066bfde52bf83e997ea87be4056a820df257ce -
Branch / Tag:
refs/tags/v0.0.1-rc.3 - Owner: https://github.com/markrichardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@d1066bfde52bf83e997ea87be4056a820df257ce -
Trigger Event:
push
-
Statement type:
File details
Details for the file qsmile-0.0.1rc3-py3-none-any.whl.
File metadata
- Download URL: qsmile-0.0.1rc3-py3-none-any.whl
- Upload date:
- Size: 17.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cede35f58e24fcc4cbb022e5f02415ab94dca2a628b87c9568256cb84e95dd23
|
|
| MD5 |
f8bd2b6a50bd1902ccf2176800d0d49e
|
|
| BLAKE2b-256 |
d596e154ea67dc1271adf5f37644d0266c0cb2ae68a34c5831708317512a9e2f
|
Provenance
The following attestation bundles were made for qsmile-0.0.1rc3-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.1rc3-py3-none-any.whl -
Subject digest:
cede35f58e24fcc4cbb022e5f02415ab94dca2a628b87c9568256cb84e95dd23 - Sigstore transparency entry: 1208695509
- Sigstore integration time:
-
Permalink:
markrichardson/qsmile@d1066bfde52bf83e997ea87be4056a820df257ce -
Branch / Tag:
refs/tags/v0.0.1-rc.3 - Owner: https://github.com/markrichardson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@d1066bfde52bf83e997ea87be4056a820df257ce -
Trigger Event:
push
-
Statement type: