How we rebuilt vollib in Rust and shrank a full NIFTY option chain refresh from 9 ms to 0.3 ms, with bit-for-bit identical Greeks.
If you have ever built an Indian options analytics engine in Python, the story usually goes like this. You start with py_vollib or vollib because it is the canonical reference for Black-76, Black-Scholes and Black-Scholes-Merton. The math is correct, the API is clean, and the textbook anchors from Hull and Haug all pass.
Then you scale up.
You move from a single quote to a full NIFTY chain. Then to BANKNIFTY plus FINNIFTY. Then to live tick refresh at 5 Hz, then 10 Hz, then 20 Hz. Somewhere along that path, your CPU starts melting and your event loop starts skipping ticks. Profile in hand, you discover that you are spending almost all of your budget inside py_lets_be_rational and scipy.stats.norm.cdf.
This is the wall that OpenGreeks was built to break. And Now integrated inside openalgo.
What OpenGreeks is
OpenGreeks is a drop-in replacement for vollib and py_vollib where the Black-76, Black-Scholes, and Black-Scholes-Merton pricing and Greeks paths are reimplemented in zero-dependency Rust and exposed to Python through PyO3.
Two design constraints shaped everything else.
First, the public API matches py_vollib exactly. Same function names, same argument order, same scaling conventions (vega multiplied by 0.01, theta divided by 365, rho multiplied by 0.01). Migration is one line:
# before
from py_vollib.black.greeks.analytical import delta, gamma, vega, theta, rho
# after
from opengreeks.black76 import delta, gamma, vega, theta, rho
Second, the math has to be byte-identical where possible. Not “close enough.” Bit-for-bit. A trading desk cannot adopt a new pricing library if it might disagree with the reference on the third decimal of delta. So we validated 29 edge cases against vollib==1.0.7:
GreekMax abs error vs vollibdelta, gamma, vega, theta, rho0.0e+00 (bit-for-bit identical)price1.4e-12 (relative 1.7e-14, around 14 digits)IV (well-conditioned)8.6e-10
The Greeks are not “approximately equal.” They are equal.
The speed numbers
These are measured on a single laptop core against vollib==1.0.7, reproduced in bench/bench_parity.py:

The headline that matters for retail and prop desks running in India: a full NIFTY option chain refresh of roughly 200 options, computing all five Greeks plus implied volatility, drops from about 9 ms in vollib to about 0.3 ms in OpenGreeks. That is the difference between saturating one CPU core at 100 Hz and using about 3 percent of that core for the same workload.
Why Rust, and why now
Pricing math is the textbook case for a native extension. The work is CPU bound, the inputs and outputs are simple floats, and the inner loop is dominated by exp, log, and erf calls. Python pays a per-call interpreter tax for every one of these, and py_vollib was built across multiple pure-Python packages that recurse through several layers before reaching the numerical kernel.
Rust gives us three things that matter here:
- No interpreter overhead on the hot path. Once you cross the PyO3 boundary, the entire pricing loop runs at native speed.
- Zero runtime dependencies in the core. black76_rust and bsm_rust use only the standard library math functions. No scipy, no pandas, no cody_special shared object that fails to load on minimal Python distros.
- Vectorisation through NumPy in one boundary crossing. The *_array variants take a NumPy array in, run the entire chain inside a tight Rust loop, and return a NumPy array out. You pay the PyO3 cost once, not 200 times.
That last point is the one that converts “fast scalar” into “stupid fast on a chain.”
The dependency story
This part rarely gets discussed but it matters a lot in production.
PackageRuntime dependenciespip install vollibpy_lets_be_rational, cody_special, piecewise_rational, simplejson, numpy, pandas, scipypip install opengreeksnumpy
If you have ever shipped a vollib-based service into a slim container or a corporate Python environment, you have probably hit a _testcapi import error from cody_special or a wheel-build failure on scipy on Python 3.13. OpenGreeks ships prebuilt wheels for Linux, macOS, and Windows on every supported CPython and PyPy, and the only thing it asks of the host environment is NumPy.
Using it
Black-76 (futures, NIFTY style)
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 and Black-Scholes-Merton
from opengreeks.black_scholes import black_scholes
from opengreeks.black_scholes_merton import black_scholes_merton
bs = black_scholes('c', 100.0, 90.0, 0.5, 0.01, 0.20) # 12.111581
bsm = black_scholes_merton('p', 100.0, 95.0, 0.5, 0.10, 0.20, 0.05) # 2.4648 (Haug p.4)Whole-chain refresh
For real workloads, use the *_array variants. One PyO3 hop, one Rust loop, NumPy in and out:
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)This is the call path that does a chain refresh in 0.3 ms.
What it does not try to be
OpenGreeks is intentionally narrow at v0.1.0. It does Black-76, Black-Scholes, and Black-Scholes-Merton, the three models that cover 99 percent of vanilla European-style options pricing on Indian and US equity, index, and futures markets. It does not yet ship Heston, SABR, American or Bermudan exercise, or volatility surface fitting.
Those are planned as additional <model>_rust/ crates and opengreeks.<model> submodules, slotted in without breaking the published API. The point of v0.1.0 is to nail the parity story on the most-used pricing kernels first.
Credits
OpenGreeks would not exist without py_vollib and vollib by Gammon Capital LLC (MIT-licensed). Their function names, argument orders, and scaling conventions are reused verbatim, because the entire value proposition of OpenGreeks is that you can pip install opengreeks, change one import line, and keep all the surrounding analytics code that you already wrote against vollib.
The numerical kernels stand on the shoulders of Peter Jaeckel’s “Let’s Be Rational” IV inversion (jaeckel.org), W. J. Cody’s rational Chebyshev approximations for erf (Math. Comp. 1969), A. R. Wichura’s Algorithm AS 241 inverse normal CDF (Appl. Stat. 1988), and the Hull and Haug textbook validation anchors.
Try it
pip install opengreeks
Repo, benchmark scripts, and the full parity report are at:
github.com/marketcalls/opengreeks
If you are running a live options analytics workload on vollib or py_vollib today, the migration is one line and you get back about 95 percent of the CPU you were spending on pricing math. Give it a swap and tell us what your chain refresh latency drops to.