Black-76

Black-76 pricing for European options on a future, the full Greek set, and an iterative implied-volatility solver. Prices dated futures options (a fixed expiry) — not perpetuals.

Contract: Black76.sol

Functions

FunctionGasDescription
call2,552European call price (Black-76)
put2,565European put price (Black-76)
delta1,915First derivative w.r.t. future — returns (Δcall, Δput)
gamma1,704Second derivative w.r.t. future (Γcall = Γput under put-call parity)
theta3,255Time decay, per day — returns (Θcall, Θput)
vega1,659Sensitivity per 1% vol (νcall = νput under put-call parity)
impliedVolatility11,760 / 11,802IV solver via Newton-Raphson (call / put)

npm install defimath-lib

Conventions

  • future, strikeuint128, 18-decimal fixed-point (1e18 = 1.0). future is the forward/futures price of the underlying (what Futures.futurePrice computes).
  • timeToExpuint32, seconds to expiration.
  • volatilityuint64, annualized vol as 18-decimal fixed-point (e.g. 50% → 5e17).
  • rateuint64, annualized risk-free rate as 18-decimal fixed-point (the discount rate only — the future already embeds the cost of carry).
  • All functions are internal pure.

Quick example

solidity
import "defimath-lib/contracts/derivatives/Black76.sol";

uint256 callPx = Black76.call(future, strike, timeToExp, vol, rate);
uint256 putPx  = Black76.put (future, strike, timeToExp, vol, rate);

// delta and theta return (call, put) tuples.
(int128 dC, int128 dP) = Black76.delta(future, strike, timeToExp, vol, rate);

// gamma and vega return a single value (equal for call and put under put-call parity).
uint256 g = Black76.gamma(future, strike, timeToExp, vol, rate);

Important notes

  • Prices options on a future, not spot. In Black-76, d₁ carries no rate term and the whole payoff is discounted by e−r·T, since the future already embeds the cost of carry. Equivalently, Black-76 = e−r·T · Black-Scholes(spot = F, rate = 0). For pricing on a spot underlying, use the Black-Scholes module.
  • Dated futures, not perpetuals. The model prices European options with a fixed expiry τ — i.e. options on a dated future. It is not a perpetual-swap pricer.
  • delta and theta return tuples; gamma and vega return scalars. For delta / theta, a single normal-CDF evaluation is amortized across both call and put. gamma and vega are identical for call and put under put-call parity, so they return a single value.
  • theta is per day, vega per 1% vol change. theta is the price change for a one-day decrease in time to expiration; vega is the change for a 1-percentage-point move in volatility (Δσ = 0.01).
  • impliedVolatility requires market price within no-arb band. For a call the market price must lie within [max(e−r·T(F − K), 0), e−r·TF] (analogous for puts — both legs are discounted), otherwise the solver reverts. Typical convergence is 4–6 Newton-Raphson iterations.

Testing

Hardhat correctness layer. 100 tests across 7 function groups (call, put, delta, gamma, theta, vega, impliedVolatility). Validated against the black-scholes and greeks npm packages via the exact Black-76 = e−r·T · Black-Scholes(F, rate = 0)identity, over 5×5×3×3 strike/time/vol/rate matrices. "Limits and near limit values" sweeps at all four parameter boundaries.

Foundry property-fuzz layer. 16 mathematical properties × 32,000 random runs each = 512,000 random executions per CI run.

CategoryCountWhat they check
Round-trips2IV(callPrice(σ)) ≈ σ, IV(putPrice(σ)) ≈ σ — Newton-Raphson solver round-trips
Monotonicity4call ↑ in future, put ↓ in future, call ↑ in vol, put ↑ in vol
Identities4put-call parity (C − P = e−rT(F − K)), δcall − δput = e−rT, delta sign & parity at expiry, θcall − θput = r·e−rT(F − K)/365
Output bounds6C ≤ e−rTF, P ≤ K·e−rT, δcall ∈ [0, e−rT], δput ∈ [−e−rT, 0], γ ≥ 0, vega ≥ 0

Sources: test/hardhat/Black76.test.mjs · test/foundry/Black76.t.sol