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
| Function | Gas | Description |
|---|---|---|
| call | 2,552 | European call price (Black-76) |
| put | 2,565 | European put price (Black-76) |
| delta | 1,915 | First derivative w.r.t. future — returns (Δcall, Δput) |
| gamma | 1,704 | Second derivative w.r.t. future (Γcall = Γput under put-call parity) |
| theta | 3,255 | Time decay, per day — returns (Θcall, Θput) |
| vega | 1,659 | Sensitivity per 1% vol (νcall = νput under put-call parity) |
| impliedVolatility | 11,760 / 11,802 | IV solver via Newton-Raphson (call / put) |
npm install defimath-lib
future, strike — uint128, 18-decimal fixed-point (1e18 = 1.0). future is the forward/futures price of the underlying (what Futures.futurePrice computes).timeToExp — uint32, seconds to expiration.volatility — uint64, annualized vol as 18-decimal fixed-point (e.g. 50% → 5e17).rate — uint64, annualized risk-free rate as 18-decimal fixed-point (the discount rate only — the future already embeds the cost of carry).internal pure.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);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.τ — 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.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.
| Category | Count | What they check |
|---|---|---|
| Round-trips | 2 | IV(callPrice(σ)) ≈ σ, IV(putPrice(σ)) ≈ σ — Newton-Raphson solver round-trips |
| Monotonicity | 4 | call ↑ in future, put ↓ in future, call ↑ in vol, put ↑ in vol |
| Identities | 4 | put-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 bounds | 6 | C ≤ 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