Skip to main content

Fourier-based European option pricing with Carr-Madan FFT, FRFT, and COS under characteristic-function models.

Project description

fourier-option-pricer

Fast European option pricing via Fourier transform methods under characteristic-function models.

Carr, P., & Madan, D. (1999). Option valuation using the fast Fourier transform. Journal of Computational Finance, 2(4), 61–73. https://doi.org/10.21314/JCF.1999.043

Lewis, A. L. (2001). A simple option formula for general jump-diffusion and other exponential Lévy processes. SSRN Working Paper. https://ssrn.com/abstract=282110 (Heston and Variance Gamma characteristic functions are provided by PyFENG, Prof. Jaehyuk Choi's library.)

Fang, F., & Oosterlee, C. W. (2008). A novel pricing method for European options based on Fourier-cosine series expansions. SIAM Journal on Scientific Computing, 31(2), 826–848. https://doi.org/10.1137/080718061

Junike, G., & Pankrashkin, K. (2022). Precise option pricing by the COS method — how to choose the truncation range. Applied Mathematics and Computation, 421, 126935. https://doi.org/10.1016/j.amc.2022.126935

Ruijter, M. J., Versteegh, M., & Oosterlee, C. W. (2015). On the application of spectral filters in a Fourier option pricing technique. Journal of Computational Finance, 19(1), 75–106. https://doi.org/10.21314/JCF.2015.306 (Used as inspiration for the spectral-filtering layer in the adaptive filtered-COS extension. The project adapts this idea into a tolerance-driven selector over no-filter and filtered COS policies, rather than forcing a single fixed filter.)

Core concept

Fourier pricing exploits the fact that, for most asset models, the characteristic function

$$\phi(u) = \mathbb{E}!\left[e^{iu \ln S_T}\right]$$

is known in closed form even when the option price integral has no analytic solution. Given $\phi$, a European call can be priced by a single numerical integral. The three methods implemented here differ in how they discretise that integral:

Method Key idea
Carr–Madan FFT Damp the payoff, apply FFT to price a whole strike grid at once
Lewis single-integral Parseval identity; avoids the dampening parameter entirely
COS (Fang–Oosterlee) Expand the risk-neutral density in a cosine series on $[a, b]$

Truncation and filtering

The COS method requires choosing a truncation interval $[a,b]$ for the log-price density. Two truncation strategies are implemented:

  • Cumulant rule (Fang & Oosterlee 2008) — sets $[a,b]$ from the first four cumulants of $\ln S_T$.
  • Tolerance rule (Junike & Pankrashkin 2022) — widens $[a,b]$ iteratively until the tail-mass proxy falls below a user-specified tolerance. This is used for stress cases where the cumulant rule can become unreliable or overly wide.

In addition, the project implements an adaptive filtered-COS extension inspired by Ruijter, Versteegh and Oosterlee (2015). Rather than applying one fixed spectral filter, the idea is adapted into a tolerance-driven policy selector: five candidates (plain Junike-COS and four filtered variants) are evaluated; the fastest that meets a target error tolerance is returned.

  • Why filtering is added: truncation fixes the interval, but COS can still show finite-series ringing near payoff kinks, short maturities, or jump-heavy densities.
  • Candidate filters: Fejér, Lanczos, raised-cosine, and exponential; plain Junike-COS is always kept in the pool so the selector can fall back when filtering does not help.
  • Selection rule: use the cheapest candidate that meets the tolerance (default ε = 1 × 10⁻⁶). Filter selection is entirely deterministic — no tuning required.

Models

Family Models
Pure diffusion Black–Scholes–Merton
Stochastic volatility Heston, OU-SV
Pure jump / Lévy Variance Gamma, NIG, CGMY
Jump diffusion Kou double-exponential
SV + jumps Bates, Heston–Kou, Heston–CGMY

Installation

pip install fourier-option-pricer

For local development:

git clone https://github.com/nl2992/fourier-option-pricer.git
cd fourier-option-pricer
pip install -e ".[test]"
pytest

Quick start

import numpy as np
import foureng as fe

# Market inputs
fwd = fe.ForwardSpec(S0=100.0, r=0.01, q=0.02, T=1.0)

# Heston model parameters
params = fe.HestonParams(kappa=4.0, theta=0.25, nu=1.0, rho=-0.5, v0=0.04)

# Characteristic function
phi = lambda u: fe.heston_cf_form2(u, fwd, params)

# Strike grid
strikes = np.array([80.0, 90.0, 100.0, 110.0, 120.0])

# Price with COS (standard truncation)
cumulants = fe.heston_cumulants(fwd, params)
grid = fe.cos_auto_grid(cumulants, N=256, L=10.0)
result = fe.cos_prices(phi, fwd, strikes, grid)
print(result.call_prices)

# Price with Carr–Madan FFT
cm_grid = fe.FFTGrid(N=4096, eta=0.25, alpha=1.5)
cm_prices = fe.carr_madan_price_at_strikes(phi, fwd, cm_grid, strikes)
print(cm_prices)

# Implied volatility
atm_iv = fe.implied_vol_newton_safeguarded(
    price=float(result.call_prices[2]),
    inputs=fe.BSInputs(F0=fwd.F0, K=100.0, T=fwd.T, r=fwd.r, q=fwd.q, is_call=True),
)
print(atm_iv)

Improved COS truncation (Junike rule)

grid = fe.cos_improved_grid(cumulants, model="heston", params=params)
result = fe.cos_prices(phi, fwd, strikes, grid)

Extension: adaptive filtered-COS policy layer

The Junike-style COS policy improves truncation-range selection, but COS accuracy is controlled by both the truncation interval and the finite cosine expansion. As an extension, this repo adds an adaptive filtered-COS overlay inspired by Ruijter, Versteegh and Oosterlee's spectral-filtering work for Fourier option pricing.

The filter is applied to the COS expansion coefficients and is tested as one candidate inside a deterministic numerical-policy search. The adaptive selector compares vanilla COS, Junike-COS, and filtered Junike-COS, then selects the fastest candidate satisfying a target error tolerance.

This extension does not necessarily improve and claim, filtered-COS universally dominates Junike-COS. The intended object is the adaptive selector, which can choose no filter where filtering is unnecessary.

Usage:

from foureng.pipeline import price_strip
from foureng.utils.spectral_filters import COSFilterSpec
from foureng.utils.grids import COSGridPolicy

policy = COSGridPolicy(
    mode="benchmark",
    truncation="tolerance",
    centered=True,
    dx_target=0.01,
    L=10.0,
    eps_trunc=1e-10,
    max_N=8192,
    width_fallback=0.0,
)

prices = price_strip(
    "vg",
    "cos_filtered",
    strikes,
    fwd,
    params,
    grid=(policy, COSFilterSpec("exponential", order=8)),
)

For the full adaptive grid-search selector (comparing vanilla, Junike, and filtered candidates):

from foureng.experiments.cos_filter_grid_search import (
    default_filtered_cos_candidates,
    run_filtered_cos_grid_search,
    select_fastest_under_tolerance,
)

df = run_filtered_cos_grid_search(
    model="vg", strikes=strikes, fwd=fwd, params=params,
    reference=reference_prices, tol=1e-6,
)
best = select_fastest_under_tolerance(df, tol=1e-6)

References:

  • Junike, G. and Pankrashkin, K. (2022), "Precise option pricing by the COS method — How to choose the truncation range," Applied Mathematics and Computation, 421, 126935.
  • Ruijter, M. J., Versteegh, M. and Oosterlee, C. W. (2015), "On the application of spectral filters in a Fourier option pricing technique," Journal of Computational Finance.
  • Junike, G. (2024), "On the number of terms in the COS method for European option pricing," Numerische Mathematik.

Available spectral filters: "none" (identity), "fejer", "lanczos", "raised_cosine", "exponential" (order-p tunable).

Notebooks

Notebook Description
notebooks/demo.ipynb Full walkthrough: models, pricers, IV surface, calibration, Greeks, MC, and the adaptive filtered-COS extension
notebooks/adaptive_cos.ipynb Standalone comparison of vanilla COS, Junike-COS, and adaptive filtered-COS on the canonical Fang–Oosterlee (2008) test cases

Open In Colab

API reference

ForwardSpec(S0, r, q, T)

Parameter Type Description
S0 float Spot price
r float Continuously compounded risk-free rate
q float Dividend yield or foreign rate
T float Time to maturity in years

Provides F0 (forward price) and disc (discount factor).

Model parameter classes

Class Model
HestonParams(kappa, theta, nu, rho, v0) Heston stochastic volatility
VGParams(sigma, nu, theta) Variance Gamma
KouParams(sigma, lam, p, eta1, eta2) Kou double-exponential jump diffusion
BatesParams(...) Bates (Heston + Poisson jumps)
CGMYParams(C, G, M, Y) CGMY pure-jump Lévy
NIGParams(alpha, beta, delta) Normal Inverse Gaussian

cos_prices(phi, fwd, strikes, grid)

Parameter Type Description
phi callable Characteristic function phi(u)
fwd ForwardSpec Market inputs
strikes (K,) array Strike prices
grid COSGrid Truncation grid from cos_auto_grid or cos_improved_grid

Returns a COSResult with fields strikes and call_prices.

carr_madan_price_at_strikes(phi, fwd, grid, strikes)

Parameter Type Description
phi callable Characteristic function
fwd ForwardSpec Market inputs
grid FFTGrid(N, eta, alpha) FFT grid
strikes (K,) array Strike prices

Returns (K,) array of call prices.

cos_auto_grid(cumulants, N, L) / cos_improved_grid(cumulants, model, params)

Parameter Type Description
cumulants cumulant object From heston_cumulants, vg_cumulants, etc.
N int Number of COS expansion terms
L float Truncation multiplier (standard rule only)
model str Model name, e.g. "heston" (improved rule only)
params param dataclass Model parameters (improved rule only)

Returns a COSGrid.

COSGridPolicy

Dataclass that controls the adaptive truncation-interval and N-selection strategy used by cos_improved_grid and filtered_cos_prices.

Key parameter Default Description
truncation "tolerance" "heuristic" (Fang–Oosterlee L rule), "tolerance" (Junike iterative widening), or "paper"
eps_trunc 1e-10 Tail-mass threshold for the tolerance rule
dx_target model default Target spatial resolution (b−a)/N; drives adaptive N selection
fixed_N None Hard override for N (bypasses adaptive selection)
mode "benchmark" "benchmark" for tighter accuracy, "surface" for speed
max_N 16384 Upper cap on adaptively chosen N

recommended_cos_policy(model, params, *, mode)

Returns the recommended COSGridPolicy for a given model string (e.g. "heston", "vg", "kou") and parameter object. Provides sensible defaults without manual tuning.

filtered_cos_prices(phi, fwd, strikes, grid, *, filter_spec)

COS pricer with a spectral filter applied to the characteristic-function samples before cosine inversion. Reduces finite-series oscillations (Gibbs-like ringing) near payoff kinks, short maturities, or jump-heavy densities.

Parameter Type Description
phi callable Characteristic function phi(u)
fwd ForwardSpec Market inputs
strikes (K,) array Strike prices
grid COSGrid Resolved grid from cos_improved_grid
filter_spec COSFilterSpec Filter to apply (default: exponential, order 8)

Returns a COSResult.

COSFilterSpec(name, order, alpha)

Spectral filter specification passed to filtered_cos_prices.

name value Description
"none" Identity — no filtering (plain COS)
"fejer" Fejér averaging kernel
"lanczos" Lanczos (sinc) filter
"raised_cosine" Raised-cosine (Hann) window
"exponential" Order-p exponential; order=8 recommended

implied_vol_newton_safeguarded(price, inputs)

Parameter Type Description
price float Option price
inputs BSInputs BSInputs(F0, K, T, r, q, is_call)

Returns implied volatility as float.

Extended methodology and results

Detailed numerical experiments, replication notes, runtime benchmarks, and implementation commentary are kept outside the README to keep this page concise.

See:

docs/methodology_and_results.md

This document records:

  • the Fang-Oosterlee COS replication workflow;
  • the Carr-Madan benchmark setup;
  • the Monte Carlo comparison setup;
  • COS truncation-interval behaviour;
  • improved COS grid logic;
  • runtime and error reporting rules;
  • model-by-model observations;
  • known numerical limitations;
  • adaptive filtered-COS extension (spectral filters + policy grid-search selector).

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fourier_option_pricer-0.3.1.tar.gz (114.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fourier_option_pricer-0.3.1-py3-none-any.whl (95.4 kB view details)

Uploaded Python 3

File details

Details for the file fourier_option_pricer-0.3.1.tar.gz.

File metadata

  • Download URL: fourier_option_pricer-0.3.1.tar.gz
  • Upload date:
  • Size: 114.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fourier_option_pricer-0.3.1.tar.gz
Algorithm Hash digest
SHA256 6db1bf8e099fb6dd68a4f301152f3e64091a1830faa65d76a79ddbb232d6bf3e
MD5 8f8d38a30d784f6cce3c176d9e4bbfd2
BLAKE2b-256 d5718fd6e14c39d22da4ee53be352b89e8e7393da7cadc49a831d64a371ff269

See more details on using hashes here.

Provenance

The following attestation bundles were made for fourier_option_pricer-0.3.1.tar.gz:

Publisher: publish.yml on nl2992/fourier-option-pricer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fourier_option_pricer-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fourier_option_pricer-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 98ea7125bd635ab486d98450418dc4e2924d26d168dfa35c26f54e6a064ac9c1
MD5 6f7874b4e0e2d3892852cf88c04fce24
BLAKE2b-256 9221ab3bd31b36756d09be95bcad244686715acd039361ccacfe1bcaa1679884

See more details on using hashes here.

Provenance

The following attestation bundles were made for fourier_option_pricer-0.3.1-py3-none-any.whl:

Publisher: publish.yml on nl2992/fourier-option-pricer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page