Portfolio-level Greeks aggregation
Chapter 119 computed Greeks per option. A multi-leg position (chapter 60) or a book with several open options needs its Greeks aggregated — the whole point of using options mathematically rather than just reading individual position P&L.
def aggregate_portfolio_greeks(positions: list[dict]) -> dict:
"""
positions: [{quantity, lot_size, delta, gamma, theta, vega, rho}, ...]
quantity: positive for long, negative for short.
"""
totals = {"delta": 0.0, "gamma": 0.0, "theta": 0.0, "vega": 0.0, "rho": 0.0}
for pos in positions:
multiplier = pos["quantity"] * pos["lot_size"]
for greek in totals:
totals[greek] += pos[greek] * multiplier
return totals
Building the position list from live positions + freshly computed Greeks
def build_position_greeks(kite, positions: list[dict], nfo_df: pd.DataFrame, spot: float, r: float) -> list[dict]:
enriched = []
for pos in positions:
row = nfo_df[nfo_df.tradingsymbol == pos["tradingsymbol"]].iloc[0]
T = get_time_to_expiry(row["expiry"])
iv = implied_volatility(pos["last_price"], spot, row["strike"], T, r, row["instrument_type"]) or 0.20
greeks = compute_greeks(spot, row["strike"], T, r, iv, row["instrument_type"])
enriched.append({
"tradingsymbol": pos["tradingsymbol"], "quantity": pos["quantity"], "lot_size": row["lot_size"],
**greeks,
})
return enriched
Reading the aggregated numbers
portfolio = build_position_greeks(kite, kite.positions()["net"], nfo_df, spot=24500, r=0.07)
totals = aggregate_portfolio_greeks(portfolio)
print(f"Net Delta: {totals['delta']:.1f}") # net directional exposure, in underlying-equivalent units
print(f"Net Gamma: {totals['gamma']:.4f}") # how fast that directional exposure will change
print(f"Net Theta: {totals['theta']:.1f}") # daily P&L from time decay alone, all else equal
print(f"Net Vega: {totals['vega']:.1f}") # P&L impact per 1% change in IV, across the whole book
Why this matters more than tracking individual position P&L
A book with 3 different option positions might show a comfortable combined P&L today, while carrying a large net Vega that would produce a significant loss on an IV spike (e.g. ahead of a budget day or RBI policy announcement) — invisible if you only look at current P&L, but immediately visible in aggregated Vega. Portfolio Greeks answer "what happens if X changes," not just "how am I doing right now."
Setting portfolio-level Greek limits — an extension of chapter 74's exposure limits
def check_greek_limits(totals: dict, limits: dict) -> list[str]:
breaches = []
for greek, limit in limits.items():
if abs(totals[greek]) > limit:
breaches.append(f"{greek} exposure {totals[greek]:.1f} exceeds limit {limit}")
return breaches
GREEK_LIMITS = {"delta": 500, "vega": 5000, "theta": -2000} # example values — set based on account size and risk tolerance
Treat this as another layer alongside chapter 73's daily loss limit and chapter 74's exposure limits — specifically catching risk concentration that raw P&L or notional exposure numbers don't reveal on their own.
Recompute frequently — Greeks are not static
Every Greek shifts continuously as spot moves, time passes, and IV changes — a portfolio Delta-neutral at 9:20 AM can drift meaningfully non-neutral by midday purely from spot movement (this is Gamma's direct effect) without a single new trade. Recompute on a regular interval during market hours (chapter 125 covers actively managing this via delta-neutral hedging), not just once at position entry.
Next: 123 — Historical vs implied volatility, IV rank/percentile