# ============================================================
# Gamma_Pressure_Map
# Created by Chewie 6/9/2026
# ============================================================
# Derives a gamma exposure proxy from IV vs. HV relationship.
#
# CORE LOGIC:
# IV/HV ratio < 1.00 → dealers are long gamma (mean-reversion)
# IV/HV ratio > 1.10 → dealers are short gamma (trend/momentum)
# Between 1.00–1.10 → flip zone, no directional bias
#
# KEY LEVELS:
# Call Wall = highest high over wallLookback bars (resistance)
# Put Wall = lowest low over wallLookback bars (support)
# Flip Line = ATR offset midpoint of the two walls
# Pressure Zone = ATR-offset midpoint band between the walls
# BEST ON: SPX, NDX, SPY, QQQ — daily or intraday
# The Z: value is the Z-score we added when implementing the regime classification fix.
# It shows how many standard deviations the current IV/HV ratio is above or below its 60-bar average for that ticker.
# Z near 0 — IV/HV is at its normal level for this instrument
# Z below -0.50 — IV/HV is suppressed, leaning long gamma
# Z below -1.50 — strongly suppressed, strong long gamma
# Z above +0.50 — IV/HV is elevated, leaning short gamma
# Z above +1.50 — strongly elevated, strong short gamma
# The five regimes
# STRONG LONG G — IV well below HV. Dealers are heavily long gamma. Fade moves, buy dips, sell rips. Price tends to pin.
# LONG G — Same directional lean, less conviction. Tighter mean-reversion expected.
# FLIP ZONE — IV and HV are in equilibrium. No directional edge. Wait for resolution before committing.
# SHORT G — IV elevated above HV. Trade with momentum. Breakouts tend to follow through.
# STRONG SHORT G — IV significantly elevated. Explosive move risk. Size down or trade breakouts only.
# GAMMA LABEL: reliable on Daily charts only.
# ImpVolatility() returns a daily figure on all timeframes.
# HV is calculated from chart bars, so IV/HV ratio is only
# meaningful when both are on the same daily timeframe.
#
# PRESSURE ZONE / CANDLE COLORS: valid on all timeframes.
# Call wall, put wall, midpoint and ATR zone use only price
# data and respond correctly to any chart timeframe.
# ============================================================
declare upper;
# ─── INPUTS ────────────────────────────────────────────────
input colorbar = yes;
input lookback = 20; # bars for HV and range calculation
input atrLen = 14; # ATR length for band normalization
input flipBand = 0.10; # IV/HV spread that defines the flip zone
input wallLookback = 50; # bars to scan for call/put wall levels
input dots = yes;
input showWalls = yes;
input showFlip = yes;
input showMidpoint = yes;
input showTrigger = yes;
input showLabel = yes;
input ShowGamma = yes;
input ShowZone = no;
input alert = no;
input showbackground = no;
# ─── PRICE REFS ────────────────────────────────────────────
def h = high;
def l = low;
def c = close;
# ─── IMPLIED VOLATILITY ────────────────────────────────────
def iv = ImpVolatility();
# ─── HISTORICAL VOLATILITY ─────────────────────────────────
def logRet = Log(c / c[1]);
def hvRaw = Sqrt(Average(Sqr(logRet), lookback)) * Sqrt(252);
def hv = if hvRaw > 0 then hvRaw else Double.NaN;
# ─── GEX PRESSURE PROXY ────────────────────────────────────
def gexRatio = if !IsNaN(iv) and hv > 0 then iv / hv else Double.NaN;
# ─── Z-SCORE NORMALIZATION ─────────────────────────────────
def gexAvg = Average(gexRatio, 60);
def gexStdDev = Sqrt(Average(Sqr(gexRatio - gexAvg), 60));
def gexZ = if gexStdDev > 0 and !IsNaN(gexRatio)
then (gexRatio - gexAvg) / gexStdDev
else Double.NaN;
def longGammaStrong = !IsNaN(gexZ) and gexZ < -1.50;
def longGamma = !IsNaN(gexZ) and gexZ >= -1.50 and gexZ < -0.50;
def flipZone = !IsNaN(gexZ) and gexZ >= -0.50 and gexZ <= 0.50;
def shortGamma = !IsNaN(gexZ) and gexZ > 0.50 and gexZ <= 1.50;
def shortGammaStrong = !IsNaN(gexZ) and gexZ > 1.50;
# ─── CALL WALL / PUT WALL LEVELS ───────────────────────────
# Simple high/low clusters over wallLookback — proxy for where
# dealer hedging activity is concentrated
def callWall = Highest(h, wallLookback);
def putWall = Lowest(l, wallLookback);
# ─── BACKGROUND CLOUD ──────────────────────────────────────
# Paints the price pane to show current regime at a glance
def cloudColor =
if longGammaStrong then 1 else
if longGamma then 2 else
if flipZone then 3 else
if shortGamma then 4 else
if shortGammaStrong then 5 else 0;
AssignBackgroundColor(
if !showbackground then Color.CURRENT else
if cloudColor == 1 then CreateColor(0, 80, 0) else # dark green
if cloudColor == 2 then CreateColor(0, 45, 0) else # dim green
if cloudColor == 3 then CreateColor(50, 45, 0) else # dark yellow
if cloudColor == 4 then CreateColor(55, 15, 0) else # dim red
if cloudColor == 5 then CreateColor(80, 0, 0) else # dark red
Color.CURRENT
);
# ─── CALL WALL PLOT ────────────────────────────────────────
plot CallWallPlot = if showWalls then callWall else Double.NaN;
CallWallPlot.SetDefaultColor(Color.magenta);
CallWallPlot.SetLineWeight(3);
CallWallPlot.HideBubble();
# ─── PUT WALL PLOT ─────────────────────────────────────────
plot PutWallPlot = if showWalls then putWall else Double.NaN;
PutWallPlot.SetDefaultColor(Color.cyan);
PutWallPlot.SetLineWeight(3);
PutWallPlot.HideBubble();
# PRICE SIGNALS
plot LD = if dots and low equals PutWall then low - ATR(60) / 2 else Double.NaN;
LD.SetPaintingStrategy(PaintingStrategy.POINTS);
LD.SetDefaultColor(Color.white);
LD.SetLineWeight(2);
plot UD = if dots and high equals CallWall then high + ATR(60) / 2 else Double.NaN;
UD.SetPaintingStrategy(PaintingStrategy.POINTS);
UD.SetDefaultColor(Color.white);
UD.SetLineWeight(2);
# ─── ATR FOR BAND NORMALIZATION ────────────────────────────
def atr = Average(TrueRange(h, c, l), atrLen);
# ─── REGIME LABEL ──────────────────────────────────────────
def regimeCode =
if longGammaStrong then 1 else
if longGamma then 2 else
if flipZone then 3 else
if shortGamma then 4 else
if shortGammaStrong then 5 else 0;
AddLabel(showLabel,
(if regimeCode == 1 then "UP: STRONG LONG G" else
if regimeCode == 2 then "UP: LONG G" else
if regimeCode == 3 then "FLAT: PRESSURE ZONE" else
if regimeCode == 4 then "DN: SHORT G" else
if regimeCode == 5 then "DN: STRONG SHORT G" else
"— NO DATA") +
" | IV/HV: " + Round(gexRatio, 2) + " | Z: " + Round(gexZ, 2),
if regimeCode == 1 then Color.GREEN else
if regimeCode == 2 then CreateColor(100,200,100) else
if regimeCode == 3 then Color.YELLOW else
if regimeCode == 4 then CreateColor(255,120,80) else
if regimeCode == 5 then Color.RED else
Color.GRAY
);
# ─── PRESSURE ZONE ────────
def flipLine = if callWall > putWall
then (callWall + putWall) / 2 + atr
else Double.NaN;
plot FlippointLine = if showflip then flipLine else Double.NaN;
FlippointLine.SetDefaultColor(CreateColor(255, 200, 0));
FlippointLine.SetLineWeight(2);
FlippointLine.HideBubble();
plot Midpoint = if showmidpoint then (callwallplot - putWallPlot) /2 + putwallplot else double.nan;
Midpoint.setdefaultColor(color.yellow);
Midpoint.SetStyle(Curve.SHORT_DASH);
Midpoint.SetLineWeight(2);
AddCloud(FlippointLine, Midpoint, CreateColor(120, 100, 0), CreateColor(120, 100, 0));
# ─── PRICE POSITION LABEL ──────────────────────────────────
def priceCode =
if c > FlippointLine then 1 else
if c < Midpoint then 3 else 2;
AddLabel(showLabel,
if priceCode == 1 then "ABOVE ZONE" else
if priceCode == 2 then "IN ZONE" else
"BELOW ZONE",
if priceCode == 1 then Color.GREEN else
if priceCode == 2 then Color.YELLOW else
Color.RED
);
# ─── CANDLE COLORING ───────────────────────────────────────
AssignPriceColor(
if !colorBar then Color.CURRENT else
if c > FlippointLine then Color.GREEN else
if c < Midpoint then Color.RED else
Color.YELLOW
);
# ─── PRESSURE ZONE ALERTS ─────────────────────────────────
# Short signal: price crosses below the dashed midpoint line
# Long signal: price crosses above the solid ATR-offset line
Alert(alert and
Crosses(c, Midpoint, CrossingDirection.BELOW),
"Gamma Pressure Map: price crossed BELOW midpoint — SHORT bias",
Alert.BAR,
Sound.Ding
);
Alert(alert and
Crosses(c, FlippointLine, CrossingDirection.ABOVE),
"Gamma Pressure Map: price crossed ABOVE pressure zone — LONG bias",
Alert.BAR,
Sound.Ding
);
# ─── REGIME CHANGE BUBBLES AT PRICE ───────────────────────
def GammaChanged = regimeCode != regimeCode[1];
def ZoneChanged = priceCode != priceCode[1];
AddChartBubble(showgamma and GammaChanged, high,
if regimeCode == 1 then "SLG" else
if regimeCode == 2 then "LG" else
if regimeCode == 3 then "FZ" else
if regimeCode == 4 then "SG" else
"SSG",
if regimeCode == 1 then Color.GREEN else
if regimeCode == 2 then CreateColor(100, 200, 100) else
if regimeCode == 3 then Color.YELLOW else
if regimeCode == 4 then CreateColor(255, 120, 80) else
Color.RED
);
AddChartBubble(showzone and ZoneChanged, low,
if priceCode == 1 then "ABV" else
if priceCode == 2 then "IN" else
"BLW",
if priceCode == 1 then Color.GREEN else
if priceCode == 2 then Color.YELLOW else
Color.RED,
no
);
#----------------------------
# TRIGGERLINE
#----------------------------
def price = close;
def smaLength = 8;
def TatrLength = 25; #15
def regLength = 21;
def atrMult = 1.25; # Used to build the Keltner mid channel
def valS = Average(price, smaLength);
def Tatr = Average(TrueRange(high, close, low), TatrLength);
def average_true_range = Tatr;
def Upper_Band = valS + atrMult * average_true_range;
def Lower_Band = valS - atrMult * average_true_range;
def mid1 = (Upper_Band - Lower_Band) / 2 + Lower_Band;
def regMid = Inertia(mid1, regLength);
plot TRIGGERLINE = if showtrigger then regMid else double.nan;
TRIGGERLINE.SetDefaultColor(Color.YELLOW);
TRIGGERLINE.SetLineWeight(3);
TRIGGERLINE.SetPaintingStrategy(PaintingStrategy.LINE);