A lightweight Black‑Scholes‑Merton pricing + full Greeks (1st–3rd order) + implied volatility (Newton & Jäckel rational) with selectable numeric precision (f64 or f32) and optional batch/parallel APIs.
-
European vanilla call/put pricing (analytic)
-
Full Greeks: delta, gamma, theta, vega, rho, epsilon, lambda, vanna, charm, veta, vomma, speed, zomma, color, ultima, dual-delta, dual-gamma
-
Two IV solvers:
calc_iv(Modified Corrado-Miller initial guess + Newton)calc_rational_iv(Jäckel “Let’s be rational”, promotes internally to f64 for accuracy)
-
Optional batch helpers (with optional Rayon parallelism via
parallelfeature)
Exactly one precision feature must be active; precision-f64 is the default.
[dependencies]
# Default (f64)
blackscholes = "*"
# Explicit selection
# blackscholes = { version = "*", default-features = false, features = ["precision-f64"] }
# blackscholes = { version = "*", default-features = false, features = ["precision-f32"] }Aliases exposed:
| Alias | Meaning |
|---|---|
Inputs |
Feature-selected generic (InputsGeneric<f64> or <f32>) |
calc_rational_price / calc_rational_iv always return f64 (f32 inputs are promoted internally).
use blackscholes::{Inputs, OptionType, Pricing, GreeksGeneric, ImpliedVolatility};
let inputs = Inputs::new(
OptionType::Call,
100.0,
100.0,
None, // price not needed when pricing
0.05, // risk-free rate
0.01, // dividend yield
0.5, // time to maturity (years)
Some(0.2),
);
let price = inputs.calc_price().unwrap();
let delta = inputs.calc_delta().unwrap();
let all = inputs.calc_all_greeks_generic().unwrap();
// Implied volatility from observed price
let mut iv_inputs = inputs.clone();
iv_inputs.p = Some(price * 1.02); // pretend observed market price
iv_inputs.sigma = None;
let iv = iv_inputs.calc_iv(0.0005).unwrap();use blackscholes::{batch::all_greeks_batch, Inputs, OptionType, Pricing, GreeksGeneric};
let v: Vec<Inputs> = (0..4).map(|i| {
Inputs::new(OptionType::Call, 100.0 + i as f64, 100.0, None, 0.05, 0.0, 0.25, Some(0.2))
}).collect();
let greeks = all_greeks_batch(&v); // Vec<Result<AllGreeksGeneric<_>, _>>Enable parallel processing:
[dependencies]
blackscholes = { version = "*", features = ["parallel"] }Then call batch::all_greeks_batch_par or batch::price_batch_par.
For maximum performance on batch operations, enable the simd feature:
[dependencies]
blackscholes = { version = "*", features = ["simd"] }The SIMD implementation processes multiple options simultaneously using vectorized operations:
- f64 precision: Processes 4 options at once using 256-bit SIMD vectors (~4x faster)
- f32 precision: Processes 8 options at once using 256-bit SIMD vectors (~8x faster)
- All 17 Greeks: Delta, Gamma, Theta, Vega, Rho, Epsilon, Lambda, Vanna, Charm, Veta, Vomma, Speed, Zomma, Color, Ultima, Dual Delta, Dual Gamma
- Fast IV: Jäckel's "Let's Be Rational" method (2-3 iterations, 5-10x faster than Newton-Raphson)
- Portable: Works on x86 (AVX/AVX2), ARM (NEON), and other architectures via
widecrate
Performance: ~0.02 µs per option for complete Greeks calculation (50M options/second)
use blackscholes::simd_batch::{greeks_batch_simd, iv_batch_simd};
use blackscholes::{Inputs, OptionType};
// Prepare batch of options
let inputs: Vec<Inputs> = vec![
Inputs::new(OptionType::Call, 100.0, 105.0, None, 0.2, 0.05, 0.25),
Inputs::new(OptionType::Put, 100.0, 95.0, None, 0.2, 0.05, 0.25),
// ... more options
];
// Calculate all 17 Greeks for the batch (automatic SIMD chunking)
let greeks = greeks_batch_simd(&inputs);
println!("Delta: {}, Gamma: {}, Vega: {}",
greeks[0].delta, greeks[0].gamma, greeks[0].vega);
// Calculate implied volatilities using Jäckel's method
let market_prices = vec![2.5, 1.8, /* ... */];
let ivs = iv_batch_simd(&inputs, &market_prices);
println!("Implied Vol: {}", ivs[0]);For detailed documentation, benchmarks, and optimization tips, see docs/SIMD.md.
For ultimate performance on large batches, combine both features:
[dependencies]
blackscholes = { version = "*", features = ["simd", "parallel"] }use blackscholes::simd_batch::{price_batch_simd_par, greeks_batch_simd_par};
// Process thousands of options with SIMD + multi-threading
let large_batch: Vec<Inputs> = create_large_batch(); // e.g., 10,000+ options
let prices = price_batch_simd_par(&large_batch);
let greeks = greeks_batch_simd_par(&large_batch);The SIMD implementation provides significant performance improvements for batch operations:
- Pricing: 4-8x speedup vs scalar code (depending on precision)
- Greeks: 4-8x speedup vs scalar code
- Implied Volatility: 3-6x speedup (fewer iterations benefit less from vectorization)
- Combined with parallel: Near-linear scaling with CPU cores
Note: SIMD acceleration is most beneficial for batches of 16+ options. For smaller batches, the overhead may outweigh the benefits.
Run the example to see SIMD performance:
cargo run --example simd_example --features simd --release| Method | Notes |
|---|---|
calc_iv(tol) |
Generic Newton; tolerance is in price difference space (scaled by vega) |
calc_rational_iv() |
Jäckel method; fast convergence (2–3 iterations) always returns f64 |
calc_all_greeks_generic() returns AllGreeksGeneric<T> with fields accessible directly (no allocations / maps):
let g = inputs.calc_all_greeks_generic().unwrap();
println!("delta={} gamma={} vega={}", g.delta, g.gamma, g.vega);Typed errors via BlackScholesError:
use blackscholes::{Inputs, OptionType, Pricing, BlackScholesError};
let bad = Inputs::new(OptionType::Call, 100.0, 0.0, None, 0.05, 0.0, 30.0/365.25, Some(0.2));
match bad.calc_price() {
Ok(p) => println!("{}", p),
Err(BlackScholesError::InvalidLogSK) => eprintln!("invalid S/K log"),
Err(e) => eprintln!("error: {}", e),
}Common variants: MissingSigma, MissingPrice, TimeToMaturityZero, InvalidLogSK, ConvergenceFailed.
Micro-benchmarks (indicative, not guarantees) show tens of nanoseconds for single pricing / first-order Greeks on modern x86_64 when using f64. For repeatable numbers run:
cargo bench
and consult project benchmark dashboards (if published).
| Feature | Effect |
|---|---|
precision-f64 (default) |
Use f64 throughout |
precision-f32 |
Use f32 core (rational IV/pricing still returns f64) |
parallel |
Enables Rayon-based parallel batch helpers |
simd |
Enables SIMD-accelerated batch operations (4x/8x speedup) |
Earlier versions exposed a concrete Inputs struct (f64). The crate now provides a generic InputsGeneric<T> internally with Inputs as a stable, feature-selected alias. No code changes are required for typical usage; just ensure only one precision feature is enabled.
MIT – see LICENSE.
| Ecosystem | Link |
|---|---|
| Python | https://pypi.org/project/blackscholes-python/ |
| WASM / npm | https://www.npmjs.com/package/@haydenr4/blackscholes_wasm |
Feel free to open issues or PRs for additional Greeks, performance tweaks, or API improvements.
A library providing Black-Scholes option pricing, Greek calculations, and implied-volatility solver.
A Black-Scholes option pricing, Greek calculation, and implied volatility calculation library.
The library handles both European and American style options for the following option types:
- Vanilla Put/Call
- Binary Put/Call
- Binary OT Range (In/Out)
- Barrier
This library is optimized for both single-option pricing and high-throughput batch processing. We've implemented a comprehensive benchmarking infrastructure to measure and improve performance.
- Single Option Pricing: ~35-40 ns per option
- Rational Pricing Method: ~55-65 ns per option
- Delta Calculation: ~30-35 ns per option
- Gamma Calculation: ~14-15 ns per option
- Batch Processing: Scales linearly up to large batch sizes
- All Greeks Calculation: ~2 ms per 1000 options
The library includes a comprehensive benchmarking system for performance tracking:
- Interactive Charts: Professional benchmark visualizations on GitHub Pages
- Automated Regression Detection: CI-integrated tests that fail on performance regressions (>10% threshold)
- Historical Tracking: Continuous monitoring of performance trends over time
- Pull Request Comments: Automatic performance comparison comments on PRs
View live benchmark results at: https://przemyslawolszewski.github.io/bs-rs/
As of the upcoming release the core engine is generic over the float type. You can choose precision via Cargo features:
[dependencies]
blackscholes = { version = "*", default-features = false, features = ["precision-f64"] }
# or for 32-bit floats
blackscholes = { version = "*", default-features = false, features = ["precision-f32"] }Exactly one of precision-f64 (default) or precision-f32 must be enabled. They are mutually exclusive.
Public helpers:
Inputs(legacy, concrete f64) – deprecated and will be removed in a future major version.InputsSelected– feature‑selected alias of the genericInputsGeneric<T>.InputsF64/InputsF32– explicit aliases if you need to write code that depends on precision at compile time.
calc_rational_iv() always computes using 64-bit precision under the hood; the f32 path promotes to f64 internally and casts back. The standard Newton IV solver (calc_iv) runs natively in the selected precision.
use blackscholes::{InputsSelected as Inputs, OptionType, Pricing, Greeks, ImpliedVolatility};
// Basic option pricing
let inputs = Inputs::new(
OptionType::Call, // Call option
100.0, // Spot price
100.0, // Strike price
None, // Option price (not needed for pricing)
0.05, // Risk-free rate
0.01, // Dividend yield
1.0, // Time to maturity (in years)
Some(0.2), // Volatility
);
// Calculate option price
let price = inputs.calc_price().unwrap();
println!("Option price: {}", price);
// Calculate option Greeks
let delta = inputs.calc_delta().unwrap();
let gamma = inputs.calc_gamma().unwrap();
let theta = inputs.calc_theta().unwrap();
let vega = inputs.calc_vega().unwrap();
let rho = inputs.calc_rho().unwrap();
println!("Delta: {}, Gamma: {}, Vega: {}", delta, gamma, vega);
// Calculate implied volatility from price
let mut iv_inputs = Inputs::new(
OptionType::Call,
100.0,
100.0,
Some(10.0), // Option price
0.05,
0.01,
1.0,
None, // Volatility is what we're solving for
);
let iv = iv_inputs.calc_iv(0.0001).unwrap();
println!("Implied volatility: {}", iv);This project is licensed under the MIT License - see the LICENSE file for details.
This library provides a simple, lightweight, and efficient (though not heavily optimized) implementation of the Black-Scholes-Merton model for pricing European options.
Includes all first, second, and third order Greeks.
Implements both:
- calc_iv() in the ImpliedVolatility trait which uses Modified Corrado-Miller by Piotr Płuciennik (2007) for the initial volatility guess and the Newton Raphson algorithm to solve for the implied volatility.
- calc_rational_iv() in the ImpliedVolatility trait which uses "Let's be rational" method from "Let's be rational" (2016) by Peter Jackel. Utilizing Jackel's C++ implementation to get convergence within 2 iterations with 64-bit floating point accuracy.
View the docs for usage and examples.
Other packages available:
Python: Pypi
WASM: npm
calc_all_greeks() returns a typed struct AllGreeks (not a HashMap) for zero-allocation and faster access to fields like delta, gamma, etc.
Inputs (the concrete f64 struct) is deprecated. Migrate to InputsSelected (preferred) or InputsF64 / InputsF32. The concrete struct will be removed after a transition period to reduce maintenance duplication.
This repo enables LTO, codegen-units=1, opt-level=3, panic=abort and target-cpu=native (via .cargo/config.toml) for maximum throughput. Use cargo bench to run Criterion benchmarks.
The library uses a typed error enum BlackScholesError instead of string errors. This makes it easy to handle specific failure modes:
use blackscholes::{Inputs, OptionType, Pricing, BlackScholesError};
let inputs = Inputs::new(OptionType::Call, 100.0, 0.0, None, 0.05, 0.0, 30.0/365.25, Some(0.2));
match inputs.calc_price() {
Ok(p) => println!("{}", p),
Err(BlackScholesError::MissingSigma) => eprintln!("volatility is required"),
Err(e) => eprintln!("error: {}", e),
}Comprehensive documentation is available:
- API Documentation - Complete API reference with examples
- SIMD Guide - Detailed guide to SIMD features, performance, and optimization
- Benchmarking Guide - How to run and interpret benchmarks
- SIMD acceleration: See SIMD section above and docs/SIMD.md
- Parallel processing: Enable
parallelfeature for rayon-based batch processing - Precision selection: Choose
precision-f64(default) orprecision-f32features - Greeks reference: All 17 Greeks documented in API docs and docs/SIMD.md
- Examples: See
examples/directory for complete working examples
Common variants: MissingSigma, MissingPrice, TimeToMaturityZero, InvalidLogSK, ConvergenceFailed.