Skip to content

About

Fourier-transform pricing, Monte Carlo validation, and calibration of European options under stochastic-volatility models in Python

Topics

Resources

Contributing

Stars

239 stars

Watchers

7 watching

Forks

Repository files navigation

stochvolmodels

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

PyPI Python CI Docs License Downloads Monthly Open LogSV quickstart in Colab

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


Why stochvolmodels

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.

Key differentiators

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. ModelPricer requires price_chain and 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 raises NotImplementedError. See the model and pricer boundaries.
  • Calibration that fails visibly. Validated LogSV and Heston calibration reports optimizer failures through CalibrationError; LogSV's ConstraintsType selects 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.

When to use it — and when not

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.

Installation

Install using

pip install stochvolmodels

Upgrade using

pip install --upgrade stochvolmodels

Clone using

git clone https://github.com/ArturSepp/StochVolModels.git

Core dependencies:

  • python >= 3.11
  • vanilla-option-pricers >= 2.1.0
  • numba >= 0.60.0
  • numpy >= 2.0
  • scipy >= 1.12.0
  • pandas >= 2.2.0
  • matplotlib >= 3.8.0
  • seaborn >= 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.

Table of contents

  1. Why stochvolmodels
  2. Installation
  3. Statement of need
  4. Overview
  5. Five-minute quickstart
  6. Examples
  7. Testing
  8. Changelog
  9. Feedback & contributing
  10. Acknowledgments
  11. Citation
  12. Ecosystem
  13. License
  14. Disclaimer

Statement of need

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

Overview

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.

Methods

Model Dynamics Implementation Article · example
Karasinski-Sepp log-normal SV with quadratic drift $dS_t = r(t) S_t dt + \sigma_t S_t dW^{(0)}_t$, $d\sigma_t = (\kappa_1 + \kappa_2 \sigma_t)(\theta - \sigma_t) dt + \beta \sigma_t dW^{(0)}_t + \varepsilon \sigma_t dW^{(1)}_t$, $dI_t = \sigma^2_t dt$, with uncorrelated $W^{(0)}, W^{(1)}$, volatility beta $\beta \in \mathbb{R}$, volatility of residual volatility $\varepsilon > 0$ and $\vartheta^2 = \beta^2 + \varepsilon^2$ LogSVPricer, LogSvParams in src/stochvolmodels/pricers/logsv_pricer.py LogSV model · examples/docs/logsv_model.py
Heston SV $dS_t = r(t) S_t dt + \sqrt{V_t} S_t dW^{(S)}_t$, $dV_t = \kappa(\theta - V_t) dt + \vartheta \sqrt{V_t} dW^{(V)}_t$, with correlation $\rho$ between $W^{(S)}$ and $W^{(V)}$ 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

Analytics at a glance

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.

Five-minute quickstart

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.py

The first call normally takes seconds because Numba compiles the numerical kernels.

Examples

Highlighted examples

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.

Runnable examples

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.

From method to implementation

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

Testing

uv sync --locked --group test
uv run --no-sync pytest
uv run --locked --only-group lint ruff check --select F,TID251,TID253,ICN src/stochvolmodels

From 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/*.whl

See the testing and coverage guide and the full verification guide.

Changelog

Release history is maintained in CHANGELOG.md.

Feedback & contributing

  • 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.

Acknowledgments

  • Parviz Rakhmonov, co-developer of the log-normal stochastic volatility model with quadratic drift that the package implements.

Citation

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}
}

Ecosystem

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.

License

MIT — see LICENSE.txt.

Disclaimer

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.

About

Fourier-transform pricing, Monte Carlo validation, and calibration of European options under stochastic-volatility models in Python

Topics

Resources

Contributing

Stars

239 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages