Two more ways the price moves
Delta, gamma and vega cover the stock and its volatility. Two inputs of the Black–Scholes formula are left: the time to expiry and the interest rate. Theta is the change of an option's value as time passes (the formula's t shrinks every day), and rho is the change when interest rates move. Theta is usually the one a trader feels daily: a long option is a wasting asset, and the premium it loses each day is the price of owning the convexity.
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def norm_pdf(x):
return math.exp(-0.5 * x * x) / math.sqrt(2 * math.pi)
def d1(spot, strike, rate, sigma, t):
return (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
def d2(spot, strike, rate, sigma, t):
return d1(spot, strike, rate, sigma, t) - sigma * math.sqrt(t)
def call_theta(spot, strike, rate, sigma, t):
decay = -spot * norm_pdf(d1(spot, strike, rate, sigma, t)) * sigma / (2 * math.sqrt(t))
carry = -rate * strike * math.exp(-rate * t) * norm_cdf(d2(spot, strike, rate, sigma, t))
return decay + carry
theta = call_theta(100, 100, 0.05, 0.2, 1)
print(round(theta, 4)) # -6.414 per year
print(round(theta / 365, 4)) # -0.0176 per calendar dayTheta: why time hurts
The first term of theta, -S * pdf(d1) * sigma / (2 * sqrt(T)), is the decay of the *option value*: with less time there is less chance of a big move. The second, -r * K * exp(-rT) * N(d2), is the interest on the strike you do not have to pay yet, which the call buyer earns. Both are negative for a call, so theta is, and the loss is not steady: it speeds up as expiry nears, fastest for options at the money. Traders quote it per calendar day, theta / 365.
# Theta per day for the same option as expiry approaches: the decay speeds up
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def norm_pdf(x):
return math.exp(-0.5 * x * x) / math.sqrt(2 * math.pi)
def call_theta(spot, strike, rate, sigma, t):
d_1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
d_2 = d_1 - sigma * math.sqrt(t)
return -spot * norm_pdf(d_1) * sigma / (2 * math.sqrt(t)) - rate * strike * math.exp(-rate * t) * norm_cdf(d_2)
for days in (360, 90, 30, 7):
per_day = call_theta(100, 100, 0.05, 0.2, days / 365) / 365
print(days, "days left:", round(per_day, 4)) # 360: -0.0177, 90: -0.0288, 30: -0.045, 7: -0.0858Rho: the rate
Rho is K * T * exp(-rT) * N(d2): positive for a call, because a higher rate lowers the present value of the strike you will pay. A put has the opposite sign. It is the smallest Greek for short-dated options and matters for long ones (LEAPS, long-dated warrants). Quote it for one percentage point of rate, rho / 100, just as vega is quoted per volatility point.
# Rho of the one-year call at several rates: it grows with the rate and with the time left
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def call_rho(spot, strike, rate, sigma, t):
d_1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return strike * t * math.exp(-rate * t) * norm_cdf(d_1 - sigma * math.sqrt(t))
for rate in (0.0, 0.05, 0.10):
print("rate", rate, "rho per point:", round(call_rho(100, 100, rate, 0.2, 1) / 100, 4)) # 0.0: 0.4602, 0.05: 0.5323, 0.1: 0.5931
print("two years, 5%:", round(call_rho(100, 100, 0.05, 0.2, 2) / 100, 4)) # 1.0568# Check theta and rho by bumping the inputs: they are slopes of the price
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def bs_call(spot, strike, rate, sigma, t):
d_1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return spot * norm_cdf(d_1) - strike * math.exp(-rate * t) * norm_cdf(d_1 - sigma * math.sqrt(t))
h = 1e-5
theta = -(bs_call(100, 100, 0.05, 0.2, 1 + h) - bs_call(100, 100, 0.05, 0.2, 1 - h)) / (2 * h)
rho = (bs_call(100, 100, 0.05 + h, 0.2, 1) - bs_call(100, 100, 0.05 - h, 0.2, 1)) / (2 * h)
print(round(theta, 4), round(rho, 4)) # -6.414 53.2325theta ~ -0.5 * sigma^2 * S^2 * gamma (at zero rate). That is why a trader who is long gamma is, in effect, betting that the stock will move more than the volatility already priced in.- Theta is the value lost as time passes: negative for a long call, faster near expiry and at the money; quote it as
theta / 365per day. - Rho is the sensitivity to the interest rate: positive for calls, negative for puts, largest for long-dated options; quote it as
rho / 100per point. - Both are slopes of the price, so a small bump of
torratechecks any formula.
Next up: implied volatility, running the formula backwards to read the market's volatility from an option price.
Lab · what's graded
- Price the passing of time with
call_theta25 pts - Quote the daily decay with
theta_per_day25 pts - Measure the sensitivity to interest rates with
call_rho25 pts - Quote rho per percentage point with
rho_per_point25 pts