SPOT GAMMA INDICATOR for thinkorswim
Indicator Derives a gamma exposure proxy from price action and IV.
Indicator Derives a gamma exposure proxy from price action and IV.
METHOD: Gamma is highest near ATM and spikes when IV is low and expiry is near. This script uses:
• ImpVolatility() for the IV surface reading
• Historical volatility vs IV ratio (GEX pressure proxy)
• Price range compression/expansion for regime detection
• ATR-normalized band width as a gamma environment signal
REGIMES:
• Long Gamma = low IV, tight range, mean-reverting price action
•Short Gamma = rising IV, expanding range, trending price
•Flip Zone = IV inflecting, range transitioning
KEY LEVELS:
•Call Wall = nearest resistance (highest recent high cluster)
•Put Wall = nearest support (lowest recent low cluster)
•Flip Line = midpoint of call/put wall weighted by GEX proxy
READING THE LABEL: The top label gives you everything at a glance:
Label text:
• What to do STRONG LONG G (bright green) Fade moves — buy dips, sell rips. Price is pinned.
•LONG G (soft green) Same bias, less conviction.
•Tighter mean-reversion.FLIP ZONE (yellow)No directional bias. Wait for resolution.SHORT G (soft red)Trade with momentum.
•Breakouts tend to follow through.STRONG SHORT G (bright red)Explosive move risk. Size down or trade breakouts only.
IV/HV ratio is the core signal. Under 100% = dealers long gamma (your friend as a mean-reversion trader). Over 110% = dealers short gamma (momentum/trend favored).
USING THE LEVELS:
•Call Wall (green dashed) — treat as resistance in long gamma, as a magnet/target in short gamma
•Put Wall (red dashed) — treat as support in long gamma, as a breakdown trigger in short gamma
•Flip Line (yellow solid) — the pivot. Price above flip = bullish lean, below = bearish lean. Crossing it mid-session is a significant signal
BEST TRADING SETUPS:
•Long Gamma + price near Put Wall → high-probability bounce trade. Dealers will hedge by buying as price falls, creating a floor.
•Short Gamma + price breaks through Call Wall or Put Wall → momentum trade in the direction of the break. Dealers must chase, amplifying the move.
•Flip Zone on the daily + intraday break of Flip Line → directional signal. The first clean break of the yellow line after a flip zone on the daily often starts a 1-3 day trend.
INPUT TUNING:
•Increase wallLookback (default 50) on slower instruments or weekly charts to capture more meaningful highs/lows
•Tighten flipBand to 0.05 for more sensitive flip detection on volatile names
•Lower lookback to 10 on intraday charts for faster regime responsiveness
My best use of indicator is for near end of day cash settled options like SPX and NDX.
A special thanks to AI for helping me get a clean indicator!
INDICATOR LINK: http://tos.mx/!dJHlEUw8
Code:
# ============================================================
# SPOT GAMMA INDICATOR for thinkorswim
# ============================================================
# Derives a gamma exposure proxy from price action and IV.
#
# METHOD:
# Gamma is highest near ATM and spikes when IV is low and
# expiry is near. This script uses:
# - ImpVolatility() for the IV surface reading
# - Historical volatility vs IV ratio (GEX pressure proxy)
# - Price range compression/expansion for regime detection
# - ATR-normalized band width as a gamma environment signal
#
# REGIMES:
# Long Gamma = low IV, tight range, mean-reverting price action
# Short Gamma = rising IV, expanding range, trending price
# Flip Zone = IV inflecting, range transitioning
#
# KEY LEVELS:
# Call Wall = nearest resistance (highest recent high cluster)
# Put Wall = nearest support (lowest recent low cluster)
# Flip Line = midpoint of call/put wall weighted by GEX proxy
#Reading the label
#The top label gives you everything at a glance:
#Label textWhat to doSTRONG LONG G (bright green)Fade moves — buy dips, sell rips. Price is pinned.LONG G (soft green)Same bias, less conviction. Tighter mean-reversion.FLIP ZONE (yellow)No directional bias. Wait for resolution.SHORT G (soft red)Trade with momentum. Breakouts tend to follow through.STRONG SHORT G (bright red)Explosive move risk. Size down or trade breakouts only.
#IV/HV ratio is the core signal. Under 100% = dealers long gamma (your friend as a mean-reversion trader). Over 110% = dealers short gamma (momentum/trend favored).
#Using the levels
#Call Wall (green dashed) — treat as resistance in long gamma, as a magnet/target in short gamma
#Put Wall (red dashed) — treat as support in long gamma, as a breakdown trigger in short gamma
#Flip Line (yellow solid) — the pivot. Price above flip = bullish lean, below = bearish lean. Crossing it mid-session is a significant signal
#Best trading setups
#Long Gamma + price near Put Wall → high-probability bounce trade. Dealers will hedge by buying as price falls, creating a floor.
#Short Gamma + price breaks through Call Wall or Put Wall → momentum trade in the direction of the break. Dealers must chase, amplifying the move.
#Flip Zone on the daily + intraday break of Flip Line → directional signal. The first clean break of the yellow line after a flip zone on the daily often starts a 1-3 day trend.
#Input tuning
#Increase wallLookback (default 50) on slower instruments or weekly charts to capture more meaningful highs/lows
#Tighten flipBand to 0.05 for more sensitive flip detection on volatile names
#Lower lookback to 10 on intraday charts for faster regime responsiveness
declare upper;
# ─── INPUTS ────────────────────────────────────────────────
input lookback = 20; # bars for HV / range calculation
input atrLen = 14; # ATR length for band normalization
input flipBand = 0.10; # % threshold for flip zone detection
input showWalls = yes;
input showFlip = yes;
input wallLookback = 50; # bars to scan for call/put walls
# ─── PRICE ─────────────────────────────────────────────────
def o = open;
def h = high;
def l = low;
def c = close;
# ─── IMPLIED VOLATILITY ────────────────────────────────────
# ImpVolatility() returns the 30-day IV for the underlying
def iv = ImpVolatility();
# ─── HISTORICAL VOLATILITY (close-to-close) ────────────────
def logRet = Log(c / c[1]);
def hvSum = Sum(logRet * logRet, lookback);
def hv = Sqrt(hvSum / lookback) * Sqrt(252);
# ─── IV / HV RATIO (GEX PRESSURE PROXY) ───────────────────
# IV > HV = options pricing in more move than realized
# dealers short gamma, amplifying environment
# IV < HV = options cheap relative to realized vol
# dealers long gamma, dampening environment
def ivhvRatio = if hv > 0 then iv / hv else 1.0;
# ─── ATR AND BAND WIDTH ────────────────────────────────────
def atr = ATR(atrLen);
def bandWidth = atr / c;
def bwMA = Average(bandWidth, lookback);
def bwNorm = if bwMA > 0 then bandWidth / bwMA else 1.0;
# ─── NET GAMMA PROXY ───────────────────────────────────────
# Combines IV/HV ratio and bandwidth expansion:
# Low ratio + tight bands = long gamma environment
# High ratio + wide bands = short gamma environment
def gexProxy = ivhvRatio * bwNorm;
def gexMA = Average(gexProxy, lookback);
def flipLow = gexMA * (1 - flipBand);
def flipHigh = gexMA * (1 + flipBand);
# ─── REGIME FLAGS ──────────────────────────────────────────
def isLongGamma = gexProxy < flipLow and ivhvRatio < 1.0;
def isShortGamma = gexProxy > flipHigh and ivhvRatio > 1.1;
def isFlip = gexProxy >= flipLow and gexProxy <= flipHigh;
def isStrLong = isLongGamma and bwNorm < 0.85;
def isStrShort = isShortGamma and bwNorm > 1.20;
# ─── CALL WALL (highest swing high in wallLookback bars) ───
def swingHigh = Highest(h, wallLookback);
def callWall = Round(swingHigh / 5, 0) * 5;
# ─── PUT WALL (lowest swing low in wallLookback bars) ──────
def swingLow = Lowest(l, wallLookback);
def putWall = Round(swingLow / 5, 0) * 5;
# ─── GAMMA FLIP PRICE ──────────────────────────────────────
# Weighted midpoint: skewed toward put wall in short gamma,
# call wall in long gamma
def gexWeight = if (ivhvRatio + 0.0001) > 0
then 1 / ivhvRatio
else 0.5;
def flipPrice = callWall * (1 - gexWeight * 0.5)
+ putWall * (gexWeight * 0.5);
# ─── PLOT LEVELS ───────────────────────────────────────────
plot CallWallLine = if showWalls then callWall else Double.NaN;
CallWallLine.SetDefaultColor(Color.GREEN);
CallWallLine.SetLineWeight(2);
CallWallLine.SetStyle(Curve.SHORT_DASH);
CallWallLine.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
plot PutWallLine = if showWalls then putWall else Double.NaN;
PutWallLine.SetDefaultColor(Color.RED);
PutWallLine.SetLineWeight(2);
PutWallLine.SetStyle(Curve.SHORT_DASH);
PutWallLine.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
plot FlipLine = if showFlip then flipPrice else Double.NaN;
FlipLine.SetDefaultColor(Color.YELLOW);
FlipLine.SetLineWeight(2);
FlipLine.SetStyle(Curve.FIRM);
FlipLine.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
# ─── BAR COLORING ──────────────────────────────────────────
AssignPriceColor(
if isFlip then Color.YELLOW
else if isStrLong then Color.GREEN
else if isLongGamma then CreateColor(100, 180, 100)
else if isStrShort then Color.RED
else if isShortGamma then CreateColor(200, 80, 80)
else Color.GRAY
);
# ─── LABELS ────────────────────────────────────────────────
def ivPct = Round(iv * 100, 1);
def hvPct = Round(hv * 100, 1);
def ratPct = Round(ivhvRatio * 100, 0);
AddLabel(yes,
"SPOT GAMMA: " +
(if isStrLong then "STRONG LONG G"
else if isLongGamma then "LONG G"
else if isFlip then "FLIP ZONE"
else if isStrShort then "STRONG SHORT G"
else if isShortGamma then "SHORT G"
else "NEUTRAL") +
" IV: " + AsText(ivPct) + "%" +
" HV: " + AsText(hvPct) + "%" +
" IV/HV: " + AsText(ratPct) + "%" +
" CW: " + AsText(Round(callWall, 0)) +
" PW: " + AsText(Round(putWall, 0)) +
" Flip: " + AsText(Round(flipPrice, 2)),
if isFlip then Color.YELLOW
else if isStrLong then Color.GREEN
else if isLongGamma then CreateColor(100, 200, 100)
else if isStrShort then Color.RED
else if isShortGamma then CreateColor(220, 80, 80)
else Color.GRAY
);
AddLabel(yes,
"GEX Proxy: " + AsText(Round(gexProxy, 2)) +
" GEX MA: " + AsText(Round(gexMA, 2)) +
" BW Norm: " + AsText(Round(bwNorm, 2)),
if gexProxy < gexMA then Color.CYAN else Color.ORANGE
);
# ─── BUBBLES ───────────────────────────────────────────────
def nearCW = AbsValue(c - callWall) <= atr * 0.5;
def nearPW = AbsValue(c - putWall) <= atr * 0.5;
def nearFlip = AbsValue(c - flipPrice) <= atr * 0.3;
AddChartBubble(nearCW and !nearCW[1], h, "CALL WALL " + AsText(Round(callWall, 0)), Color.GREEN, yes);
AddChartBubble(nearPW and !nearPW[1], l, "PUT WALL " + AsText(Round(putWall, 0)), Color.RED, no);
AddChartBubble(nearFlip and !nearFlip[1], h, "G FLIP " + AsText(Round(flipPrice, 2)), Color.YELLOW, yes);
# ─── ALERTS ────────────────────────────────────────────────
Alert(nearCW and !nearCW[1],
"Approaching CALL WALL at " + AsText(Round(callWall, 0)),
Alert.BAR, Sound.Ding);
Alert(nearPW and !nearPW[1],
"Approaching PUT WALL at " + AsText(Round(putWall, 0)),
Alert.BAR, Sound.Bell);
Alert(nearFlip and !nearFlip[1],
"Price at GAMMA FLIP: " + AsText(Round(flipPrice, 2)),
Alert.BAR, Sound.Ring);
Alert(isFlip and !isFlip[1],
"Entered GAMMA FLIP ZONE",
Alert.BAR, Sound.Ring);
Alert(isShortGamma and !isShortGamma[1],
"Regime shift to SHORT GAMMA",
Alert.BAR, Sound.Bell);
Alert(isLongGamma and !isLongGamma[1],
"Regime shift to LONG GAMMA",
Alert.BAR, Sound.Ding);
# ─── END ───────────────────────────────────────────────────
Last edited by a moderator: