Options & the Greeks
An option is the right — not the obligation — to trade an asset at a fixed price. That asymmetry turns it into a bet on where and how much a price will move, priced by no-arbitrage. This chapter builds the instrument from its payoff up: intrinsic versus time value, put-call parity from a replication argument, Black-Scholes as the risk-neutral discounted expectation, the Greeks as its partial derivatives, and implied volatility as the number you back out by inverting the formula. Every quantity here maps to a call in ek.options.
Calls, puts, and payoff#
A call pays off when the underlying finishes above the strike; a put pays off below it. At expiry the value depends only on the terminal spot and the strike :
The is the whole story: your loss is floored at the premium you paid, while the upside is open (call) or bounded only by (put). That kinked, convex payoff is what you cannot replicate with the linear P&L of a spot position — it is why options exist.

Intrinsic value vs time value#
Before expiry an option is worth more than its payoff-if-exercised-now. Split the premium in two:
Intrinsic value is what you would collect exercising immediately — zero for an out-of-the-money option. Time value is everything left: the premium the market charges for the chance that the option moves further into the money before expiry. It is largest at-the-money and decays to zero as — the erosion that (below) measures.
Scenario. Acme has climbed to $108 and your $100 call now trades at $9.50. Its intrinsic value is ; the remaining is time value — the market's charge for the chance the stock climbs even further before expiry. Hold to the last day with Acme still at $108 and that $1.50 has bled away to nothing: you are left with the $8 intrinsic and not a cent more. The time value is what you are really buying and selling when you trade an option early.
Put-call parity#
Before any pricing model, a pure no-arbitrage relation ties calls and puts together. Consider two portfolios held to expiry :
A: long one call, short one put, both struck at . At expiry it is worth in every state of the world.
B: long one share (reinvesting the continuous carry ) plus a loan whose face value is . At expiry it is worth too.
Two portfolios with identical payoffs in every state must have identical value today, or you could buy the cheap one, sell the dear one, and pocket a riskless profit. Discounting each leg to the present — the share by its carry, the loan by the rate — gives put-call parity:
This holds for European options independent of any model — it never assumes Black-Scholes. It is the first thing to sanity-check a pricer against: price a call and a put at the same strike and confirm the difference equals the forward minus the discounted strike.
import numpy as np
import edgekit as ek
S, K, t, r, q, sigma = 100.0, 100.0, 0.5, 0.04, 0.01, 0.20
C = ek.options.bs_price(S, K, t, r, sigma, kind="call", q=q)
P = ek.options.bs_price(S, K, t, r, sigma, kind="put", q=q)
lhs = C - P
rhs = S * np.exp(-q * t) - K * np.exp(-r * t)
print(round(lhs, 8), round(rhs, 8)) # equal to machine precisionThe Black-Scholes price#
Black-Scholes-Merton assumes the underlying follows geometric Brownian motion with constant volatility , and prices the option as the discounted expectation of its payoff under the risk-neutral measure (where the drift is the carry-adjusted rate, not the real-world return). The closed form for a call is:
where is the standard-normal CDF and
Read the call intuitively: is (roughly) the risk-neutral probability of finishing in the money, so is the expected cost you pay for the stock, and is the discounted expected value of the stock you receive, conditional on exercise. The single free input is : everything else is observable. The same formula prices equity ( = dividend yield), FX ( = foreign rate) and futures options (, the Black-76 case).
import edgekit as ek
# 6-month at-the-money call, 4% rate, 1% carry, 20% vol
px = ek.options.bs_price(S=100, K=100, t=0.5, r=0.04, sigma=0.20, kind="call", q=0.01)
print(round(px, 4)) # ~ 5.9 (all time value: ATM has zero intrinsic)
# deep in-the-money call -> price dominated by intrinsic (S - K discounted)
print(round(ek.options.bs_price(140, 100, 0.5, 0.04, 0.20, kind="call"), 4))ek.options implements as via math.erf, so every function is float-in / float-out but also broadcasts elementwise over numpy arrays — price a whole strike ladder in one call by passing an array of K.The Greeks#
The Greeks are the partial derivatives of the price with respect to each input — the sensitivities you hedge and risk-manage against. ek.options.bs_greeks(...) returns all five in one dict: delta, gamma, vega, theta, rho.
Delta — sensitivity to spot#
, the change in option value per move in spot. For a call it is exactly:
It doubles as the hedge ratio (hold shares to neutralise spot risk) and as a rough in-the-money probability. Deep ITM calls approach (they behave like stock); far OTM approach .
Gamma — curvature#
, how fast delta itself moves. It is the same for calls and puts and is always positive for a long option:
Gamma is the convexity you pay theta for. It peaks at-the-money and near expiry, and it is the term that makes a delta-hedged long-option position profit from large moves — the subject of the next chapter.
Vega — sensitivity to volatility#
, the change in value per unit of volatility. Same for calls and puts, always positive for a long option:
bs_greeks returns vega per 1.00 (=100 vol-points) of . To get the more familiar “dollars per 1% vol move”, divide by 100. Likewise theta is per year — divide by 365 for per-calendar-day decay — and rho is per 1.00 of rate (÷100 for per-1%). Getting these unit conventions wrong is the single most common Greeks bug.Theta — time decay#
(per year), the erosion of time value as expiry approaches. It is negative for a long option — you bleed premium every day nothing happens:
Rho — sensitivity to rates#
, usually the smallest Greek for short-dated options: and .

import edgekit as ek
g = ek.options.bs_greeks(S=100, K=100, t=0.5, r=0.04, sigma=0.20, kind="call", q=0.01)
print(round(g["delta"], 4)) # ~ 0.55 (slightly above 0.5 for ATM call)
print(round(g["gamma"], 5))
print(round(g["vega"] / 100, 4)) # $ per +1% vol (divide the per-1.00 vega by 100)
print(round(g["theta"] / 365, 4)) # $ per calendar day of decay
print(round(g["rho"] / 100, 4)) # $ per +1% rateImplied volatility#
Black-Scholes takes and returns a price. In the market you observe the price and want the volatility that reproduces it — the implied volatility. Because the price is strictly increasing in (vega > 0), there is a unique solution, found by inverting the formula numerically:
ek.options.implied_vol does this with Newton-Raphson on vega, falling back to bisection when a step leaves the no-arbitrage bracket. It returns nan when no positive vol can reproduce the price — chiefly a quote below intrinsic value, or a non-positive price/time.
import edgekit as ek
# price a call at a known vol, then recover that vol from the price
px = ek.options.bs_price(100, 100, 0.5, 0.04, 0.30, kind="call")
iv = ek.options.implied_vol(px, 100, 100, 0.5, 0.04, kind="call")
print(round(iv, 6)) # -> 0.30, the round-trip is exact
# a quote below intrinsic has no solution
print(ek.options.implied_vol(0.01, 140, 100, 0.5, 0.04, kind="call")) # nanek.options.implied_vol(2.85, 100, 100, 0.083, 0.04, kind="call") returns roughly . The market is pricing 25% implied vol, not your 20% — it expects the earnings print to shake the stock 25% harder (annualised) than its recent history did. That 5-vol-point gap is the earnings risk premium, made explicit. If you still believe 20% is the right number, the call is expensive and you are on the wrong side of the bet — which is precisely the comparison the next chapter turns into a trade.Implied vol is the market's forward-looking price of risk, and reading it across strikes and maturities is where the constant- assumption of Black-Scholes visibly breaks — the vol smile. That is the door into volatility trading.
Next: Volatility trading — realized versus implied vol, the variance risk premium, the smile, and delta-hedging a long-gamma book.

