Skip to content

Latest commit

 

History

History
178 lines (128 loc) · 6.71 KB

File metadata and controls

178 lines (128 loc) · 6.71 KB

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 implied vol inversion via black76_call, black76_put, and black76_implied_vol.
  • 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) 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.