Saltar al contenido
← Volver a proyectos

Opengreeks

Fast options pricing, Greeks, and implied volatility for Black-76, Black-Scholes, and Black-Scholes-Merton. Rust core, Python API. 5-180x faster than vollib.

#OpenGreeks

#Fast options pricing & Greeks for Python — Rust core, drop-in for vollib / py_vollib.

pip install opengreeks

#Up to 183× faster than vollib. Bit-identical Greeks. One dependency.

OpenGreeks reimplements the Black-76, Black-Scholes, and Black-Scholes-Merton pricing paths in zero-dependency Rust, exposed through PyO3 with the same function names and signatures as py_vollib / vollib. Migration is a one-line import swap; the math is unchanged.

And it goes further than vollib ever did: alongside the first-order Greeks, OpenGreeks ships a complete second- and third-order Greek surface — vanna, charm, vomma, speed, zomma, color, veta, ultima, dual delta, dual gamma — on all three models, each validated to ~1e-13 against automatic differentiation.

#Headline speedups vs vollib==1.0.7 (latest canonical)

Workload vollib 1.0.7 OpenGreeks Speedup
Black-76 chain — vega × 177 strikes 795 µs 4.3 µs 183×
Black-76 chain — delta × 177 strikes 695 µs 6.3 µs 110×
Black-76 chain — all-5 Greeks × 200 options ~4.2 ms ~45 µs ~94×
Black-76 chain — price × 177 strikes 812 µs 9.4 µs 86×
Implied volatility, single call 28.75 µs 0.50 µs 58×
Black-76 IV × 177-strike chain 4.37 ms 0.26 ms 17×
Black-76 theta (scalar) 11.38 µs 0.33 µs 34×
Black-Scholes IV (scalar) 18.88 µs 0.50 µs 38×
BSM IV (scalar) 18.83 µs 0.50 µs 38×

A full NIFTY option chain refresh (~200 options, all 5 Greeks + IV) drops from ~9 ms in vollib to ~0.3 ms in OpenGreeks — the difference between "saturates a core at 100 Hz" and "uses ~3% of one core."

#Parity that lets you trust the swap

29 edge cases × 3 models × 7 functions each, validated against vollib==1.0.7:

Greek max abs error vs vollib
delta, gamma, vega, theta, rho 0.0e+00 — bit-for-bit identical
price 1.4e-12 (rel 1.7e-14, ~14 digits)
IV (well-conditioned) 8.6e-10

Textbook anchors (Hull 13.6, Hull 17.1, Haug page 4) all pass. Full report: bench/RESULTS.md.

#One dependency, not seven

Package Runtime dependencies
pip install vollib py_lets_be_rational, cody_special, piecewise_rational, simplejson, numpy, pandas, scipy
pip install opengreeks numpy

No pandas, no scipy, no Cython build hell, no _testcapi import errors on minimal Python distributions.


Inspired by py_vollib and vollib by Gammon Capital LLC — same function names, same argument order, same numerical conventions. OpenGreeks reimplements the pricing math in Rust to deliver the speed and dependency wins above without changing any of the math.

Model OpenGreeks submodule vollib equivalent
Black-76 (futures options) opengreeks.black76 vollib.black
Black-Scholes (no dividends) opengreeks.black_scholes vollib.black_scholes
Black-Scholes-Merton (dividends) opengreeks.black_scholes_merton vollib.black_scholes_merton

Function names, argument order, and numerical conventions (vega × 0.01, theta / 365, rho × 0.01) match vollib exactly.


#Quick start

#Black-76 (futures / NIFTY options)

from opengreeks.black76 import black, implied_volatility, delta, gamma, vega, theta, rho

F, K, t, r, sigma = 22000.0, 22000.0, 30/365, 0.07, 0.18
price = black('c', F, K, t, r, sigma)        # 450.27
iv    = implied_volatility(price, F, K, r, t, 'c')   # 0.18
d     = delta('c', F, K, t, r, sigma)
g     = gamma('c', F, K, t, r, sigma)
v     = vega ('c', F, K, t, r, sigma)
th    = theta('c', F, K, t, r, sigma)
rh    = rho  ('c', F, K, t, r, sigma)

#Black-Scholes (equity, no dividend)

from opengreeks.black_scholes import black_scholes, implied_volatility, delta

price = black_scholes('c', 100.0, 90.0, 0.5, 0.01, 0.20)  # 12.111581

#Black-Scholes-Merton (equity with continuous dividend yield)

from opengreeks.black_scholes_merton import black_scholes_merton, implied_volatility, delta

price = black_scholes_merton('p', 100.0, 95.0, 0.5, 0.10, 0.20, 0.05)  # 2.4648 (Haug p.4)

#Chain-wide computation (NumPy batch)

For option-chain analytics, use the *_array variants — one PyO3 boundary crossing, internal tight loop:

import numpy as np
from opengreeks import black76

K = np.linspace(20000.0, 24000.0, 200)
F = np.full_like(K, 22000.0)
t = np.full_like(K, 30/365)
s = np.full_like(K, 0.18)

prices = black76.black_array('c', F, K, t, 0.07, s)
ivs    = black76.implied_volatility_array(prices, F, K, 0.07, t, 'c')
deltas = black76.delta_array('c', F, K, t, 0.07, s)

#Second- & third-order Greeks (what vollib never had)

Risk that first-order Greeks miss — how your delta drifts as vol moves, where gamma is about to spike, how vega bleeds into expiry — lives in the higher-order Greeks. OpenGreeks exposes all of them, on every model, with the same (flag, F/S, K, t, r, sigma[, q]) signature as the first-order Greeks:

from opengreeks.black76 import vanna, charm, vomma, speed, zomma, color, veta, ultima

F, K, t, r, sigma = 22000.0, 22000.0, 30/365, 0.07, 0.18
vanna('c', F, K, t, r, sigma)   # ∂delta/∂σ  — how your delta moves when IV moves
charm('c', F, K, t, r, sigma)   # ∂delta/∂τ  — delta bleed per year of time decay
vomma('c', F, K, t, r, sigma)   # ∂vega/∂σ   — vega convexity (vol-of-vol exposure)

Every Greek has a NumPy batch twin (vanna_array, charm_array, …) for whole-chain analytics in a single call.

Greek What it tells a trader Definition
vanna How delta shifts as IV moves (and how vega shifts as spot moves) ∂²V/∂S∂σ
charm Delta decay — how your hedge drifts purely from time passing ∂delta/∂τ
vomma Vega convexity — whether long-vol positions get longer as vol rises ∂²V/∂σ²
speed How fast gamma changes with spot — gamma-of-gamma ∂³V/∂S³
zomma How gamma changes as IV moves ∂gamma/∂σ
color Gamma decay — how gamma changes as expiry approaches ∂gamma/∂τ
veta Vega decay — how vega bleeds with time ∂vega/∂τ
ultima Sensitivity of vomma to vol — third-order vol risk ∂³V/∂σ³
dual delta Risk-neutral prob. of finishing ITM (strike sensitivity) ∂V/∂K
dual gamma Density of the terminal price at the strike ∂²V/∂K²

Conventions: raw mathematical partials (no per-day / per-1% rescaling); τ-derivatives (charm, color, veta) are per year of time-to-expiry. The same engine powers opengreeks.black_scholes and opengreeks.black_scholes_merton.

#Validated against automatic differentiation

vollib ships no second-order Greeks, so there is no library oracle to match. Instead every formula is checked against autograd automatic differentiation of the option price — exact partials, zero hand-derivation risk:

max relative error vs autograd
all 10 Greeks × 3 models < 1.2e-13 — machine precision

#Speed vs pure Python

There is nothing in vollib to race against here, so the baseline is a hand-written pure-Python (math) implementation of the identical closed forms:

Workload pure Python OpenGreeks Speedup
Higher-order Greek — scalar call ~0.7–1.1 µs ~0.42 µs ~2×
Higher-order Greek — 177-strike chain (*_array) ~95–165 µs ~4–8 µs 20–31×

The batch path crosses the Python↔Rust boundary once and loops in native code — so a full chain of second-order Greeks costs single-digit microseconds. Reproduce with bench/bench_second_order.py.


#Migrating from py_vollib / vollib

The function signatures are byte-identical. Migration is a one-line import swap.

Before:

from py_vollib.black.implied_volatility import implied_volatility as black_iv
from py_vollib.black.greeks.analytical import delta as black_delta, gamma as black_gamma

After:

from opengreeks.black76 import implied_volatility as black_iv
from opengreeks.black76 import delta as black_delta, gamma as black_gamma

The aliases (as black_iv etc.) keep the rest of your code unchanged.

Old import New import
from py_vollib.black import black from opengreeks.black76 import black
from py_vollib.black.implied_volatility import implied_volatility from opengreeks.black76 import implied_volatility
from py_vollib.black.greeks.analytical import delta, gamma, vega, theta, rho from opengreeks.black76 import delta, gamma, vega, theta, rho
from py_vollib.black_scholes import black_scholes from opengreeks.black_scholes import black_scholes
from py_vollib.black_scholes_merton import black_scholes_merton from opengreeks.black_scholes_merton import black_scholes_merton

#Reproducing the benchmarks

Want to verify the speedups on your hardware before you commit? Two-command repro:

# Install the baseline + opengreeks itself
pip install 'vollib>=1.0.7' 'py_lets_be_rational>=1.0.1' numpy opengreeks

# Run the bench against your CPU
python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/marketcalls/opengreeks/main/bench/bench_parity.py').read())"

Or clone the repo and run bench/bench_parity.py — prints a full parity + performance report in ~30 seconds. Headline numbers above are reproducible.

Full report including all 29 edge cases and chain-wide tables: bench/RESULTS.md.


#Project layout

OpenGreeks/                          # this monorepo
├── pyproject.toml                   # name = "opengreeks"   →   pip install opengreeks
├── Cargo.toml                       # Rust workspace
├── src/lib.rs                       # PyO3 cdylib  _opengreeks
├── python/opengreeks/
│   ├── __init__.py
│   ├── black76.py                   # opengreeks.black76 — vollib.black equivalent
│   ├── black_scholes.py             # opengreeks.black_scholes
│   └── black_scholes_merton.py      # opengreeks.black_scholes_merton
├── black76_rust/                    # pure-Rust Black-76 core (zero deps)
├── bsm_rust/                        # pure-Rust BSM (BS via q=0); depends on black76_rust
├── bench/
│   ├── bench_parity.py              # first-order parity + performance vs vollib
│   ├── bench_second_order.py        # higher-order Greeks: autograd correctness + pure-Python speed
│   └── RESULTS.md                   # full report
└── .github/workflows/CI.yml         # cargo test + wheel matrix + PyPI publish

Future pricing models (Heston stochastic vol, SABR, American/Bermudan via tree, etc.) slot in as new <model>_rust/ crates + opengreeks.<model> submodules without breaking the published API.


#Build from source

# Toolchain
rustup default stable
pip install maturin

# Develop install into a venv
cd OpenGreeks/
maturin develop --release

# Or build a wheel
maturin build --release
pip install target/wheels/opengreeks-*.whl

#Run tests

# Rust crates
cargo test --release

# Python parity + performance bench (first-order, vs vollib)
pip install 'vollib>=1.0.7' 'py_lets_be_rational>=1.0.1' numpy
python bench/bench_parity.py

# Second/third-order Greeks: correctness (autograd) + speed (vs pure Python)
pip install autograd numpy
python bench/bench_second_order.py

#Dependencies

Layer Dependencies
Rust core (black76_rust, bsm_rust) None — std math only
PyO3 wrapper (build-time) pyo3, numpy Rust crates
Python runtime numpy only

Compare to vollib==1.0.7 which pulls in py_lets_be_rational, cody_special, piecewise_rational, simplejson, numpy, pandas, scipy (7 packages including pandas & scipy).


#Credits & inspiration

OpenGreeks is a Rust reimplementation that takes its public API directly from py_vollib / vollib (© 2017 Gammon Capital LLC, MIT-licensed) — function names, argument orders, return-value conventions, and per-day theta / per-1% vega-rho scaling. The vollib code base is the canonical Python reference for these formulas and we recommend it as the parity oracle when validating any port.

Algorithmic credit:

  • Peter Jäckel — "Let's Be Rational" (jaeckel.org). The IV inversion algorithm used by py_lets_be_rational and by future versions of OpenGreeks.
  • W. J. Cody — "Rational Chebyshev approximations for the error function" (Math. Comp., 1969). Used for the normal CDF in both libraries.
  • A. R. Wichura — "Algorithm AS 241" (Appl. Stat., 1988). Inverse normal CDF.
  • John Hull and Espen Haug — textbook formula references; their Black-76 / BS / BSM examples are the validation anchors in bench/bench_parity.py.

#License

MIT — Copyright (c) 2026 Marketcalls / Rajandran R. See LICENSE.

Nueva versión disponible.