Options

Black-Scholes pricing for European options, the full Greek set, and an iterative implied-volatility solver.

Contract: Options.sol

Functions

FunctionGasDescription
callOptionPrice2,729European call price (Black-Scholes)
putOptionPrice2,739European put price (Black-Scholes)
delta1,724First derivative w.r.t. spot — returns (Δcall, Δput)
gamma1,496Second derivative w.r.t. spot (Γcall = Γput under put-call parity)
theta3,290Time decay, per day — returns (Θcall, Θput)
vega1,436Sensitivity per 1% vol (νcall = νput under put-call parity)
impliedVolatility~12,370IV solver via Newton-Raphson

npm install defimath-lib

Conventions

  • spot, strikeuint128, 18-decimal fixed-point (1e18 = 1.0).
  • 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.
  • All functions are internal pure.

Quick example

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

uint256 callPx = DeFiMathOptions.callOptionPrice(spot, strike, timeToExp, vol, rate);
uint256 putPx  = DeFiMathOptions.putOptionPrice (spot, strike, timeToExp, vol, rate);

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

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

Important notes

  • 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 — ~halves gas vs. two separate calls. gamma and vega are identical for call and put under put-call parity, so they return a single value.
  • theta is per day. The result is the price change for a one-day decrease in time to expiration, not per-second.
  • vega is per 1% vol change. The result is the price change for a 1-percentage-point change in volatility (Δσ = 0.01).
  • impliedVolatility requires market price within no-arb band. The passed market price must lie within [max(S − K·e−r·T, 0), S] for calls (analogous for puts), 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 greeksnpm packages over 5×5×3×3 strike/time/vol/rate matrices. "Limits and near limit values" sweeps at all four parameter boundaries (low/high strike, short/long expiry, near-zero/near-max vol and rate).

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

CategoryCountWhat they check
Round-trips2IV(callPrice(σ)) ≈ σ, IV(putPrice(σ)) ≈ σ — Newton-Raphson solver round-trips
Monotonicity4call ↑ in spot, put ↓ in spot, call ↑ in vol, put ↑ in vol
Identities3put-call parity (C − P = S − K·e−rT), δcall − δput = 1, θcall − θput = −r·K·e−rT/365
Output bounds6C ≤ S, P ≤ K·e−rT, δcall ∈ [0, 1], δput ∈ [-1, 0], γ ≥ 0, vega ≥ 0

Sources: test/Options.test.mjs · test/foundry/Options.t.sol