Option Greeks in Python

Delta, Gamma, Theta, Vega. Step by step, with code, plots, and an interactive view of how gamma changes near expiry.

Newsletter

Get exclusive models, prompts and code

One short email when there's something new. finance models, code walkthroughs, agentic prompts. No spam.

Built with
Anthropic Claude Python R ChatGPT GitHub Jupyter Excel PowerPoint VS Code SQL MATLAB

1. What the Greeks tell you

The Greeks are sensitivities. They answer simple questions about an option price.

All four come from the same Black and Scholes formula. Once you have the helper variables $d_1$ and $d_2$, every Greek is one short line of code.


2. Setup

You only need NumPy and SciPy.

import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt

And one set of inputs we will reuse for every example.

S = 100.0      # Spot price
K = 100.0      # Strike
T = 1.0        # Time to expiry, in years
r = 0.05       # Risk-free rate
sigma = 0.25   # Volatility

3. The two helpers: $d_1$ and $d_2$

$d_1$ and $d_2$ show up in every Greek. Compute them once and reuse.

$$d_1 = \frac{\ln(S/K) + (r + \tfrac{\sigma^2}{2})\,T}{\sigma\sqrt{T}}, \quad d_2 = d_1 - \sigma\sqrt{T}$$

def d1(S, K, T, r, sigma):
    return (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

def d2(S, K, T, r, sigma):
    return d1(S, K, T, r, sigma) - sigma * np.sqrt(T)

4. Delta

Delta measures the change in option price for a one-dollar change in the stock. For a call, it sits between 0 and 1. For a put, between $-1$ and 0.

$$\Delta_{\text{call}} = N(d_1), \qquad \Delta_{\text{put}} = N(d_1) - 1$$

Reading: a call delta of 0.60 means the option will move about 0.60 dollars if the stock moves one dollar. A put delta of $-0.40$ means the put will move about 0.40 dollars against you if the stock moves up by one dollar.

def delta(S, K, T, r, sigma, option_type="call"):
    d_1 = d1(S, K, T, r, sigma)
    if option_type == "call":
        return norm.cdf(d_1)
    return norm.cdf(d_1) - 1.0

print(f"Call delta: {delta(S, K, T, r, sigma, 'call'):.4f}")
print(f"Put delta : {delta(S, K, T, r, sigma, 'put'):.4f}")

5. Gamma

Gamma measures how fast delta moves. It is identical for calls and puts.

$$\Gamma = \frac{\varphi(d_1)}{S\,\sigma\,\sqrt{T}}$$

where $\varphi$ is the standard normal density. Gamma is largest when the option is at the money and small once the option is deep in or deep out of the money. We will see in section 9 that gamma also explodes as $T$ approaches zero. For the full 3D picture against moneyness, vol and time, see the gamma surface notebook.

def gamma(S, K, T, r, sigma):
    d_1 = d1(S, K, T, r, sigma)
    return norm.pdf(d_1) / (S * sigma * np.sqrt(T))

print(f"Gamma: {gamma(S, K, T, r, sigma):.4f}")

6. Theta

Theta measures the value the option loses with the passage of time, holding everything else constant. It is usually negative for both calls and puts because options are decaying assets.

$$\Theta_{\text{call}} = -\frac{S\,\varphi(d_1)\,\sigma}{2\sqrt{T}} - r\,K\,e^{-rT}\,N(d_2)$$

$$\Theta_{\text{put}} = -\frac{S\,\varphi(d_1)\,\sigma}{2\sqrt{T}} + r\,K\,e^{-rT}\,N(-d_2)$$

The values above are theta per year. To get the daily theta, divide by 365.

def theta(S, K, T, r, sigma, option_type="call"):
    d_1 = d1(S, K, T, r, sigma)
    d_2 = d2(S, K, T, r, sigma)
    first = -S * norm.pdf(d_1) * sigma / (2.0 * np.sqrt(T))
    if option_type == "call":
        return first - r * K * np.exp(-r * T) * norm.cdf(d_2)
    return first + r * K * np.exp(-r * T) * norm.cdf(-d_2)

print(f"Call theta (per year): {theta(S, K, T, r, sigma, 'call'):.4f}")
print(f"Call theta (per day) : {theta(S, K, T, r, sigma, 'call') / 365:.4f}")

7. Vega

Vega measures the change in option price for a 1 percentage point change in implied volatility. It is the same for calls and puts and is always positive.

$$\mathcal{V} = S\,\sqrt{T}\,\varphi(d_1)$$

The expression above gives vega per unit of $\sigma$. Most desks quote vega per 1 percentage point, so divide by 100.

def vega(S, K, T, r, sigma):
    d_1 = d1(S, K, T, r, sigma)
    return S * np.sqrt(T) * norm.pdf(d_1) / 100.0

print(f"Vega (per 1pp of vol): {vega(S, K, T, r, sigma):.4f}")

8. Visualizing all four Greeks

The clearest way to build intuition is to plot each Greek against the spot price for a fixed time to expiry. The chart below shows how each one behaves around the strike.

spots = np.linspace(60, 140, 200)
deltas  = [delta(s, K, T, r, sigma, "call") for s in spots]
gammas  = [gamma(s, K, T, r, sigma) for s in spots]
thetas  = [theta(s, K, T, r, sigma, "call") / 365 for s in spots]  # per day
vegas   = [vega(s, K, T, r, sigma) for s in spots]

fig, axes = plt.subplots(2, 2, figsize=(11, 7))

axes[0, 0].plot(spots, deltas);  axes[0, 0].set_title("Delta (call)")
axes[0, 1].plot(spots, gammas);  axes[0, 1].set_title("Gamma")
axes[1, 0].plot(spots, thetas);  axes[1, 0].set_title("Theta per day (call)")
axes[1, 1].plot(spots, vegas);   axes[1, 1].set_title("Vega per 1pp")

for ax in axes.flat:
    ax.axvline(K, color="red", linestyle="--", alpha=0.5)
    ax.set_xlabel("Spot price")

plt.tight_layout()
plt.show()

Greeks at $T = 1.0$ year


9. Dynamic gamma: what happens as $T$ goes to zero

This is where most of the intuition about expiry pinning comes from. Drag the slider below and watch the gamma curve. When time to expiry is large, gamma is a smooth, low hill across a wide range of spot prices. As expiry approaches, gamma narrows into a tall spike right at the strike.

The reason is simple. Far from expiry, every cent of stock movement makes the option a little more or a little less likely to finish in the money. Near expiry, only the last few cents around the strike still flip the outcome, so the option price reacts violently in that tiny zone and barely at all everywhere else.

T = 0.50

Strike fixed at $K = 100$. Volatility 25%. Risk-free rate 5%. Move the slider toward 0 to see the gamma spike.

The same effect, written in Python:

spots = np.linspace(60, 140, 400)
maturities = [1.0, 0.5, 0.25, 0.10, 0.02]

plt.figure(figsize=(9, 5))
for T_i in maturities:
    g = [gamma(s, K, T_i, r, sigma) for s in spots]
    plt.plot(spots, g, label=f"T = {T_i:.2f}")

plt.axvline(K, color="red", linestyle="--", alpha=0.5)
plt.title("Gamma vs Spot Price for several expiries")
plt.xlabel("Spot price")
plt.ylabel("Gamma")
plt.legend()
plt.show()

10. Quick recap

Pair this notebook with the interactive Greeks calculator to play with all five inputs at once, or jump to the gamma surface walkthrough for the 3D picture and the higher-order Greeks (Zomma, Vanna).


Free download

Get the full Greeks notebook + 4 higher-order Greeks

Everything on this page packaged as a runnable Jupyter notebook, plus four higher-order Greeks not covered above. Join the newsletter (free) and the download is yours.

  • The full notebook — every formula, every plot, every Python cell from this page, ready to run.
  • Vanna ($\partial \Delta / \partial \sigma$) — how delta moves when implied vol moves. The Greek that explains OTM call buyers' P&L on a vol rally.
  • Charm ($\partial \Delta / \partial T$) — delta decay over time. Why your hedge needs rebalancing even when the spot doesn't move.
  • Vomma ($\partial \mathrm{Vega} / \partial \sigma$) — vega's sensitivity to vol. The "convexity of vega" — why short-vol positions blow up disproportionately on vol spikes.
  • Color ($\partial \Gamma / \partial T$) — gamma's decay over time. Where on the surface gamma is melting fastest.
  • Interactive Plotly versions — rotate, zoom, and hover-for-values across all the Greek surfaces.

Write your name and a valid email to unlock the download.

option_greeks_extras.ipynb

Enter your name & email above to unlock

Download

Free. One short email when there's something new. No spam, unsubscribe in one click.

David Arias, CFA
Written and Modelled by

David Arias, CFA

Licensed portfolio manager with 4+ years of experience, specializing in emerging markets private debt, derivatives, and quantitative finance.

Let's Connect