Options Greeks Complete Guide: Delta, Gamma, Theta, Vega
The Greeks are the quantitative language of options risk management. Every options position, no matter how complex, can be decomposed into its sensitivities to the underlying price (delta, gamma), time (theta), volatility (vega), and interest rates (rho). Our Options Calculator computes all of these Greeks directly from Black-Scholes for any strike and expiration, letting you check your own numbers before trusting a custom implementation. Professional options traders think in Greeks rather than in terms of profit and loss, because Greeks reveal the structural risks embedded in a position and guide hedging decisions. This guide covers each Greek in depth, with practical trading applications, hedging strategies, and the relationships between Greeks that experienced traders exploit.Key Takeaways
- Delta measures directional exposure and is the primary hedging parameter. A delta-neutral portfolio has no first-order sensitivity to price changes.
- Gamma measures the rate of delta change and determines how frequently a delta hedge must be rebalanced.
- Theta is the cost of holding optionality. Positive theta positions collect time decay but are short gamma (exposed to large moves).
- Vega quantifies volatility exposure. Most retail traders are unknowingly short vega, which creates hidden risk during volatility spikes.
Delta: Directional Sensitivity
Delta measures how much the option price changes for a $1 move in the underlying. ``python
import numpy as np
import pandas as pd
from scipy.stats import norm
class GreeksAnalyzer:
"""Comprehensive Greek analysis for options positions."""
def __init__(self, S: float, K: float, T: float, r: float, sigma: float, q: float = 0.0):
self.S = S
self.K = K
self.T = T
self.r = r
self.sigma = sigma
self.q = q
def _d1(self):
return (np.log(self.S / self.K) + (self.r - self.q + 0.5 self.sigma2) self.T) / (self.sigma * np.sqrt(self.T))
def _d2(self):
return self._d1() - self.sigma * np.sqrt(self.T)
def delta_analysis(self, option_type: str = "call") -> dict:
"""
Detailed delta analysis with hedging implications.
"""
d1 = self._d1()
if option_type == "call":
delta = np.exp(-self.q self.T) norm.cdf(d1)
else:
delta = np.exp(-self.q self.T) (norm.cdf(d1) - 1)
# Dollar delta: how much portfolio value changes per $1 move
dollar_delta = delta * self.S
# Delta as probability proxy (approximately P(ITM) under risk-neutral measure)
prob_itm = norm.cdf(d1) if option_type == "call" else norm.cdf(-d1)
# Share equivalent: option behaves like owning this many shares
share_equivalent = delta * 100 # Standard contract = 100 shares
return {
"delta": delta,
"dollar_delta": dollar_delta,
"prob_itm_approx": prob_itm,
"share_equivalent": share_equivalent,
"hedge_shares": -share_equivalent, # Shares to delta-hedge
}
def delta_over_price_range(
self, price_range: np.ndarray = None, option_type: str = "call"
) -> pd.DataFrame:
"""Show how delta changes across different underlying prices."""
if price_range is None:
price_range = np.linspace(self.S 0.8, self.S 1.2, 50)
results = []
for s in price_range:
d1 = (np.log(s / self.K) + (self.r - self.q + 0.5 self.sigma2) self.T) / (self.sigma * np.sqrt(self.T))
if option_type == "call":
delta = np.exp(-self.q self.T) norm.cdf(d1)
else:
delta = np.exp(-self.q self.T) (norm.cdf(d1) - 1)
results.append({"price": s, "delta": delta, "moneyness": s / self.K})
return pd.DataFrame(results)
`
Gamma: Delta Sensitivity
Gamma is the second derivative of price with respect to the underlying. It measures how quickly delta changes and determines hedging frequency.
`python
def gamma_analysis(self) -> dict:
"""
Gamma analysis with trading implications.
"""
d1 = self._d1()
gamma = np.exp(-self.q self.T) norm.pdf(d1) / (self.S self.sigma np.sqrt(self.T))
# Dollar gamma: change in dollar delta per $1 move
dollar_gamma = gamma self.S self.S / 100
# Gamma P&L for a given move
def gamma_pnl(price_move_pct: float) -> float:
"""P&L from gamma for a given price move."""
move = self.S * price_move_pct
return 0.5 gamma move*2 100 # Per contract
# Gamma scalping break-even: daily move needed to cover theta
d1_val = self._d1()
theta_daily = -(
self.S np.exp(-self.q self.T) norm.pdf(d1_val) self.sigma
/ (2 * np.sqrt(self.T))
) / 365
# Break-even daily move
if gamma > 0:
breakeven_move = np.sqrt(2 abs(theta_daily) / (gamma 100)) / self.S
else:
breakeven_move = np.inf
return {
"gamma": gamma,
"dollar_gamma": dollar_gamma,
"gamma_pnl_1pct": gamma_pnl(0.01),
"gamma_pnl_2pct": gamma_pnl(0.02),
"gamma_pnl_5pct": gamma_pnl(0.05),
"gamma_scalping_breakeven": breakeven_move,
"interpretation": (
"Maximum near ATM; decays as option moves ITM/OTM. "
f"Need {breakeven_move:.1%} daily move to offset theta."
),
}
`
Theta: Time Decay
Theta quantifies the daily erosion of option value, everything else being equal.
`python
def theta_analysis(self, option_type: str = "call") -> dict:
"""Theta analysis with decay patterns."""
d1, d2 = self._d1(), self._d2()
# Theta per day
common_term = -(
self.S np.exp(-self.q self.T) norm.pdf(d1) self.sigma
/ (2 * np.sqrt(self.T))
)
if option_type == "call":
theta = (
common_term
+ self.q self.S np.exp(-self.q self.T) norm.cdf(d1)
- self.r self.K np.exp(-self.r self.T) norm.cdf(d2)
) / 365
else:
theta = (
common_term
- self.q self.S np.exp(-self.q self.T) norm.cdf(-d1)
+ self.r self.K np.exp(-self.r self.T) norm.cdf(-d2)
) / 365
# Weekly and monthly theta
theta_weekly = theta * 7
theta_monthly = theta * 30
# Theta as percentage of option value
price = self.call_price() if option_type == "call" else self.put_price()
theta_pct = theta / price if price > 0 else 0
return {
"theta_daily": theta,
"theta_weekly": theta_weekly,
"theta_monthly": theta_monthly,
"theta_pct_daily": theta_pct,
"dollar_theta_per_contract": theta * 100,
}
def call_price(self):
d1, d2 = self._d1(), self._d2()
return self.S np.exp(-self.q self.T) norm.cdf(d1) - self.K np.exp(-self.r self.T) norm.cdf(d2)
def put_price(self):
d1, d2 = self._d1(), self._d2()
return self.K np.exp(-self.r self.T) norm.cdf(-d2) - self.S np.exp(-self.q self.T) norm.cdf(-d1)
def theta_over_time(self, option_type: str = "call") -> pd.DataFrame:
"""Show how theta accelerates as expiry approaches."""
days_to_expiry = np.arange(1, int(self.T * 365) + 1)[::-1]
results = []
for dte in days_to_expiry:
T = dte / 365
d1 = (np.log(self.S / self.K) + (self.r - self.q + 0.5 self.sigma2) T) / (self.sigma * np.sqrt(T))
theta_component = -(
self.S np.exp(-self.q T) norm.pdf(d1) self.sigma
/ (2 * np.sqrt(T))
) / 365
results.append({"dte": dte, "theta": theta_component})
return pd.DataFrame(results)
`
Vega: Volatility Sensitivity
Vega measures how the option price changes when implied volatility moves by one percentage point.
`python
def vega_analysis(self) -> dict:
"""Vega analysis with vol trading implications."""
d1 = self._d1()
vega = self.S np.exp(-self.q self.T) norm.pdf(d1) np.sqrt(self.T) / 100
# Vega is highest for ATM options and increases with time
# Vega per contract
dollar_vega = vega * 100
# Vanna: d(delta)/d(sigma) = d(vega)/d(S)
vanna = -np.exp(-self.q self.T) norm.pdf(d1) * self._d2() / (self.sigma)
# Volga: d(vega)/d(sigma) = second-order vol sensitivity
volga = vega self._d1() self._d2() / self.sigma
return {
"vega": vega,
"dollar_vega_per_contract": dollar_vega,
"vanna": vanna,
"volga": volga,
"vol_pnl_1pct_up": dollar_vega,
"vol_pnl_5pct_up": dollar_vega * 5,
}
`
Greek-Based Trading Strategies
Delta-Neutral Trading
`python
class DeltaHedger:
"""
Dynamic delta hedging for options positions.
"""
def __init__(
self,
option_position: dict,
hedge_frequency: str = "daily",
):
self.position = option_position
self.hedge_frequency = hedge_frequency
self.hedge_history = []
def compute_hedge(
self, current_price: float, current_time_to_expiry: float
) -> dict:
"""Compute required hedge adjustment."""
ga = GreeksAnalyzer(
S=current_price,
K=self.position["strike"],
T=current_time_to_expiry,
r=self.position["rate"],
sigma=self.position["vol"],
)
delta_info = ga.delta_analysis(self.position["type"])
gamma_info = ga.gamma_analysis()
current_delta = delta_info["delta"] self.position["contracts"] 100
# Target hedge: opposite sign shares
target_shares = -current_delta
# Current shares held
current_shares = self.hedge_history[-1]["shares"] if self.hedge_history else 0
# Trade needed
trade = target_shares - current_shares
hedge = {
"price": current_price,
"tte": current_time_to_expiry,
"delta": delta_info["delta"],
"gamma": gamma_info["gamma"],
"portfolio_delta": current_delta,
"target_shares": target_shares,
"current_shares": current_shares,
"trade_shares": trade,
"shares": target_shares,
}
self.hedge_history.append(hedge)
return hedge
def hedging_pnl(
self,
price_path: pd.Series,
) -> pd.DataFrame:
"""
Simulate delta hedging P&L along a price path.
"""
results = []
initial_tte = self.position["tte"]
n_days = len(price_path)
shares_held = 0
for i in range(n_days):
price = price_path.iloc[i]
tte = max(initial_tte - i / 252, 1 / 252)
# Compute new hedge
ga = GreeksAnalyzer(
S=price, K=self.position["strike"],
T=tte, r=self.position["rate"],
sigma=self.position["vol"],
)
delta = ga.delta_analysis(self.position["type"])["delta"]
target_shares = -delta self.position["contracts"] 100
trade = target_shares - shares_held
shares_held = target_shares
# Option P&L (mark to market)
option_value = ga.call_price() if self.position["type"] == "call" else ga.put_price()
option_value = self.position["contracts"] 100
results.append({
"date": price_path.index[i],
"price": price,
"delta": delta,
"shares_held": shares_held,
"trade": trade,
"option_value": option_value,
"stock_value": shares_held * price,
})
results_df = pd.DataFrame(results).set_index("date")
# Net portfolio value
results_df["net_value"] = results_df["option_value"] + results_df["stock_value"]
results_df["hedge_pnl"] = results_df["net_value"].diff()
return results_df
`
Gamma Scalping Strategy
`python
def gamma_scalping_backtest(
price_path: pd.Series,
strike: float,
vol: float,
rate: float = 0.05,
initial_tte: float = 30/365,
rehedge_threshold: float = 0.05,
) -> dict:
"""
Gamma scalping: buy options (long gamma) and delta-hedge.
Profit when realized volatility exceeds implied volatility.
"""
n = len(price_path)
shares = 0
option_contracts = 10
total_hedge_pnl = 0
total_hedge_cost = 0
rehedge_count = 0
# Initial option cost
ga_initial = GreeksAnalyzer(
S=price_path.iloc[0], K=strike, T=initial_tte, r=rate, sigma=vol
)
option_cost = ga_initial.call_price() option_contracts 100
prev_delta = 0
daily_pnls = []
for i in range(n):
price = price_path.iloc[i]
tte = max(initial_tte - i / 252, 0.001)
ga = GreeksAnalyzer(S=price, K=strike, T=tte, r=rate, sigma=vol)
current_delta = ga.delta_analysis("call")["delta"] option_contracts 100
delta_change = abs(current_delta - prev_delta) / max(abs(prev_delta), 1)
# Rehedge when delta drifts beyond threshold
if i == 0 or delta_change > rehedge_threshold:
trade = -(current_delta - shares)
hedge_cost = abs(trade price 0.001) # 10 bps
total_hedge_cost += hedge_cost
shares = -int(current_delta)
prev_delta = current_delta
rehedge_count += 1
# Daily P&L from stock position
if i > 0:
stock_pnl = shares * (price - price_path.iloc[i-1])
daily_pnls.append(stock_pnl)
# Final option value
final_price = price_path.iloc[-1]
option_payoff = max(final_price - strike, 0) option_contracts 100
results = {
"option_cost": option_cost,
"option_payoff": option_payoff,
"option_pnl": option_payoff - option_cost,
"hedge_pnl": sum(daily_pnls),
"hedge_costs": total_hedge_cost,
"total_pnl": (option_payoff - option_cost) + sum(daily_pnls) - total_hedge_cost,
"rehedge_count": rehedge_count,
"realized_vol": price_path.pct_change().std() * np.sqrt(252),
"implied_vol": vol,
}
print(f"Gamma Scalping Results:")
print(f" Implied Vol: {vol:.1%}, Realized Vol: {results['realized_vol']:.1%}")
print(f" Option P&L: ${results['option_pnl']:.0f}")
print(f" Hedge P&L: ${results['hedge_pnl']:.0f}")
print(f" Hedge Costs: ${results['hedge_costs']:.0f}")
print(f" Total P&L: ${results['total_pnl']:.0f}")
print(f" Rehedge Count: {results['rehedge_count']}")
return results
`
Greek Risk Limits
`python
def greek_risk_report(
positions: list[dict],
spot: float,
) -> pd.DataFrame:
"""
Generate portfolio-level Greek risk report.
"""
total_greeks = {"delta": 0, "gamma": 0, "theta": 0, "vega": 0, "value": 0}
position_details = []
for pos in positions:
ga = GreeksAnalyzer(
S=spot, K=pos["strike"], T=pos["tte"],
r=pos.get("rate", 0.05), sigma=pos.get("vol", 0.25),
)
delta = ga.delta_analysis(pos["type"])
gamma = ga.gamma_analysis()
theta = ga.theta_analysis(pos["type"])
vega = ga.vega_analysis()
qty = pos["contracts"]
total_greeks["delta"] += delta["delta"] qty 100
total_greeks["gamma"] += gamma["gamma"] qty 100
total_greeks["theta"] += theta["theta_daily"] qty 100
total_greeks["vega"] += vega["vega"] qty 100
position_details.append({
"strike": pos["strike"],
"type": pos["type"],
"qty": qty,
"delta": delta["delta"] qty 100,
"gamma": gamma["gamma"] qty 100,
"theta": theta["dollar_theta_per_contract"] * qty,
"vega": vega["dollar_vega_per_contract"] * qty,
})
details_df = pd.DataFrame(position_details)
print("Portfolio Greek Risk Report:")
print(f" Net Delta: {total_greeks['delta']:+.0f} shares")
print(f" Net Gamma: {total_greeks['gamma']:+.2f}")
print(f" Net Theta: ${total_greeks['theta']:+.0f}/day")
print(f" Net Vega: ${total_greeks['vega']:+.0f}/1% vol")
return details_df
``