edgekit.options
Black-Scholes — European option pricing, the full first-order Greeks, and an implied-volatility solver, with a continuous dividend / carry yield q throughout. Enough to price, hedge, and back out vol without pulling in a derivatives library.
What's inside. The two normal-distribution primitives (norm_cdf, norm_pdf) the formulas are built on, the price itself (bs_price), the Greek bundle (bs_greeks), and a Newton/bisection implied-vol root-finder (implied_vol). Calls and puts are selected with kind="call" / "put"; the dividend yield q also serves as the foreign rate for FX or the convenience-adjusted carry for a future.
The Greeks are reported in raw Black-Scholes units, not the rescaled forms desks often quote:
- delta — change in option value per
$1move in spot. - gamma — change in delta per
$1move in spot (i.e. per$1²). - vega — per
1.00(100 vol-points) change in sigma; divide by 100 for the per-1%-vol figure traders usually quote. - theta — per year; divide by 365 for per-calendar-day decay.
- rho — per
1.00(100 bp × 100) change in the rate.
Distribution primitives#
norm_cdf#
Standard-normal cumulative distribution function — the N(·) in the Black-Scholes price. Vectorised over array input.
norm_cdf(x) -> float | np.ndarrayx— a scalar or array of standardised values.
Returns: the cumulative probability P(Z ≤ x), same shape as the input.
norm_pdf#
Standard-normal probability density function — the n(·) that appears in gamma and vega.
norm_pdf(x) -> float | np.ndarrayx— a scalar or array of standardised values.
Returns: the density at x, same shape as the input.
Pricing#
bs_price#
The Black-Scholes-Merton price of a European call or put with continuous yield q. The core valuation everything else in the module supports.
bs_price(S, K, t, r, sigma, kind="call", q=0.0) -> float| Param | Type | Default | Meaning |
|---|---|---|---|
S | float | — | Spot price of the underlying. |
K | float | — | Strike price. |
t | float | — | Time to expiry in years. |
r | float | — | Continuously-compounded risk-free rate. |
sigma | float | — | Volatility (annualised, e.g. 0.20 for 20%). |
kind | str | "call" | "call" or "put". |
q | float | 0.0 | Continuous dividend / carry yield. |
Returns: a float option premium in the underlying's currency.
import edgekit as ek
ek.options.bs_price(S=100, K=105, t=0.5, r=0.03, sigma=0.20, kind="call")Greeks & implied vol#
bs_greeks#
All five first-order Greeks in one call — delta, gamma, vega, theta, rho — for the same option as bs_price. Read the units callout above before you size a hedge off these.
bs_greeks(S, K, t, r, sigma, kind="call", q=0.0) -> dict| Param | Type | Default | Meaning |
|---|---|---|---|
S | float | — | Spot price. |
K | float | — | Strike. |
t | float | — | Time to expiry (years). |
r | float | — | Risk-free rate. |
sigma | float | — | Volatility. |
kind | str | "call" | "call" or "put". |
q | float | 0.0 | Dividend / carry yield. |
Returns: a dict with keys "delta" (per $1 spot), "gamma" (per $1² spot), "vega" (per 1.00 vol — ÷100 for per-1%), "theta" (per year — ÷365 for per-day), and "rho" (per 1.00 rate).
g = ek.options.bs_greeks(S=100, K=100, t=0.25, r=0.03, sigma=0.20)
g["delta"]
g["vega"] / 100 # per 1% vol move
g["theta"] / 365 # per calendar dayimplied_vol#
Back out the volatility that reprices a European option to an observed price — a Newton root-find with a bisection fallback. The inverse of bs_price: feed it a market premium, get the market's vol view.
implied_vol(price, S, K, t, r, kind="call", q=0.0, tol=1e-6, max_iter=100) -> float| Param | Type | Default | Meaning |
|---|---|---|---|
price | float | — | Observed option premium to match. |
S | float | — | Spot price. |
K | float | — | Strike. |
t | float | — | Time to expiry (years). |
r | float | — | Risk-free rate. |
kind | str | "call" | "call" or "put". |
q | float | 0.0 | Dividend / carry yield. |
tol | float | 1e-6 | Price convergence tolerance. |
max_iter | int | 100 | Maximum solver iterations. |
Returns: a float implied volatility. Returns nan when price is below intrinsic value (no real vol reproduces it).
iv = ek.options.implied_vol(price=6.20, S=100, K=100, t=0.25, r=0.03, kind="call")
# nan if price < max(S - K, 0) discounted (below intrinsic)See also#
- edgekit.timeseries — realized / EWMA / GARCH vol to compare against implied.
- edgekit.risk — tail-risk metrics for an options-overlaid book.