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.