Author: Artur Sepp / First recorded: 2022-08-03
Implemented in stochvolmodels. Software citation: CITATION.cff.
Fourier-transform pricing, Monte Carlo validation, and calibration of European options under stochastic-volatility models in Python
The stable workflows cover European vanilla and related variance analytics under Heston and the Karasinski-Sepp log-normal stochastic-volatility model; experimental research modules are labelled separately rather than presented as stable derivatives infrastructure.
Install: pip install stochvolmodels · Import: stochvolmodels · Status: Stable
Paper: Sepp, A. and Rakhmonov, P. (2023), Log-normal stochastic volatility model with quadratic drift, International Journal of Theoretical and Applied Finance, 26(8). See Statement of need for the six papers the repository reproduces and Citation for the full BibTeX list.
Documentation: stochvolmodels.readthedocs.io · offline quickstart · LogSV quickstart in Colab · JOSS paper draft
stochvolmodels is the reference implementation of the Karasinski-Sepp log-normal stochastic
volatility model, maintained by one of the model's originators, with the Heston model implemented
alongside as a benchmark. The design goal is a single generic interface for a stochastic
volatility model — a closed-form moment generating function for Fourier-transform pricing on one
side, Monte Carlo dynamics on the other — so that analytic prices, simulated prices, and
calibrated implied volatilities are directly comparable model to model.
Researchers and quantitative practitioners need more than a standalone pricing formula when they evaluate a stochastic-volatility specification: market quotes must share explicit forward, discount, option-type, and maturity conventions; calibration must fail visibly when constraints are not satisfied; and analytic prices need an independent simulation route. General derivatives libraries provide much broader instrument infrastructure, while model collections provide many formulas. This package deliberately serves the narrower workflow around the quadratic-drift LogSV model, with Heston as a like-for-like benchmark and paper implementations tied to the same code.
- A common pricing interface.
ModelPricerrequiresprice_chainand supplies single-option, slice and implied-volatility helpers. LogSV and Heston implement both transform and Monte Carlo pricing, plus constrained calibration. Other models expose their supported capabilities; an unavailable simulation or calibration method raisesNotImplementedError. See the model and pricer boundaries. - Calibration that fails visibly. Validated LogSV and Heston calibration reports
optimizer failures through
CalibrationError; LogSV'sConstraintsTypeselects the martingale conditions. - One lightweight
OptionChain. Bundled simulated chains, normalised OCA panels, and local CBOE, ThetaData and Tardis caches all converge to the same container, so fitting and pricing code does not depend on the original provider. - Diagnostics in declared quote units. Fit residuals support scalar or per-quote spread floors. Native quote diagnostics retain tick-size, price-bound and missing-data evidence, separately from any caller-defined acceptance policy. Research helpers provide synthetic challenge cases and saved-study verification in a source checkout.
- Paper implementations tied to the same code. The
papers/directory reproduces the computations and figures of six papers with the package's own pricers.
For reusable model dynamics, the provisional
HestonModel adapter supplies named path states and joint transforms
without a quote chain. HestonPricer remains the stable pricing/calibration entry point;
products and valuation
own payoffs and discounting.
Use stochvolmodels for European vanilla pricing and implied-volatility analytics under stochastic volatility, for model calibration to option chains (a calibration example to Bitcoin options data is included), and for replicating the papers listed under Statement of need.
It is not a general derivatives platform: there is no American-option, local-volatility, or
term-structure framework. Black-Scholes-Merton and absolute-normal Bachelier analytics are
provided by the required
vanilla-option-pricers package and
re-exported from stochvolmodels; for strategy backtesting and reporting, use
qis.
Install using
pip install stochvolmodelsUpgrade using
pip install --upgrade stochvolmodelsClone using
git clone https://github.com/ArturSepp/StochVolModels.gitCore dependencies:
python >= 3.11vanilla-option-pricers >= 2.1.0numba >= 0.60.0numpy >= 2.0scipy >= 1.12.0pandas >= 2.2.0matplotlib >= 3.8.0seaborn >= 0.13.0
Version 2.6.1.dev0 and newer require Python 3.11 or newer. Python 3.12 is the development and primary CI interpreter. Published version 2.5.0 retains its original Python 3.10 requirement.
Optional extras:
| Extra | Installs | Needed for |
|---|---|---|
research |
qis >= 5.11.0, option-chain-analytics[cboe] >= 6.0.0 |
option-chain calibration, local Parquet data, and scripts in papers/ |
visualization |
plotly >= 5.0.0 |
interactive figures |
numerical |
scikit-learn >= 1.3.0, statsmodels >= 0.14.2 |
statistical fits |
jupyter |
jupyter, notebook, jupyterlab, ipykernel, ipywidgets |
notebooks |
Install an extra using
pip install stochvolmodels[research]The library itself imports none of these: import stochvolmodels needs the core dependencies only.
Pinned contributor lint and audit tools live in PEP 735 groups and are installed with uv; see
Testing.
Paper workflows and cached-data examples read two machine-local roots from the ignored
src/stochvolmodels/settings.yaml (copy settings.yaml.example and set RESOURCE_PATH and
OUTPUT_PATH); stochvolmodels.local_path returns them as absolute strings with a trailing
separator, following the qis convention. The local YAML is not included in Git or package
distributions; PyYAML is loaded only when it exists.
- Why stochvolmodels
- Installation
- Statement of need
- Overview
- Five-minute quickstart
- Examples
- Testing
- Changelog
- Feedback & contributing
- Acknowledgments
- Citation
- Ecosystem
- License
- Disclaimer
A stochastic-volatility specification is only evaluated when its analytic prices, its simulated
prices and its calibrated implied volatilities can be put side by side on quotes with explicit
conventions. stochvolmodels provides that workflow for the quadratic-drift log-normal SV model
and the Heston benchmark, and the same analytics power the research: the repository's papers/
directory reproduces the computations and figures of the papers below, from the quadratic-drift
log-normal SV model to cryptocurrency inverse options, robust stochastic volatility modelling,
impermanent-loss hedging in DeFi, stochastic volatility for the factor HJM framework and jump
risk premia under clustered jumps.
| Paper | Implemented analytics | Where |
|---|---|---|
| Sepp, A. and Rakhmonov, P. (2023), Log-normal Stochastic Volatility Model with Quadratic Drift, IJTAF 26(8) | The LogSV model: moment-generating-function Fourier pricing, Monte Carlo dynamics, martingale constraints and calibration; the paper figures and equation-mapped analysis | pricers/logsv_pricer.py, papers/logsv_model_with_quadratic_drift_ijtaf_2023, LogSV model |
| Sepp, A. and Rakhmonov, P. (2023), What is a robust stochastic volatility model, SSRN 4647027 | Model comparison analytics of the robustness study | papers/volatility_models, robust volatility models |
| Lucic, V. and Sepp, A. (2024), Valuation and Hedging of Cryptocurrency Inverse Options, Quantitative Finance 24(7), 851–869 | Inverse-option valuation and hedging, with the Bitcoin chain calibration | papers/inverse_options, inverse options, Bitcoin options |
| Lipton, A., Lucic, V. and Sepp, A. (2025), Unified approach for hedging impermanent loss of liquidity provision, Digital Finance 7, 429-477 | Impermanent-loss hedging analytics | papers/unified_approach_for_hedging_il_df_2025, impermanent-loss hedging |
| Sepp, A. and Rakhmonov, P. (2025), Stochastic Volatility for Factor Heath-Jarrow-Morton Framework, Review of Derivatives Research 28(3), 12 (preprint SSRN 4646925) | Factor HJM stochastic-volatility pricing (experimental research surface) | papers/sv_for_factor_hjm_rdr_2025, factor HJM |
| Liu, F., Packham, N. and Sepp, A., Jump risk premia in the presence of clustered jumps, SSRN 4735365 | Related development code (not an exact replication package), with the Tardis hourly and EOD chain conventions | papers/jump_risk_premia_clustered_jumps, Hawkes jump-diffusion |
StochVolModels/
├── src/
│ └── stochvolmodels/
│ ├── data/ # option-chain containers and sample data
│ ├── fitters/ # approximate LogSV smile fitter and Student-t utilities
│ ├── pricers/ # analytic, transform, and Monte Carlo models
│ ├── utils/ # quadrature, payoff, plotting, and rate helpers
│ └── tests/ # shipped pytest suite and regression data
├── examples/ # repository-only runnable workflows
│ ├── getting_started/
│ ├── pricing/
│ ├── calibration/
│ └── options_time_series_data/
├── papers/ # paper replications and labelled development code
├── docs/ # documentation sources and figures
└── README.md
The model interface is src/stochvolmodels/pricers/model_pricer.py. ModelPricer separates
what a model must supply from what every model inherits: to add a model engine, subclass
ModelPricer, define the model's ModelParams dataclass, and implement price_chain (analytic
pricing of an OptionChain, typically a wrapper over a Numba-compiled moment-generating-function
transform), model_mc_price_chain with simulate_vol_paths and simulate_terminal_values, and
calibrate_model_params_to_chain (reporting failures through CalibrationError). Single-option
and slice pricing (price_vanilla, price_slice), model implied vols from chain prices
(compute_chain_prices_with_vols), Monte Carlo implied vols with confidence bounds
(compute_mc_chain_implied_vols) and the visualisation layer (plot_model_ivols,
plot_model_ivols_vs_bid_ask, plot_model_ivols_vs_mc) are inherited. LogSVPricer and
HestonPricer are the two stable reference implementations of this contract; see
software design.
| Model | Dynamics | Implementation | Article · example |
|---|---|---|---|
| Karasinski-Sepp log-normal SV with quadratic drift |
|
LogSVPricer, LogSvParams in src/stochvolmodels/pricers/logsv_pricer.py
|
LogSV model · examples/docs/logsv_model.py
|
| Heston SV |
|
HestonPricer, HestonParams in src/stochvolmodels/pricers/heston_pricer.py
|
Heston model · examples/docs/heston_model.py
|
| Approximate LogSV smile fitter | three-parameter smile illustration for one slice; not a substitute for calibrating the full term-structure model |
fit_logsv_ivols, calc_logsv_ivols in stochvolmodels.fitters
|
LogSV smile fitter · run_logsv_smile_fitter.py
|
| Area | What the package computes |
|---|---|
| Analytic pricing | European vanilla prices and implied vols from closed-form moment generating functions by Fourier transform; single options, slices and chains with uniform strikes |
| Monte Carlo | Model-dynamics simulation, chain pricing from simulated paths, Monte Carlo implied vols with confidence bounds, analytic-versus-MC comparison plots |
| Calibration | Constrained calibration to an option chain with ConstraintsType martingale conditions, bid/ask comparison plots; fit and surface diagnostics with bid/ask residuals, strike and calendar consistency, and comparisons at common tenors |
| Option data | OptionChain from the bundled OCA-simulated chain (get_oca_simulated_chain_data(), no credentials), any normalised OCA panel (load_option_chain()), or local CBOE, ThetaData EOD and Tardis hourly/EOD caches; see option chains and conventions |
| Provisional direct-import surface | ModelPaths, PathModel, TransformModel (first implementations LogSvModel, TgarchModel); TerminalDistributionModel, TerminalSmileModel (TdistTerminalModel, GmmTerminalModel, InverseGammaNormalTerminalModel); EuropeanOptionPayoff, IntegratedVarianceOptionPayoff, value_paths, value_paths_self_normalized; regime-switching LogSV equilibrium, transform, Fourier, Monte Carlo and risk-premium attribution analytics — shipped and documented in the API guide but outside __all__ until their contracts are stabilised across another dynamic model |
| Experimental research surfaces | Rough-LogSV Monte Carlo and Factor HJM: characterised pricing paths are tested, deep imports under stochvolmodels.pricers.rough_logsv and stochvolmodels.pricers.factor_hjm may evolve between minor releases; the legacy Gaussian_interval quadrature raises ImportError without orthopy/quadpy, the incomplete rough-Heston kernel raises NotImplementedError |
The names listed by stochvolmodels.__all__ are the stable high-level API. Historical
package-root names remain available lazily for compatibility, but names not in __all__ should
be treated as advanced interfaces.
This first result runs from the installed package and needs no credentials, local YAML, qis,
OptionChainAnalytics, or data download:
import stochvolmodels as svm
params = svm.LogSvParams(
sigma0=1.0,
theta=1.0,
kappa1=5.0,
kappa2=5.0,
beta=0.2,
volvol=2.0,
)
price, implied_vol = svm.LogSVPricer().price_vanilla(
params=params,
ttm=0.25,
forward=1.0,
strike=1.0,
optiontype="C",
)
print(f"price={price:.6f}, implied_vol={implied_vol:.6f}")Expected output after the initial Numba compilation:
price=0.197331, implied_vol=0.999577
The fuller repository quickstart prices a two-maturity
synthetic chain and prints two five-strike slices with deterministic reference values including
vanilla_price=0.197331 and six_month_atm_price=0.275202. Repository examples are not
included in the wheel, so clone the repository before running them:
python examples/getting_started/quickstart.pyThe first call normally takes seconds because Numba compiles the numerical kernels.
Computing model prices and vols (examples/calibration/run_lognormal_sv_pricer.py):
import numpy as np
from stochvolmodels import LogSVPricer, LogSvParams, OptionChain
logsv_pricer = LogSVPricer()
params = LogSvParams(sigma0=1.0, theta=1.0, kappa1=5.0, kappa2=5.0, beta=0.2, volvol=2.0)
# 1. compute the price
model_price, vol = logsv_pricer.price_vanilla(params=params,
ttm=0.25,
forward=1.0,
strike=1.0,
optiontype='C')
print(f"price={model_price:0.4f}, implied vol={vol: 0.2%}")
# 2. prices for slices
model_prices, vols = logsv_pricer.price_slice(params=params,
ttm=0.25,
forward=1.0,
strikes=np.array([0.9, 1.0, 1.1]),
optiontypes=np.array(['P', 'C', 'C']))
# 3. prices for option chain with uniform strikes
option_chain = OptionChain.get_uniform_chain(ttms=np.array([0.083, 0.25]),
ids=np.array(['1m', '3m']),
strikes=np.linspace(0.9, 1.1, 3))
model_prices, vols = logsv_pricer.compute_chain_prices_with_vols(option_chain=option_chain, params=params)Calibration to sample Bitcoin options data and comparison of model prices with Monte Carlo (same script):
import stochvolmodels as sv
btc_option_chain = sv.get_btc_test_chain_data()
params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=5.0, kappa2=None, beta=0.15, volvol=2.0)
btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
params0=params0,
constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
params=btc_calibrated_params)
uniform_chain_data = OptionChain.to_uniform_strikes(obj=btc_option_chain, num_strikes=31)
logsv_pricer.plot_comp_mma_inverse_options_with_mc(option_chain=uniform_chain_data,
params=btc_calibrated_params,
nb_path=400000)The full fitted-surface and analytic-versus-Monte-Carlo figures are generated by the paper
workflow in papers/logsv_model_with_quadratic_drift_ijtaf_2023
and are not committed as build artifacts.
Heston slices across correlations (examples/pricing/run_heston.py):
import numpy as np
import matplotlib.pyplot as plt
from stochvolmodels import HestonPricer, HestonParams, OptionChain
params_dict = {'rho=0.0': HestonParams(v0=0.2**2, theta=0.2**2, kappa=4.0, volvol=0.75, rho=0.0),
'rho=-0.4': HestonParams(v0=0.2**2, theta=0.2**2, kappa=4.0, volvol=0.75, rho=-0.4),
'rho=-0.8': HestonParams(v0=0.2**2, theta=0.2**2, kappa=4.0, volvol=0.75, rho=-0.8)}
option_chain = OptionChain.get_uniform_chain(ttms=np.array([0.25]), ids=np.array(['3m']), strikes=np.linspace(0.8, 1.15, 20))
option_slice = option_chain.get_slice(id='3m')
pricer = HestonPricer()
pricer.plot_model_slices_in_params(option_slice=option_slice, params_dict=params_dict)
plt.show()Approximate smile fit on the bundled chain — one two-maturity chain captured from OCA's deterministic simulator ships with the package; it is generated data rather than a vendor snapshot:
import numpy as np
from stochvolmodels.data.sample_option_chains import get_oca_simulated_chain_data
from stochvolmodels.fitters import calc_logsv_ivols, fit_logsv_ivols
chain = get_oca_simulated_chain_data()
idx = 1 # one-month slice
log_strikes = np.log(chain.strikes_ttms[idx] / chain.forwards[idx])
mid_vols = 0.5 * (chain.bid_ivs[idx] + chain.ask_ivs[idx])
fit = fit_logsv_ivols(log_strikes, mid_vols, chain.ttms[idx])
fitted_vols = calc_logsv_ivols(log_strikes, **fit)The same chain can be passed to LogSVPricer.calibrate_model_params_to_chain() for a full
analytic model calibration.
| Data route | Local data needed | SVM entry point | Example |
|---|---|---|---|
| Bundled OCA-generated chain | No | get_oca_simulated_chain_data() |
run_logsv_smile_fitter.py |
| Any normalised OCA panel | No for OCA's simulator | load_option_chain() |
run_oca_logsv_calibration.py |
| OCA CBOE cache | Yes | load_cboe_option_chain() |
load_cboe_option_chain.py |
| OCA ThetaData EOD cache | Yes | load_thetadata_option_chain() |
run_spy_thetadata_month.py |
| OCA Tardis hourly archive | Yes | load_tardis_hourly_option_chain() |
clustered-jump paper workflows |
| OCA Tardis 08:00 UTC EOD cache | Yes | load_tardis_eod_option_chain() |
clustered-jump chain calibration |
The repository does not redistribute CBOE, ThetaData, or Tardis records. OCA owns provider access,
normalisation, and caches; SVM receives only the strikes, option types, forwards, discounts, and
bid/ask quotes required for an illustration or calibration. The cached routes need
pip install "stochvolmodels[research]" and RESOURCE_PATH in the local settings file; the
examples guide describes each loader and the cache layout, and
papers/jump_risk_premia_clustered_jumps/README.md
sets the script-by-script Tardis hourly-versus-EOD data policy. The
examples/docs/ scripts are the canonical scripts of the documentation
articles, and examples/pricing/ holds the Heston, Hawkes, quadratic
variance and BSM transform runners.
| Method | Implementation | Article · script |
|---|---|---|
| Fourier pricing from the moment generating function (IJTAF paper) | LogSVPricer.price_chain |
European option pricing, affine expansion · european_option_pricing.py |
| Analytic versus Monte Carlo validation | model_mc_price_chain, compute_mc_chain_implied_vols |
analytic vs Monte Carlo · analytic_vs_monte_carlo.py |
| Martingale conditions and skews | ConstraintsType |
martingale conditions and skews · martingale_conditions_and_skews.py |
| Calibration to a chain | calibrate_model_params_to_chain |
calibration · calibration.py |
| Inverse options (Quantitative Finance paper) | inverse-option payoff branches | inverse options · inverse_options.py |
| Quadratic variance options | examples/pricing QVAR runners |
quadratic variance options · quadratic_variance_options.py |
| Reproducing the papers | papers/ |
reproducing the papers |
uv sync --locked --group test
uv run --no-sync pytest
uv run --locked --only-group lint ruff check --select F,TID251,TID253,ICN src/stochvolmodelsFrom a source checkout, the complete public artifact and documentation path is verified with:
uv sync --locked --group test --extra docs
uv run --no-sync pytest -m "not slow"
uv run --no-sync pytest -m slow
uv run --no-sync pytest --cov=stochvolmodels --cov-report=json
uv run --no-sync python scripts/check_coverage_scopes.py coverage.json
uv run --no-sync python -m sphinx -W --keep-going -b html docs docs/_build/html
uv build
uv run --no-project python scripts/check_sdist_contents.py dist/*.tar.gz
uv run --no-project python scripts/check_wheel_contents.py dist/*.whlSee the testing and coverage guide and the full verification guide.
Release history is maintained in CHANGELOG.md.
- Bug: report a reproducible bug, including the package version, Python/platform, inputs, and expected versus actual result.
- Feature: request a feature, describing the payoff, quote convention, or model edge case, the current workaround, and the smallest useful API.
- Question or methodology: search or open an issue and name the model, paper section, or convention involved.
- Contribution: read CONTRIBUTING.md for scope, development commands, numerical-change rules, and pull-request guidance.
- Parviz Rakhmonov, co-developer of the log-normal stochastic volatility model with quadratic drift that the package implements.
Machine-readable metadata is available in CITATION.cff. If you use this
package in your research, please cite the software and the relevant papers:
@misc{sepp2026stochvolmodels,
title={StochVolModels: Python Implementation of Stochastic Volatility Models},
author={Sepp, Artur},
year={2026},
version={2.6.1.dev1},
url={https://github.com/ArturSepp/StochVolModels},
note={Python package for pricing analytics and Monte Carlo simulations}
}
@article{sepprakhmonov2023,
title={Log-normal stochastic volatility model with quadratic drift},
author={Sepp, Artur and Rakhmonov, Parviz},
journal={International Journal of Theoretical and Applied Finance},
volume={26},
number={8},
year={2023},
url={https://www.worldscientific.com/doi/epdf/10.1142/S0219024924500031}
}
@article{sepprakhmonov2023b,
title={What is a robust stochastic volatility model},
author={Sepp, Artur and Rakhmonov, Parviz},
year={2023},
note={Working paper},
url={https://ssrn.com/abstract=4647027}
}
@article{lucicsepp2024,
title={Valuation and hedging of cryptocurrency inverse options},
author={Lucic, Vladimir and Sepp, Artur},
journal={Quantitative Finance},
volume={24},
number={7},
pages={851--869},
year={2024},
url={https://www.tandfonline.com/doi/full/10.1080/14697688.2024.2364804}
}
@article{sepprakhmonov2025,
title={Stochastic volatility for factor Heath-Jarrow-Morton framework},
author={Sepp, Artur and Rakhmonov, Parviz},
volume={28},
number={3},
pages={12},
year={2025},
journal={Review of Derivatives Research},
doi={10.1007/s11147-025-09217-4},
note={Preprint: https://ssrn.com/abstract=4646925}
}This package is part of an open-source Python stack for quantitative finance. The ArturSepp profile is the canonical catalogue:
| Package | Purpose |
|---|---|
qis |
Performance analytics, backtesting, and factsheet reporting |
optimalportfolios |
Portfolio construction and rolling backtesting |
factorlasso |
Sparse factor-model estimation |
bbg-fetch |
Bloomberg data in pandas DataFrames |
option-chain-analytics |
Point-in-time option-chain data and queries |
vanilla-option-pricers |
Numba-vectorised BSM and Bachelier pricing |
stochvolmodels (this package) |
Stochastic-volatility pricing and calibration |
trendfollowing |
Closed-form trend-following analytics |
privateassets |
Multi-factor PME for private assets |
goal-based-allocation |
Goal-based allocation under regime-switching jump-diffusions |
For this package, vanilla-option-pricers is a runtime dependency; the research extra adds qis
and option-chain-analytics.
MIT — see LICENSE.txt.
stochvolmodels is distributed FREE & WITHOUT ANY WARRANTY under the MIT License. See
LICENSE.txt in the release for details. Use the routes in
Feedback & contributing for bugs, feature requests and
methodology questions.