BlackScholesLib
Black-Scholes option pricing implementation
The BlackScholesLib and BlackScholesWad libraries implement the Black-Scholes pricing model for European options. These libraries are the mathematical core of Diffusal's pricing system, calculating option prices and Greeks with high precision using 64.64 fixed-point arithmetic.
Overview
The Black-Scholes libraries provide:
| Library | Purpose |
|---|---|
| BlackScholesLib | Core pricing in 64.64 fixed-point format |
| BlackScholesWad | WAD adapter for external interface |
Architecture
┌─────────────────────────────────────────────┐
│ DiffusalOptionsQuoter │
│ (external interface) │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ BlackScholesWad │
│ (WAD <-> 64.64 conversion) │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ BlackScholesLib │
│ (core 64.64 arithmetic + normalPdf/Cdf) │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ ABDKMath64x64 │
│ (ln, exp, sqrt, mul, div) │
└─────────────────────────────────────────────┘Key Concepts
Black-Scholes Formula
The Black-Scholes model prices European options. For the complete mathematical derivation and formula details, see Black-Scholes Model.
Call Option:
Put Option:
Option Greeks
Greeks measure option sensitivity to various parameters. See Black-Scholes: Greeks for complete formulas.
| Greek | Meaning | Range (Call) |
|---|---|---|
| Delta | Price sensitivity to spot | 0 to 1 |
| Gamma | Delta sensitivity to spot | Always positive |
| Vega | Price sensitivity to volatility | Always positive |
| Theta | Time decay (per year) | Usually negative |
| Rho | Sensitivity to interest rate | Positive (calls) |
Precision Model
Format Conversion
┌─────────────────────────────────────────────────────────────┐
│ Precision Hierarchy │
├─────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────┐ │
│ │ External Interface: WAD (1e18) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 0.50e18 = 50% volatility │ │ │
│ │ │ 3000e18 = $3000 spot price │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └────────────────────────┬──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Internal Math: 64.64 fixed-point (int128) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Upper 64 bits = integer part │ │ │
│ │ │ Lower 64 bits = fractional part │ │ │
│ │ │ Range: roughly +/-9.2e18 with ~18 decimal │ │ │
│ │ │ precision │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Conversion Functions
// WAD → 64.64
function wadTo64x64(uint256 wad) internal pure returns (int128) {
return ((wad << 64) / 1e18).toInt256().toInt128();
}
// 64.64 → WAD (unsigned)
function from64x64ToWad(int128 x) internal pure returns (uint256) {
return (uint256(int256(x)) * 1e18) >> 64;
}
// 64.64 → WAD (signed)
function from64x64ToSignedWad(int128 x) internal pure returns (int256) {
// Handles negative values for Greeks
}Normal Distribution Functions
BlackScholesLib includes inline implementations of the normal distribution functions required for Black-Scholes pricing.
Why 64.64 Fixed-Point?
┌─────────────────────────────────────────────────────────────┐
│ 64.64 Format (int128) │
├─────────────────────────────────────────────────────────────┤
│ [64 bits integer part] [64 bits fractional part] │
│ │
│ - ~18 decimal digits of precision │
│ - Range: approximately +/-9.2 x 10^18 │
│ - Deterministic (no floating-point rounding) │
│ - Efficient compared to arbitrary precision │
└─────────────────────────────────────────────────────────────┘_normalPdf (Internal)
Standard normal probability density function:
Used in Gamma and Theta calculations.
_normalCdf (Internal)
Standard normal cumulative distribution function using the Abramowitz-Stegun approximation (equation 26.2.17):
Where and
Properties:
- Maximum error: < 7.5 × 10^-8
- Deterministic across all nodes
- Clamped for numerical stability at |x| ≥ 6
Internal Constants
// 1/√2π in 64.64 format
int128 private constant INV_SQRT_2PI_64X64 = 7_359_186_146_747_302_912;
// Abramowitz-Stegun coefficients
int128 private constant AS_P_64X64 = 4_273_038_846_047_820_800; // p = 0.2316419
int128 private constant AS_A1_64X64 = 5_891_549_345_779_789_824; // a1 = 0.319381530
int128 private constant AS_A2_64X64 = -6_577_440_832_507_964_416; // a2 = -0.356563782
int128 private constant AS_A3_64X64 = 32_862_467_576_799_068_160; // a3 = 1.781477937
int128 private constant AS_A4_64X64 = -33_596_242_918_879_592_448; // a4 = -1.821255978
int128 private constant AS_A5_64X64 = 24_539_231_939_563_106_304; // a5 = 1.330274429BlackScholesLib (64.64)
Types
Params64x64
Input parameters for pricing:
struct Params64x64 {
int128 spot; // S - Current price of underlying
int128 strike; // K - Strike price
int128 rate; // r - Risk-free interest rate
int128 volatility; // σ - Annualized volatility
int128 timeToExpiry; // T - Time to expiration in years
}Greeks64x64
Option Greeks:
struct Greeks64x64 {
int128 delta; // ∂V/∂S - Rate of change with respect to spot
int128 gamma; // ∂²V/∂S² - Rate of change of delta
int128 vega; // ∂V/∂σ - Sensitivity to volatility
int128 theta; // ∂V/∂t - Time decay (per year)
int128 rho; // ∂V/∂r - Sensitivity to interest rate
}PriceResult64x64
Complete pricing result:
struct PriceResult64x64 {
int128 price; // Option price
int128 d1; // d1 intermediate value
int128 d2; // d2 intermediate value
Greeks64x64 greeks; // Option Greeks
}Core Functions
calculateD1D2
Calculates the d1 and d2 values:
function calculateD1D2(Params64x64 memory p)
internal pure returns (int128 d1, int128 d2)Implementation:
// σ√T
int128 sqrtT = ABDKMath64x64.sqrt(p.timeToExpiry);
int128 volSqrtT = p.volatility.mul(sqrtT);
// ln(S/K)
int128 ratio = p.spot.div(p.strike);
int128 logRatio = ABDKMath64x64.ln(ratio);
// (r + σ²/2)T
int128 volSquaredHalf = p.volatility.mul(p.volatility).mul(HALF);
int128 rateAdjustedT = (p.rate.add(volSquaredHalf)).mul(p.timeToExpiry);
// d1 = [ln(S/K) + (r + σ²/2)T] / (σ√T)
d1 = (logRatio.add(rateAdjustedT)).div(volSqrtT);
// d2 = d1 - σ√T
d2 = d1.sub(volSqrtT);priceCall
Prices a European call option with full Greeks:
function priceCall(Params64x64 memory p)
internal pure returns (PriceResult64x64 memory result)Formula:
pricePut
Prices a European put option with full Greeks:
function pricePut(Params64x64 memory p)
internal pure returns (PriceResult64x64 memory result)Formula:
priceCallOnly / pricePutOnly
Optimized pricing without Greeks calculation:
function priceCallOnly(Params64x64 memory p) internal pure returns (int128 optionPrice)
function pricePutOnly(Params64x64 memory p) internal pure returns (int128 optionPrice)Use case: When only the price is needed (e.g., getOptionQuote()).
BlackScholesWad (WAD Adapter)
Types
ParamsWad
struct ParamsWad {
uint256 spot; // S - Current price (WAD)
uint256 strike; // K - Strike price (WAD)
uint256 rate; // r - Risk-free rate (WAD, 0.05e18 = 5%)
uint256 volatility; // σ - Volatility (WAD, 0.50e18 = 50%)
uint256 timeToExpiry; // T - Time in years (WAD, 0.25e18 = 3 months)
}GreeksWad
struct GreeksWad {
int256 delta; // Delta (WAD, range -1e18 to 1e18)
int256 gamma; // Gamma (WAD)
int256 vega; // Vega (WAD)
int256 theta; // Theta (WAD, per year)
int256 rho; // Rho (WAD)
}PriceResultWad
struct PriceResultWad {
uint256 price; // Option price (WAD)
GreeksWad greeks; // Greeks (WAD)
}Pricing Functions
priceCall / pricePut
Full pricing with Greeks:
function priceCall(ParamsWad memory p)
internal pure returns (PriceResultWad memory result)
function pricePut(ParamsWad memory p)
internal pure returns (PriceResultWad memory result)priceCallOnly / pricePutOnly
Optimized pricing:
function priceCallOnly(ParamsWad memory p) internal pure returns (uint256 priceWad)
function pricePutOnly(ParamsWad memory p) internal pure returns (uint256 priceWad)calculateDiscountFactor
Calculate the discount factor in WAD format:
function calculateDiscountFactor(uint256 rate, uint256 timeToExpiry) internal pure returns (uint256 discount)Parameters:
rate— Risk-free rate in WAD (e.g.,0.05e18for 5%)timeToExpiry— Time to expiry in years, WAD format
Returns: The discount factor in WAD format
Use case: Put-Call Parity verification, forward price calculations, and any scenario requiring the present value factor. Uses the same 64.64 math path as pricing functions for consistency.
Pricing Example
Input Parameters
ParamsWad memory params = ParamsWad({
spot: 3000e18, // $3000
strike: 3200e18, // $3200
rate: 0.05e18, // 5% risk-free rate
volatility: 0.60e18, // 60% annualized volatility
timeToExpiry: 0.25e18 // 3 months (0.25 years)
});Call Option Price
PriceResultWad memory result = BlackScholesWad.priceCall(params);
// result.price ≈ 197.42e18 ($197.42 per contract)
// result.greeks.delta ≈ 0.423e18 (42.3%)
// result.greeks.gamma ≈ 0.0008e18
// result.greeks.vega ≈ 573.2e18
// result.greeks.theta ≈ -385.6e18 (per year)
// result.greeks.rho ≈ 273.4e18Interpretation
| Greek | Value | Meaning |
|---|---|---|
| Delta = 0.423 | 42.3% | $1 spot increase → $0.423 option increase |
| Gamma = 0.0008 | Delta increases by 0.08% per $1 spot move | |
| Vega = 573.2 | 1% vol increase → $5.73 price increase | |
| Theta = -385.6/365 | -$1.06/day | Option loses ~$1.06/day to time decay |
| Rho = 273.4 | 1% rate increase → $2.73 price increase |
Integration Points
Depends On
| Library | Purpose |
|---|---|
| ABDKMath64x64 | 64.64 fixed-point arithmetic (ln, exp, sqrt, mul, div) |
| Constants | WAD, 64.64 constants |
Note: The normal distribution functions (_normalPdf, _normalCdf) are implemented inline within BlackScholesLib using the Abramowitz-Stegun approximation for numerical efficiency.
Used By
| Contract | Purpose |
|---|---|
| DiffusalOptionsQuoter | All option pricing |
| DiffusalLiquidationEngine | Mark prices for liquidation |
| MarginEngine | Stress scenario pricing |
Security Considerations
Numerical Precision
The 64.64 format provides ~18 decimal digits of precision:
- Safe range: Prices and volatilities in practical ranges
- Overflow risk: Very large spot/strike ratios (>10^9)
- Underflow risk: Very small time to expiry or volatility
Numerical Stability Features
The library includes safeguards for numerical stability:
| Feature | Implementation | Purpose |
|---|---|---|
| Minimum time-to-expiry | MIN_TIME_64X64 (1 minute) | Prevents division by zero when σ√T → 0 |
| normalCdf clamping | Values |x| ≥ 6 clamped | Returns 0 or 1 for extreme inputs |
// Minimum time to expiry: 1 minute = 1/(365*24*60) years
int128 private constant MIN_TIME_64X64 = 35_100_917_606_222;
// In normalCdf:
if (x >= sixInt) return ONE; // N(6) ≈ 1
if (x <= neg(sixInt)) return 0; // N(-6) ≈ 0Edge Cases
| Scenario | Handling |
| ------------------------- | ----------------------------------------------- | --- | ---- |
| Zero volatility | Reverts with ZeroVolatility |
| Time to expiry < 1 minute | Reverts with OptionExpired |
| Very deep ITM/OTM | normalCdf clamped to 0 or 1 ( | x | ≥ 6) |
| Negative price result | Clamped to 0 (shouldn't occur for valid inputs) |
Note: The minimum time-to-expiry check prevents numerical instability that occurs when dividing by σ√T as it approaches zero. This threshold (1 minute) was chosen to allow pricing of very short-dated options while maintaining numerical stability.
Input Validation
The libraries assume valid inputs. The DiffusalOptionsQuoter validates:
- Non-zero volatility
- Non-expired options
- Non-zero spot and strike prices
Code Reference
Source:
packages/contracts/src/utils/BlackScholesLib.sol— Core 64.64 implementationpackages/contracts/src/utils/BlackScholesWad.sol— WAD adapter
Key Constants
// From Constants.sol (64.64 format)
int128 constant ONE_64X64 = 0x10000000000000000; // 1.0
int128 constant TWO_64X64 = 0x20000000000000000; // 2.0
int128 constant HALF_64X64 = 0x8000000000000000; // 0.5
// WAD
uint256 constant WAD = 1e18;Related
- Black-Scholes (Math) — Mathematical background
- DiffusalOptionsQuoter — External pricing interface
- Solidity Math — Precision and conversion details