Delta-neutral hedging

Closes Part 13. Delta-neutral means structuring a position so net Delta (chapter 122's aggregated metric) is near zero — the position's value becomes (approximately) insensitive to small moves in the underlying, isolating exposure to other Greeks (Theta, Vega) instead.

Why anyone wants this

A short-straddle seller (chapter 124) profits from time decay and IV contraction, but starts fully exposed to directional risk from the moment spot moves away from the strike. Delta-hedging offsets that directional risk with an opposing futures position, so the trader's primary exposure stays to what they actually want to bet on — decay and volatility — not direction.

Computing the hedge quantity

def compute_hedge_quantity(portfolio_delta: float, futures_lot_size: int) -> int:
    """Returns futures quantity (in lots) needed to bring net delta to ~zero.
    Negative portfolio_delta -> need to BUY futures; positive -> SELL futures."""
    hedge_units = -portfolio_delta   # futures have delta = 1 per unit
    hedge_lots = round(hedge_units / futures_lot_size)
    return hedge_lots
totals = aggregate_portfolio_greeks(portfolio)   # chapter 122
hedge_lots = compute_hedge_quantity(totals["delta"], futures_lot_size=25)

if hedge_lots > 0:
    side = "BUY"
elif hedge_lots < 0:
    side = "SELL"
qty = abs(hedge_lots) * 25

if hedge_lots != 0:
    order_manager.place(strategy_code="delta-hedge", exchange="NFO", tradingsymbol=fut_symbol,
                          transaction_type=side, quantity=qty, product="NRML", order_type="MARKET")

Why this needs continuous rebalancing — Gamma is the reason

Delta-neutral at one moment does not stay neutral — as spot moves, Delta itself changes (that's what Gamma measures, chapter 119). A position hedged to zero Delta at 9:20 AM can be meaningfully non-neutral again by 9:35 AM purely from normal intraday price movement, with zero new option trades. This is why delta-hedging is inherently an ongoing process ("dynamic hedging"), not a one-time setup.

def rebalance_hedge_loop(kite, order_manager, price_cache, rebalance_threshold: float = 50, check_interval_s: int = 60):
    while market_is_open():
        totals = aggregate_portfolio_greeks(build_position_greeks(kite, kite.positions()["net"], nfo_df, get_spot(), RISK_FREE_RATE))
        if abs(totals["delta"]) > rebalance_threshold:
            hedge_lots = compute_hedge_quantity(totals["delta"], futures_lot_size=25)
            execute_hedge(order_manager, hedge_lots)
        time.sleep(check_interval_s)

rebalance_threshold is a deliberate tradeoff: rebalancing on every tiny Delta drift generates excessive transaction costs (chapter 86) that can easily exceed the Theta/Vega edge you're trying to isolate; rebalancing too infrequently leaves meaningful directional exposure you didn't intend to carry. Tune this threshold against realistic cost assumptions, not zero.

The honest limitation: hedging costs eat into the edge you're isolating

Every rebalancing trade incurs brokerage, STT, and slippage (chapter 86) — a delta-neutral strategy's actual profitability depends heavily on how efficiently (how few times, how tight the hedge) you can maintain neutrality relative to the Theta/Vega edge being captured. This is precisely the kind of cost sensitivity chapter 86 warned generalizes across strategy types: a strategy that looks profitable ignoring transaction costs can be marginal or negative once realistic rebalancing costs are included — always backtest this with full cost modeling before trusting the result.

Next: 126 — Descriptive stats: min, max, mean, median, mode