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