ChatGPT, BARD, Other AI Scripts Which Can't Be Used In ThinkOrSwim

merryDay

Administrative
Staff member
Staff
VIP
Lifetime
90% of the members attempting chatGPT scripting; do not provide good detailed specifications.

Members know what end result that they want but lack the ability to tell chatGPT, the step-by-step logic required to code a script to achieve that end result.
This results in chatGPT creating non-working garbage code.

Here is a video on how to successfully have chatGPT create functional scripts.

The above video, explains the basics of providing good specifications to AI which results in better scripts.
AOzhl05.png
 
Last edited:
@merryDay @useThinkScript

i have a suggestion,
create a new indicator forum topic , 'questions about AI code'
to join the existing 7 topics.
then encourage people to make a new post under that topic when it calls for it, like this post

there is an existing thread for these. but all the posts are just comments. it is up to 22 pages , and hard to follow what replies are to what post. when i answer a question there, i tried to reference post #s, but sometimes posts are deleted and then the counts get off.
https://usethinkscript.com/threads/...ipts-which-cant-be-used-in-thinkorswim.13822/
this thread could be moved in under the new topic

i think more and more people are going to be making posts like this one.

Most of the code being contributed these days is AI code.

This post will be moved to questions.
In case an intrepid contributor wants to take it on.

Thanks for the heads up.
 
Last edited:
GAMMA PRESSURE MAP
View attachment 27537

This indicator derives a real-time gamma exposure proxy from implied volatility and historical volatility, identifying whether options dealers are currently positioned in a long gamma or short gamma regime. It plots a dynamic pressure zone on the price chart that expands and contracts with volatility, giving you both a structural read on market behavior and actionable price levels.

NOTE: 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.

How it works

The core signal is the ratio of Implied Volatility (ImpVolatility()) to Historical Volatility (calculated as the annualized standard deviation of log returns). When IV is running below realized volatility, dealers are likely long gamma — their hedging activity dampens price movement and creates mean-reverting conditions. When IV rises above realized volatility, dealers are likely short gamma — their hedging amplifies moves and favors trending and momentum behavior. The threshold between these regimes is tunable via the flipBand input (default 10%).

The five regimes in the Label
  • 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.
Key levels

Three levels are plotted on the price chart:
  • Call Wall (green solid) — the highest high over the lookback window, representing the nearest overhead resistance cluster where dealer hedging activity concentrates. In long gamma treat as resistance. In short gamma treat as a magnet or target.
  • Put Wall (red solid) — the lowest low over the lookback window, representing the nearest support cluster. In long gamma treat as a floor. In short gamma treat as a breakdown trigger.
  • Pressure Zone (gold cloud) — a dynamic band sitting above the true midpoint of the call and put walls. The lower boundary is the plain midpoint (dashed yellow); the upper boundary is the midpoint plus one ATR (solid gold). The zone widens naturally in high volatility environments and tightens when the market is pinning. Price inside the zone is in contested territory. Price below the zone favors the short side. Price above the zone favors the long side.
  • Trigger Line - After price makes a new higher or lower level, trading the break of the trigger line is a good entry.
Bubbles
  • Gamma bubble— fires when gamma status changes on the chart.
  • Zone bubble— fires when price crosses above, is in, or crosses below the Pressure Zone.
Alerts
  • Short bias alert — fires when price crosses below the dashed midpoint line
  • Long bias alert — fires when price crosses above the solid ATR-offset line
Alerts are tied to actual price levels rather than abstract ratio thresholds, so they fire at the same moment the visual signal appears on the chart.

Best use cases

Works best on index products where dealer hedging actually dominates order flow: SPX, NDX, SPY, QQQ. Also effective on futures like /ES and /MGC. Most reliable on the daily timeframe and intraday near end of session on cash-settled options. For intraday use reduce lookback to 10 and wallLookback to 20 for faster responsiveness.


Code:
# ============================================================
# 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);

If you want a companion lower indicator, try this.
View attachment 27547

Code:
# ============================================================
# GAMMA PRESSURE MAP — LOWER OSCILLATOR
# Created by Chewie 6/9/2026
# ============================================================
# Companion lower panel to the Gamma Pressure Map upper study.
# Normalizes price position within the call/put wall range
# as a 0–100 oscillator.
#
# SCALE:
#   0   = price at put wall (max bearish)
#   100 = price at call wall (max bullish)
#   50  = true midpoint of the range
#
# PRESSURE ZONE:
#   Dashed line = normalized midpoint (50)
#   Solid line  = midpoint + ATR-normalized offset
#   Cloud       = contested zone between the two
#
# 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 / OSCILLATOR: valid on all timeframes.
#
# BEST ON: SPX, NDX, SPY, QQQ
# ============================================================

declare lower;

# ─── INPUTS ────────────────────────────────────────────────
input lookback       = 20;    # bars for HV calculation
input atrLen         = 14;    # ATR length for pressure zone width
input flipBand       = 0.10;  # IV/HV spread defining the flip zone
input wallLookback   = 50;    # bars to scan for call/put wall levels
input showLabel      = yes;
input showBackground = no;
input showFlip       = yes;
input showMidpoint   = yes;
input showGamma      = yes;
input showZone       = no;
input alert          = 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 (self-calibrating per ticker) ───
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;

# ─── REGIME CLASSIFICATION ─────────────────────────────────
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;

# ─── WALLS ─────────────────────────────────────────────────
def callWall  = Highest(h, wallLookback);
def putWall   = Lowest(l, wallLookback);
def wallRange = callWall - putWall;

# ─── ATR ───────────────────────────────────────────────────
def atr = Average(TrueRange(h, c, l), atrLen);

# ─── NORMALIZE ATR TO 0–100 SCALE ──────────────────────────
def atrNorm = if wallRange > 0 then (atr / wallRange) * 100 else 0;

# ─── PRICE OSCILLATOR ──────────────────────────────────────
def oscValue = if wallRange > 0
               then ((c - putWall) / wallRange) * 100
               else Double.NaN;

# ─── PRESSURE ZONE LEVELS (normalized) ─────────────────────
def midpoint  = 50;
def flipLevel = midpoint + atrNorm;

# ─── BACKGROUND CLOUD ──────────────────────────────────────
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
    if cloudColor == 2 then CreateColor(0, 45, 0)   else
    if cloudColor == 3 then CreateColor(50, 45, 0)  else
    if cloudColor == 4 then CreateColor(55, 15, 0)  else
    if cloudColor == 5 then CreateColor(80, 0, 0)   else
    Color.CURRENT
);

# ─── PRICE OSCILLATOR PLOT ─────────────────────────────────
plot PriceOsc = oscValue;
PriceOsc.AssignValueColor(if oscValue >= 50 then Color.GREEN else Color.RED);
PriceOsc.SetLineWeight(2);
PriceOsc.HideBubble();

# ─── CALL WALL LINE (100) ──────────────────────────────────
plot CallLine = 100;
CallLine.SetDefaultColor(Color.MAGENTA);
CallLine.SetLineWeight(3);
CallLine.HideBubble();

# ─── PUT WALL LINE (0) ─────────────────────────────────────
plot PutLine = 0;
PutLine.SetDefaultColor(Color.CYAN);
PutLine.SetLineWeight(3);
PutLine.HideBubble();

# ─── SOLID ATR-OFFSET PRESSURE LINE ────────────────────────
plot FlippointLine = if showFlip then flipLevel else Double.NaN;
FlippointLine.SetDefaultColor(CreateColor(255, 200, 0));
FlippointLine.SetLineWeight(2);
FlippointLine.HideBubble();

# ─── DASHED MIDPOINT LINE (50) ─────────────────────────────
plot MidpointLine = if showMidpoint then midpoint else Double.NaN;
MidpointLine.SetDefaultColor(Color.YELLOW);
MidpointLine.SetStyle(Curve.SHORT_DASH);
MidpointLine.SetLineWeight(2);
MidpointLine.HideBubble();

# ─── PRESSURE ZONE CLOUD ───────────────────────────────────
AddCloud(FlippointLine, MidpointLine, CreateColor(120, 100, 0), CreateColor(120, 100, 0));

# ─── 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),

    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
);

# ─── PRICE POSITION LABEL ──────────────────────────────────
def priceCode =
    if oscValue > flipLevel then 1 else
    if oscValue < 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
);

# ─── REGIME CHANGE BUBBLES ─────────────────────────────────
def gammaChanged = regimeCode != regimeCode[1];
def zoneChanged  = priceCode  != priceCode[1];

AddChartBubble(showGamma and gammaChanged, oscValue,
    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, oscValue,
    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
);

# ─── ALERTS ────────────────────────────────────────────────
Alert(alert and
    Crosses(oscValue, midpoint, CrossingDirection.BELOW),
    "Gamma Pressure Map: oscillator crossed BELOW midpoint — SHORT bias",
    Alert.BAR,
    Sound.Ding
);
Alert(alert and
    Crosses(oscValue, flipLevel, CrossingDirection.ABOVE),
    "Gamma Pressure Map: oscillator crossed ABOVE pressure zone — LONG bias",
    Alert.BAR,
    Sound.Ding
);

Here is a column watchlist that shows when price extends the upper and lower levels and breaks up or down.

Code:
# ============================================================
# Gamma_Pressure_Map Column Watchlist
# Created by Chewie 6/9/2026
# ============================================================
# Derives a gamma exposure proxy from IV vs. HV relationship.
#
# ─── INPUTS ────────────────────────────────────────────────
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

# ─── 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);

# ─── CALL WALL PLOT ────────────────────────────────────────
def CallWallPlot =  callWall;

# ─── PUT WALL PLOT ─────────────────────────────────────────
def PutWallPlot =  putWall;

# ─── ATR FOR BAND NORMALIZATION ────────────────────────────
def atr = Average(TrueRange(h, c, l), atrLen);

# ─── PRESSURE ZONE  ────────

def flipLine = if callWall > putWall
                then (callWall + putWall) / 2 + atr
                else Double.NaN;

def FlippointLine =  flipLine;

def Midpoint = (callwallplot - putWallPlot) /2 + putwallplot;
plot BUY = close crosses above flipline;
plot SELL = close crosses below midpoint;
def UPPER = high equals callwall;
def LOWER = low equals putwall;

AddLabel(yes, if buy then "BUY" else if sell then "SELL" else if buy then "BUY" else if sell then "SELL" else if UPPER then "Upper" else if LOWER then "Lower" else " ", if buy or lower then color.blue else if sell then color.yellow else if upper then color.white else Color.black);

AssignBACKGROUNDColor(if buy then color.green else if sell then color.red else if upper then color.magenta else if lower then color.cyan else color.black);

--------------------------------------------------------------------------------------------------------------------
Both your Upper and Lower indicator is really good. Thank you appreciated for sharing it.
when i was asking AI Gemini some trading tips using this indicator but AI Gemini did some changes to your code by it self and told me to improve it. I don't see much difference from your code but if you see something let me know how it made it better will appreciate it.
--------------------------------------------------------------------------------------------------------------------
AI Gemini:
The Major Math Error in the Cloud
The script uses an AddCloud function to paint a shaded zone between the FlippointLine and the Midpoint. However, the math used to define these lines contains a logical flaw:
  1. Midpoint formula: (callwallplot - putWallPlot) / 2 + putwallplot -> This is the exact geometric center between the two walls.
  2. flipLine formula: (callWall + putWall) / 2 + atr -> This takes that exact same geometric center and adds 1 ATR to it.
The Bug: Because the flipLine is permanently offset higher by 1 ATR, it is not a true "Flip Line" representing structural equilibrium. Instead, the script shifts the entire "Pressure Zone" into the upper half of the trading range.
  • If the market is trading perfectly flat in the middle of the walls, your candles will turn RED (Below Zone), and the indicator will trigger false short alerts because the midpoint line is artifically pushed lower relative to the cloud.

Code:
declare upper;

# ─── INPUTS ────────────────────────────────────────────────
input colorbar = yes;
input lookback = 20; # bars for HV calculation
input atrLen = 14; # ATR length for band normalization
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;

# ─── FIXED TIMEFRAME VOLATILITY ENGINE ──────────────────────
# Using the lowercase fundamental function imp_volatility to correctly accept a period parameter
def ivDaily = imp_volatility(period = AggregationPeriod.DAY);

def cDaily = close(period = AggregationPeriod.DAY);
def logRetDaily = Log(cDaily / cDaily[1]);
def hvRawDaily = Sqrt(Average(Sqr(logRetDaily), lookback)) * Sqrt(252);
def hvDaily = if hvRawDaily > 0 then hvRawDaily else Double.NaN;

# ─── GEX PRESSURE PROXY ────────────────────────────────────
def gexRatio = if !IsNaN(ivDaily) and hvDaily > 0 then ivDaily / hvDaily 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 ───────────────────────────
def callWall = Highest(h, wallLookback);
def putWall = Lowest(l, wallLookback);

# ─── BACKGROUND CLOUD ──────────────────────────────────────
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 if cloudColor == 2 then CreateColor(0, 45, 0)
else if cloudColor == 3 then CreateColor(50, 45, 0)
else if cloudColor == 4 then CreateColor(55, 15, 0)
else if cloudColor == 5 then CreateColor(80, 0, 0)
else Color.CURRENT
);

# ─── WALL PLOTS & RE-TEST DOTS ──────────────────────────────
plot CallWallPlot = if showWalls then callWall else Double.NaN;
CallWallPlot.SetDefaultColor(Color.MAGENTA);
CallWallPlot.SetLineWeight(3);
CallWallPlot.HideBubble();

plot PutWallPlot = if showWalls then putWall else Double.NaN;
PutWallPlot.SetDefaultColor(Color.CYAN);
PutWallPlot.SetLineWeight(3);
PutWallPlot.HideBubble();

plot LD = if dots and low == putWall then low - ATR(60) / 2 else Double.NaN;
LD.SetPaintingStrategy(PaintingStrategy.POINTS);
LD.SetDefaultColor(Color.WHITE);
LD.SetLineWeight(3);

plot UD = if dots and high == callWall then high + ATR(60) / 2 else Double.NaN;
UD.SetPaintingStrategy(PaintingStrategy.POINTS);
UD.SetDefaultColor(Color.WHITE);
UD.SetLineWeight(3);

# ─── FIXED MATHEMATICAL SYMMETRICAL PRESSURE ZONE ──────────
def atr = Average(TrueRange(h, c, l), atrLen);
def absoluteMid = (callWall + putWall) / 2;

# Symmetrical bands wrapped cleanly around the true midpoint
def flipLineUpper = absoluteMid + (atr * 0.5);
def flipLineLower = absoluteMid - (atr * 0.5);

plot FlippointLine = if showFlip then flipLineUpper else Double.NaN;
FlippointLine.SetDefaultColor(CreateColor(255, 200, 0));
FlippointLine.SetLineWeight(2);
FlippointLine.HideBubble();

plot Midpoint = if showMidpoint then absoluteMid else Double.NaN;
Midpoint.SetDefaultColor(Color.YELLOW);
Midpoint.SetStyle(Curve.SHORT_DASH);
Midpoint.SetLineWeight(2);
Midpoint.HideBubble();

# Bottom boundary of the zone container for cloud mapping
def LowerFlank = if showFlip then flipLineLower else Double.NaN;

# Paints a unified cloud completely encapsulating the balanced zone
AddCloud(FlippointLine, LowerFlank, CreateColor(70, 60, 10), CreateColor(70, 60, 10));

# ─── REGIME LABELS ──────────────────────────────────────────
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 "VIX REGIME: STRONG LONG G" else if regimeCode == 2 then "VIX REGIME: LONG G" else if regimeCode == 3 then "VIX REGIME: FLIP ZONE" else if regimeCode == 4 then "VIX REGIME: SHORT G" else if regimeCode == 5 then "VIX REGIME: STRONG SHORT G" else "— NO DATA") + " | IV/HV: " + Round(gexRatio, 2) + " | Z-Score: " + 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 );

def priceCode = if c > flipLineUpper then 1 else if c < flipLineLower then 3 else 2;
AddLabel(showLabel, if priceCode == 1 then "PRICE: POSITION ABOVE ZONE" else if priceCode == 2 then "PRICE: INSIDE EQUILIBRIUM ZONE" else "PRICE: POSITION BELOW ZONE", if priceCode == 1 then Color.GREEN else if priceCode == 2 then Color.YELLOW else Color.RED );

# ─── CANDLE COLORING & BALANCED ALERTS ───────────────────────
AssignPriceColor(
if !colorbar then Color.CURRENT
else if priceCode == 1 then Color.GREEN
else if priceCode == 3 then Color.RED
else Color.YELLOW
);

Alert(alert and Crosses(c, flipLineLower, CrossingDirection.BELOW), "Gamma Pressure Map: Price breakdown BELOW zone - SHORT bias", Alert.BAR, Sound.Ding);
Alert(alert and Crosses(c, flipLineUpper, CrossingDirection.ABOVE), "Gamma Pressure Map: Price breakout ABOVE zone - LONG bias", Alert.BAR, Sound.Ding);

# ─── REGIME CHANGE BUBBLES ─────────────────────────────────
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);

# ─── TREND TRIGGERLINE ─────────────────────────────────────
def smaLength = 8;
def TatrLength = 25;
def regLength = 21;
def atrMult = 1.25;
def valS = Average(c, smaLength);
def Tatr = Average(TrueRange(h, c, l), TatrLength);
def Upper_Band = valS + atrMult * Tatr;
def Lower_Band = valS - atrMult * Tatr;
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);


--------------------------------------------------------------------------------------------------------------------

The error in this lower oscillator code is located on line 22 (def logRetDaily = Log(cDaily / cDaily);). Because it lacks the index offset [1], it divides the current day's price by itself, resulting in a log of 1 (zero) and breaking the historical volatility calculation loops.
Here is the fully corrected, completely clean code for the Gamma Pressure Map — Lower Oscillator. It is fully ready to paste into Thinkorswim.

The Corrected Lower Oscillator Script
--------------------------------------------------------------------------------------------------------------------
Fixes Applied:
  • cDaily[1] Added: Restored the proper 1-bar historical lookup variable so that your log returns are real instead of forcing a zero value.
  • imp_volatility Stability: Fully preserved the native lower-case notation with timeframe string keys required for problem-free calculation on any intra-day charts.
Code:
declare lower;

# ─── INPUTS ────────────────────────────────────────────────
input lookback = 20; # bars for HV calculation
input atrLen = 14; # ATR length for band normalization
input wallLookback = 50; # bars to scan for call/put wall levels
input showLabel = yes;
input showGammaLabels = yes;
input showCloud = yes;

# ─── PRICE REFS ────────────────────────────────────────────
def h = high;
def l = low;
def c = close;

# ─── FIXED TIMEFRAME VOLATILITY ENGINE ──────────────────────
# Pulls Daily metrics so the Volatility Regime labels are accurate on ANY timeframe
def ivDaily = imp_volatility(period = AggregationPeriod.DAY);
def cDaily = close(period = AggregationPeriod.DAY);
def logRetDaily = Log(cDaily / cDaily[1]);
def hvRawDaily = Sqrt(Average(Sqr(logRetDaily), lookback)) * Sqrt(252);
def hvDaily = if hvRawDaily > 0 then hvRawDaily else Double.NaN;

# ─── GEX PRESSURE PROXY & Z-SCORE ──────────────────────────
def gexRatio = if !IsNaN(ivDaily) and hvDaily > 0 then ivDaily / hvDaily else Double.NaN;
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;

# ─── WALL LEVELS & RANGE CALCULATIONS ──────────────────────
def callWall = Highest(h, wallLookback);
def putWall = Lowest(l, wallLookback);
def wallRange = callWall - putWall;

# ─── 0–100 RANGE NORMALIZATION ─────────────────────────────
# Maps current price seamlessly onto a 0 to 100 boundary scale
plot GEX_Oscillator = if wallRange > 0 then ((c - putWall) / wallRange) * 100 else 50;
GEX_Oscillator.SetDefaultColor(Color.WHITE);
GEX_Oscillator.SetLineWeight(2);

# ─── FIXED MATHEMATICAL SYMMETRICAL PRESSURE ZONE ──────────
def atr = Average(TrueRange(h, c, l), atrLen);

# Translates the ATR-offset channel into the 0-100 percentage scale
def normalizedAtrOffset = if wallRange > 0 then (atr / wallRange) * 100 else 0;

plot Midpoint = 50;
Midpoint.SetDefaultColor(Color.YELLOW);
Midpoint.SetStyle(Curve.SHORT_DASH);
Midpoint.SetLineWeight(1);
Midpoint.HideBubble();

plot PressureZoneUpper = 50 + (normalizedAtrOffset * 0.5);
PressureZoneUpper.SetDefaultColor(CreateColor(255, 200, 0));
PressureZoneUpper.SetLineWeight(1);
PressureZoneUpper.HideBubble();

plot PressureZoneLower = 50 - (normalizedAtrOffset * 0.5);
PressureZoneLower.SetDefaultColor(CreateColor(255, 200, 0));
PressureZoneLower.SetLineWeight(1);
PressureZoneLower.HideBubble();

# Paints the internal contested consolidation zone
AddCloud(if showCloud then PressureZoneUpper else Double.NaN, if showCloud then PressureZoneLower else Double.NaN, CreateColor(70, 60, 10), CreateColor(70, 60, 10));

# Static Boundary Markers
plot ExtremeBullish = 100;
ExtremeBullish.SetDefaultColor(Color.MAGENTA);
ExtremeBullish.SetStyle(Curve.LONG_DASH);
ExtremeBullish.HideBubble();

plot ExtremeBearish = 0;
ExtremeBearish.SetDefaultColor(Color.CYAN);
ExtremeBearish.SetStyle(Curve.LONG_DASH);
ExtremeBearish.HideBubble();

# ─── REGIME CONSOLE LABELS ──────────────────────────────────
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 and showGammaLabels, (if regimeCode == 1 then "VIX REGIME: STRONG LONG G" else if regimeCode == 2 then "VIX REGIME: LONG G" else if regimeCode == 3 then "VIX REGIME: FLIP ZONE" else if regimeCode == 4 then "VIX REGIME: SHORT G" else if regimeCode == 5 then "VIX REGIME: STRONG SHORT G" else "— NO DATA") + " | Z-Score: " + 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 );

def positionCode = if GEX_Oscillator > PressureZoneUpper then 1 else if GEX_Oscillator < PressureZoneLower then 3 else 2;
AddLabel(showLabel, if positionCode == 1 then "OSC: BULLISH BREAKOUT (" + Round(GEX_Oscillator, 0) + ")" else if positionCode == 2 then "OSC: INSIDE COMPRESSION ZONE (" + Round(GEX_Oscillator, 0) + ")" else "OSC: BEARISH BREAKOUT (" + Round(GEX_Oscillator, 0) + ")", if positionCode == 1 then Color.GREEN else if positionCode == 2 then Color.YELLOW else Color.RED );
 
Last edited by a moderator:
--------------------------------------------------------------------------------------------------------------------
Both your Upper and Lower indicator is really good. Thank you appreciated for sharing it.
when i was asking AI Gemini some trading tips using this indicator but AI Gemini did some changes to your code by it self and told me to improve it. I don't see much difference from your code but if you see something let me know how it made it better will appreciate it.
--------------------------------------------------------------------------------------------------------------------
AI Gemini:
The Major Math Error in the Cloud
The script uses an AddCloud function to paint a shaded zone between the FlippointLine and the Midpoint. However, the math used to define these lines contains a logical flaw:
  1. Midpoint formula: (callwallplot - putWallPlot) / 2 + putwallplot -> This is the exact geometric center between the two walls.
  2. flipLine formula: (callWall + putWall) / 2 + atr -> This takes that exact same geometric center and adds 1 ATR to it.
The Bug: Because the flipLine is permanently offset higher by 1 ATR, it is not a true "Flip Line" representing structural equilibrium. Instead, the script shifts the entire "Pressure Zone" into the upper half of the trading range.
  • If the market is trading perfectly flat in the middle of the walls, your candles will turn RED (Below Zone), and the indicator will trigger false short alerts because the midpoint line is artifically pushed lower relative to the cloud.

Code:
declare upper;

# ─── INPUTS ────────────────────────────────────────────────
input colorbar = yes;
input lookback = 20; # bars for HV calculation
input atrLen = 14; # ATR length for band normalization
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;

# ─── FIXED TIMEFRAME VOLATILITY ENGINE ──────────────────────
# Using the lowercase fundamental function imp_volatility to correctly accept a period parameter
def ivDaily = imp_volatility(period = AggregationPeriod.DAY);

def cDaily = close(period = AggregationPeriod.DAY);
def logRetDaily = Log(cDaily / cDaily[1]);
def hvRawDaily = Sqrt(Average(Sqr(logRetDaily), lookback)) * Sqrt(252);
def hvDaily = if hvRawDaily > 0 then hvRawDaily else Double.NaN;

# ─── GEX PRESSURE PROXY ────────────────────────────────────
def gexRatio = if !IsNaN(ivDaily) and hvDaily > 0 then ivDaily / hvDaily 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 ───────────────────────────
def callWall = Highest(h, wallLookback);
def putWall = Lowest(l, wallLookback);

# ─── BACKGROUND CLOUD ──────────────────────────────────────
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 if cloudColor == 2 then CreateColor(0, 45, 0)
else if cloudColor == 3 then CreateColor(50, 45, 0)
else if cloudColor == 4 then CreateColor(55, 15, 0)
else if cloudColor == 5 then CreateColor(80, 0, 0)
else Color.CURRENT
);

# ─── WALL PLOTS & RE-TEST DOTS ──────────────────────────────
plot CallWallPlot = if showWalls then callWall else Double.NaN;
CallWallPlot.SetDefaultColor(Color.MAGENTA);
CallWallPlot.SetLineWeight(3);
CallWallPlot.HideBubble();

plot PutWallPlot = if showWalls then putWall else Double.NaN;
PutWallPlot.SetDefaultColor(Color.CYAN);
PutWallPlot.SetLineWeight(3);
PutWallPlot.HideBubble();

plot LD = if dots and low == putWall then low - ATR(60) / 2 else Double.NaN;
LD.SetPaintingStrategy(PaintingStrategy.POINTS);
LD.SetDefaultColor(Color.WHITE);
LD.SetLineWeight(3);

plot UD = if dots and high == callWall then high + ATR(60) / 2 else Double.NaN;
UD.SetPaintingStrategy(PaintingStrategy.POINTS);
UD.SetDefaultColor(Color.WHITE);
UD.SetLineWeight(3);

# ─── FIXED MATHEMATICAL SYMMETRICAL PRESSURE ZONE ──────────
def atr = Average(TrueRange(h, c, l), atrLen);
def absoluteMid = (callWall + putWall) / 2;

# Symmetrical bands wrapped cleanly around the true midpoint
def flipLineUpper = absoluteMid + (atr * 0.5);
def flipLineLower = absoluteMid - (atr * 0.5);

plot FlippointLine = if showFlip then flipLineUpper else Double.NaN;
FlippointLine.SetDefaultColor(CreateColor(255, 200, 0));
FlippointLine.SetLineWeight(2);
FlippointLine.HideBubble();

plot Midpoint = if showMidpoint then absoluteMid else Double.NaN;
Midpoint.SetDefaultColor(Color.YELLOW);
Midpoint.SetStyle(Curve.SHORT_DASH);
Midpoint.SetLineWeight(2);
Midpoint.HideBubble();

# Bottom boundary of the zone container for cloud mapping
def LowerFlank = if showFlip then flipLineLower else Double.NaN;

# Paints a unified cloud completely encapsulating the balanced zone
AddCloud(FlippointLine, LowerFlank, CreateColor(70, 60, 10), CreateColor(70, 60, 10));

# ─── REGIME LABELS ──────────────────────────────────────────
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 "VIX REGIME: STRONG LONG G" else if regimeCode == 2 then "VIX REGIME: LONG G" else if regimeCode == 3 then "VIX REGIME: FLIP ZONE" else if regimeCode == 4 then "VIX REGIME: SHORT G" else if regimeCode == 5 then "VIX REGIME: STRONG SHORT G" else "— NO DATA") + " | IV/HV: " + Round(gexRatio, 2) + " | Z-Score: " + 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 );

def priceCode = if c > flipLineUpper then 1 else if c < flipLineLower then 3 else 2;
AddLabel(showLabel, if priceCode == 1 then "PRICE: POSITION ABOVE ZONE" else if priceCode == 2 then "PRICE: INSIDE EQUILIBRIUM ZONE" else "PRICE: POSITION BELOW ZONE", if priceCode == 1 then Color.GREEN else if priceCode == 2 then Color.YELLOW else Color.RED );

# ─── CANDLE COLORING & BALANCED ALERTS ───────────────────────
AssignPriceColor(
if !colorbar then Color.CURRENT
else if priceCode == 1 then Color.GREEN
else if priceCode == 3 then Color.RED
else Color.YELLOW
);

Alert(alert and Crosses(c, flipLineLower, CrossingDirection.BELOW), "Gamma Pressure Map: Price breakdown BELOW zone - SHORT bias", Alert.BAR, Sound.Ding);
Alert(alert and Crosses(c, flipLineUpper, CrossingDirection.ABOVE), "Gamma Pressure Map: Price breakout ABOVE zone - LONG bias", Alert.BAR, Sound.Ding);

# ─── REGIME CHANGE BUBBLES ─────────────────────────────────
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);

# ─── TREND TRIGGERLINE ─────────────────────────────────────
def smaLength = 8;
def TatrLength = 25;
def regLength = 21;
def atrMult = 1.25;
def valS = Average(c, smaLength);
def Tatr = Average(TrueRange(h, c, l), TatrLength);
def Upper_Band = valS + atrMult * Tatr;
def Lower_Band = valS - atrMult * Tatr;
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);


--------------------------------------------------------------------------------------------------------------------

The error in this lower oscillator code is located on line 22 (def logRetDaily = Log(cDaily / cDaily);). Because it lacks the index offset [1], it divides the current day's price by itself, resulting in a log of 1 (zero) and breaking the historical volatility calculation loops.
Here is the fully corrected, completely clean code for the Gamma Pressure Map — Lower Oscillator. It is fully ready to paste into Thinkorswim.

The Corrected Lower Oscillator Script
--------------------------------------------------------------------------------------------------------------------
Fixes Applied:
  • cDaily[1] Added: Restored the proper 1-bar historical lookup variable so that your log returns are real instead of forcing a zero value.
  • imp_volatility Stability: Fully preserved the native lower-case notation with timeframe string keys required for problem-free calculation on any intra-day charts.
Code:
declare lower;

# ─── INPUTS ────────────────────────────────────────────────
input lookback = 20; # bars for HV calculation
input atrLen = 14; # ATR length for band normalization
input wallLookback = 50; # bars to scan for call/put wall levels
input showLabel = yes;
input showGammaLabels = yes;
input showCloud = yes;

# ─── PRICE REFS ────────────────────────────────────────────
def h = high;
def l = low;
def c = close;

# ─── FIXED TIMEFRAME VOLATILITY ENGINE ──────────────────────
# Pulls Daily metrics so the Volatility Regime labels are accurate on ANY timeframe
def ivDaily = imp_volatility(period = AggregationPeriod.DAY);
def cDaily = close(period = AggregationPeriod.DAY);
def logRetDaily = Log(cDaily / cDaily[1]);
def hvRawDaily = Sqrt(Average(Sqr(logRetDaily), lookback)) * Sqrt(252);
def hvDaily = if hvRawDaily > 0 then hvRawDaily else Double.NaN;

# ─── GEX PRESSURE PROXY & Z-SCORE ──────────────────────────
def gexRatio = if !IsNaN(ivDaily) and hvDaily > 0 then ivDaily / hvDaily else Double.NaN;
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;

# ─── WALL LEVELS & RANGE CALCULATIONS ──────────────────────
def callWall = Highest(h, wallLookback);
def putWall = Lowest(l, wallLookback);
def wallRange = callWall - putWall;

# ─── 0–100 RANGE NORMALIZATION ─────────────────────────────
# Maps current price seamlessly onto a 0 to 100 boundary scale
plot GEX_Oscillator = if wallRange > 0 then ((c - putWall) / wallRange) * 100 else 50;
GEX_Oscillator.SetDefaultColor(Color.WHITE);
GEX_Oscillator.SetLineWeight(2);

# ─── FIXED MATHEMATICAL SYMMETRICAL PRESSURE ZONE ──────────
def atr = Average(TrueRange(h, c, l), atrLen);

# Translates the ATR-offset channel into the 0-100 percentage scale
def normalizedAtrOffset = if wallRange > 0 then (atr / wallRange) * 100 else 0;

plot Midpoint = 50;
Midpoint.SetDefaultColor(Color.YELLOW);
Midpoint.SetStyle(Curve.SHORT_DASH);
Midpoint.SetLineWeight(1);
Midpoint.HideBubble();

plot PressureZoneUpper = 50 + (normalizedAtrOffset * 0.5);
PressureZoneUpper.SetDefaultColor(CreateColor(255, 200, 0));
PressureZoneUpper.SetLineWeight(1);
PressureZoneUpper.HideBubble();

plot PressureZoneLower = 50 - (normalizedAtrOffset * 0.5);
PressureZoneLower.SetDefaultColor(CreateColor(255, 200, 0));
PressureZoneLower.SetLineWeight(1);
PressureZoneLower.HideBubble();

# Paints the internal contested consolidation zone
AddCloud(if showCloud then PressureZoneUpper else Double.NaN, if showCloud then PressureZoneLower else Double.NaN, CreateColor(70, 60, 10), CreateColor(70, 60, 10));

# Static Boundary Markers
plot ExtremeBullish = 100;
ExtremeBullish.SetDefaultColor(Color.MAGENTA);
ExtremeBullish.SetStyle(Curve.LONG_DASH);
ExtremeBullish.HideBubble();

plot ExtremeBearish = 0;
ExtremeBearish.SetDefaultColor(Color.CYAN);
ExtremeBearish.SetStyle(Curve.LONG_DASH);
ExtremeBearish.HideBubble();

# ─── REGIME CONSOLE LABELS ──────────────────────────────────
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 and showGammaLabels, (if regimeCode == 1 then "VIX REGIME: STRONG LONG G" else if regimeCode == 2 then "VIX REGIME: LONG G" else if regimeCode == 3 then "VIX REGIME: FLIP ZONE" else if regimeCode == 4 then "VIX REGIME: SHORT G" else if regimeCode == 5 then "VIX REGIME: STRONG SHORT G" else "— NO DATA") + " | Z-Score: " + 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 );

def positionCode = if GEX_Oscillator > PressureZoneUpper then 1 else if GEX_Oscillator < PressureZoneLower then 3 else 2;
AddLabel(showLabel, if positionCode == 1 then "OSC: BULLISH BREAKOUT (" + Round(GEX_Oscillator, 0) + ")" else if positionCode == 2 then "OSC: INSIDE COMPRESSION ZONE (" + Round(GEX_Oscillator, 0) + ")" else "OSC: BEARISH BREAKOUT (" + Round(GEX_Oscillator, 0) + ")", if positionCode == 1 then Color.GREEN else if positionCode == 2 then Color.YELLOW else Color.RED );

No, your changes will fundamentally change and weaken the indicator.
Here is why:

1. The Volatility Regime & Gamma Framing​

The Critique: "Drop the gamma framing entirely... or invert your hypothesis and backtest IV/HV<1 as a trend signal, not mean-reversion."

Why it doesn't apply:​

You are looking at the baseline text description (IV/HV ratio < 1.00) and treating it like a raw, static ratio filter. If this script just traded a raw $IV < HV$ signal, you might have a point regarding standard volatility premium harvesting.

However, you missed the core mathematical engine of the script: the 60-period Z-score normalization (gexZ).
  • Contextual Volatility: The script does not look at $IV/HV$ in a vacuum. By processing the ratio through a rolling Z-score, it calculates relative volatility suppression or expansion tailored to that specific underlying asset.
  • The Dealer Mechanics: The "gamma framing" is structurally valid. In equity indices (like SPX/SPY), when the $IV/HV$ ratio is severely suppressed relative to its own history (a deep negative Z-score), options are cheap, implied volatility leans flat, and market makers/dealers structurally accumulate long gamma profiles. Mechanically, to maintain delta-neutrality, long gamma dealers must buy dips and sell rallies, which dampens realized volatility and creates a mean-reverting "pinning" effect.
  • When $IV$ expands rapidly past $HV$ (a high positive Z-score), premium explodes, dealers short gamma, and they are forced to buy highs and sell lows to hedge, accelerating breakouts into explosive momentum. Inverting this logic would fly directly in the face of market maker hedging mechanics.

2. The Keltner Scaffolding in TRIGGERLINE​

The Critique: "Delete the Keltner scaffolding in TRIGGERLINE — it's computing an SMA8 the long way."

Why it doesn't apply:​

You get bonus points for catching the algebraic redundancy here, but you fundamentally misread the final output of the calculation.

Look at the math.

Code snippet
def mid1 = (Upper_Band - Lower_Band) / 2 + Lower_Band;
If you expand the Keltner bands, the ATR components perfectly cancel out:

$$\text{mid1} = \frac{(valS + 1.25 \cdot ATR) - (valS - 1.25 \cdot ATR)}{2} + (valS - 1.25 \cdot ATR) = valS$$
So yes, you are 100% correct that mid1 simplifies exactly to valS, which is just Average(price, 8).

However, the critique falls apart on the very next line of code:
Code snippet
def regMid = Inertia(mid1, regLength);
plot TRIGGERLINE = if showtrigger then regMid else double.nan;
  • It's not just an SMA8: The final TRIGGERLINE does not plot mid1 (the SMA8). It plots regMid, which passes that value through Inertia(mid1, 21).
  • The Math Engine: Inertia in ThinkScript calculates the endpoint of a Linear Regression Curve. By applying a 21-period linear regression to an 8-period simple moving average, you are drastically reducing the inherent lag of the SMA while keeping the curve incredibly smooth.
  • Future-Proof Scaffolding: While the ATR math currently cancels out to a symmetrical midpoint, keeping the Keltner scaffolding intact allows you to easily introduce asymmetric volatility bands or options skew adjustments later on without rewriting the entire core block.
 
Last edited:
Hi All, I am learning to make a scanner that will scan for stocks that are up in the 3 timeframes Short/Medium/Long. Claude is asking me to choose a study as follows:

But I dont see this in the stock hacker, what am i missing?

1783373129528.png
 
Last edited by a moderator:
Hi. Welcome.

For the most part, MTF scanning is not a thing. It is not natively supported by ToS, at least as far as I know.

There are workarounds, but they are cludgy and hackish and not really MTF. They work by making a first list (your first timeframe condition is true) and then scanning that list with your second condition or timeframe. And if you really need a third, then scan the second resultant list with the next condition... As said, a hack.

Sorry to disappoint. Good luck.
-mashume
 
There are several steps missing between 2 and 3.

The MTF_Uptrend_Scanner would need to have been created if it hasn't.
If Claude generated that script for you, the code itself likely suffers similar mistakes.

As already stated, the scanner doesn't allow scripts that access multiple time frames.
So it doesn't really matter anyway.

In some cases, you can also use multiple parallel scripts in the same scan, as an alternative to multi-layered scanning in sequence. But, it depends on the specifics.

Artificial intelligence has artificial mental problems.
 
There are several steps missing between 2 and 3.

The MTF_Uptrend_Scanner would need to have been created if it hasn't.
If Claude generated that script for you, the code itself likely suffers similar mistakes.

As already stated, the scanner doesn't allow scripts that access multiple time frames.
So it doesn't really matter anyway.

In some cases, you can also use multiple parallel scripts in the same scan, as an alternative to multi-layered scanning in sequence. But, it depends on the specifics.

Artificial intelligence has artificial mental problems.
unless you weight the timeframes into ranges and have one score to scan for - a weighted matrix is just one way around TOS limitations
 
I found this interesting script using AI. But there is one error popping up on Line 50. Would a coding expert please fix it for me? I'll insert XXX next to it so you can find it. Thanks in advance!

Code:
# SPY 0DTE Sowing & Harvesting Terminal Study
# Target Timeframe: 5-Minute Chart

input ZoneStart = 0930;
input ZoneEnd = 1015;
input CutoffTime = 1530; # 3:30 PM EST Hard Expiration Cutoff
input VolumeMultiplier = 1.6;
input MinRangeSize = 1.50; # Minimum SPY range size required to trade

def isTargetZone = SecondsFromTime(ZoneStart) >= 0 and SecondsToTime(ZoneEnd) > 0;
def postZone = SecondsFromTime(ZoneEnd) >= 0 and SecondsToTime(1600) > 0;
def isBeforeCutoff = SecondsToTime(CutoffTime) > 0;

# Capture High/Low of the Morning Sowing Window
def morningHigh = if isTargetZone and !isTargetZone then high else if isTargetZone then Max(high, morningHigh) else morningHigh;
def morningLow = if isTargetZone and !isTargetZone then low else if isTargetZone then Min(low, morningLow) else morningLow;
def rangeSize = morningHigh - morningLow;

# Calculate Target Projections
def rangeSizeCalc = if rangeSize == 0 then 1 else rangeSize;
plot BullTarget1 = if postZone and isBeforeCutoff then morningHigh + rangeSizeCalc else Double.NaN;
plot BullTarget2 = if postZone and isBeforeCutoff then morningHigh + (rangeSizeCalc * 2) else Double.NaN;
plot BearTarget1 = if postZone and isBeforeCutoff then morningLow - rangeSizeCalc else Double.NaN;
plot BearTarget2 = if postZone and isBeforeCutoff then morningLow - (rangeSizeCalc * 2) else Double.NaN;

BullTarget1.SetDefaultColor(Color.GREEN);
BullTarget1.SetStyle(Curve.MEDIUM_DASH);
BullTarget2.SetDefaultColor(Color.DARK_GREEN);
BullTarget2.SetStyle(Curve.SOLID);
BearTarget1.SetDefaultColor(Color.RED);
BearTarget1.SetStyle(Curve.MEDIUM_DASH);
BearTarget2.SetDefaultColor(Color.DARK_RED);
BearTarget2.SetStyle(Curve.SOLID);

# Relative Volume Filter
def avgVol = Average(volume, 20);
def highVol = volume > (avgVol * VolumeMultiplier);

# Trailing Stop Mechanics (9 EMA)
def ema9 = MovingAverage(AverageType.EXPONENTIAL, close, 9);

# Chop Lockout Detector
def IsChoppyMarket = postZone and isBeforeCutoff and (rangeSize < MinRangeSize or Average(volume, 10) < avgVol * 0.8);

# Breakout Triggers
def BullishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] <= morningHigh and close > morningHigh and highVol;
def BearishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] >= morningLow and close < morningLow and highVol;

# Track Active Trades to maintain background color
XXX type state = {BULL, BEAR, NONE};
def tradeState = if BullishBreakout then state.BULL else if BearishBreakout then state.BEAR else if (state == state.BULL and close < ema9) or (state == state.BEAR and close > ema9) or !isBeforeCutoff then state.NONE else state;

# --- AUTOMATIC CHART BACKGROUND COLORING ---
AssignBackgroundColor(
if !isBeforeCutoff then Color.CURRENT
else if isTargetZone then Color.DARK_GRAY
else if IsChoppyMarket then CreateColor(60, 15, 15) # Dark Red Lockout
else if tradeState == state.BULL then CreateColor(15, 50, 15) # Active Call Trend
else if tradeState == state.BEAR then CreateColor(40, 15, 50) # Active Put Trend
else Color.CURRENT
);

# Plot Range Lines
plot RangeHigh = if postZone and isBeforeCutoff then morningHigh else Double.NaN;
plot RangeLow = if postZone and isBeforeCutoff then morningLow else Double.NaN;
RangeHigh.SetDefaultColor(Color.GRAY);
RangeHigh.SetStyle(Curve.SHORT_DASH);
RangeLow.SetDefaultColor(Color.GRAY);
RangeLow.SetStyle(Curve.SHORT_DASH);

# --- ON-SCREEN DASHBOARD ---
AddLabel(1,
if isTargetZone then " PHASE: SOWING (Observe Morning Range) "
else if !isBeforeCutoff then " !!! HARVEST OVER: LIQUIDATE ALL 0DTE OPTIONS !!! "
else if IsChoppyMarket then " LOCKOUT: CHOPPY / LOW-VOLUME CONDITIONS "
else if tradeState == state.BULL then " HARVESTING: ACTIVE CALLS (Riding 9 EMA) "
else if tradeState == state.BEAR then " HARVESTING: ACTIVE PUTS (Riding 9 EMA) "
else " PHASE: WATCHING (Waiting for High-Volume Breakout) ",
if !isBeforeCutoff then Color.RED else if isTargetZone then Color.YELLOW else if IsChoppyMarket then Color.LIGHT_RED else if tradeState == state.BULL then Color.GREEN else if tradeState == state.BEAR then Color.MAGENTA else Color.WHITE
);

AddLabel(postZone, " AM Range Size: $" + AsText(rangeSize) + " (Min Required: $" + AsText(MinRangeSize) + ") ", if rangeSize >= MinRangeSize then Color.GREEN else Color.RED);

Alert(BullishBreakout, "0DTE Bullish Breakout Confirmed!", Alert.BAR, Sound.Chime);
Alert(BearishBreakout, "0DTE Bearish Breakdown Confirmed!", Alert.BAR, Sound.Bell);
Alert(!isBeforeCutoff and isBeforeCutoff[1], "3:30 PM Cutoff Hit! Flatten positions!", Alert.BAR, Sound.Ring);
 
Last edited by a moderator:
Try this... I only got rid of the errors but didn't check the logic overall...

Corrected by Google AI and myself...

Ruby:
# SPY 0DTE Sowing & Harvesting Terminal Study
# Target Timeframe: 5-Minute Chart

input ZoneStart = 0930;
input ZoneEnd = 1015;
input CutoffTime = 1530; # 3:30 PM EST Hard Expiration Cutoff
input VolumeMultiplier = 1.6;
input MinRangeSize = 1.50; # Minimum SPY range size required to trade

def isTargetZone = SecondsFromTime(ZoneStart) >= 0 and SecondsTillTime(ZoneEnd) > 0;
def postZone = SecondsFromTime(ZoneEnd) >= 0 and SecondsTillTime(1600) > 0;
def isBeforeCutoff = SecondsTillTime(CutoffTime) > 0;

# Capture High/Low of the Morning Sowing Window
def morningHigh = if isTargetZone and !isTargetZone[1] then high else if isTargetZone then Max(high, morningHigh[1]) else morningHigh[1];
def morningLow = if isTargetZone and !isTargetZone[1] then low else if isTargetZone then Min(low, morningLow[1]) else morningLow[1];
def rangeSize = morningHigh - morningLow;

# Calculate Target Projections
def rangeSizeCalc = if rangeSize == 0 then 1 else rangeSize;

plot BullTarget1 = if postZone and isBeforeCutoff then morningHigh + rangeSizeCalc else Double.NaN;
plot BullTarget2 = if postZone and isBeforeCutoff then morningHigh + (rangeSizeCalc * 2) else Double.NaN;
plot BearTarget1 = if postZone and isBeforeCutoff then morningLow - rangeSizeCalc else Double.NaN;
plot BearTarget2 = if postZone and isBeforeCutoff then morningLow - (rangeSizeCalc * 2) else Double.NaN;

BullTarget1.SetDefaultColor(Color.GREEN);
BullTarget1.SetStyle(Curve.MEDIUM_DASH);
BullTarget2.SetDefaultColor(Color.DARK_GREEN);
BullTarget2.SetStyle(Curve.FIRM);
BearTarget1.SetDefaultColor(Color.RED);
BearTarget1.SetStyle(Curve.MEDIUM_DASH);
BearTarget2.SetDefaultColor(Color.DARK_RED);
BearTarget2.SetStyle(Curve.FIRM);

# Relative Volume Filter
def avgVol = Average(volume, 20);
def highVol = volume > (avgVol * VolumeMultiplier);

# Trailing Stop Mechanics (9 EMA)
def ema9 = MovingAverage(AverageType.EXPONENTIAL, close, 9);

# Chop Lockout Detector
def IsChoppyMarket = postZone and isBeforeCutoff and (rangeSize < MinRangeSize or Average(volume, 10) < avgVol * 0.8);

# Breakout Triggers
def BullishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] <= morningHigh and close > morningHigh and highVol;
def BearishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] >= morningLow and close < morningLow and highVol;

# --- STATE MACHINE (Integers replace unsupported custom types) ---
# 1 = BULL, -1 = BEAR, 0 = NONE
def tradeState = if BullishBreakout then 1
                 else if BearishBreakout then -1
                 else if (tradeState[1] == 1 and close < ema9) or (tradeState[1] == -1 and close > ema9) or !isBeforeCutoff then 0
                 else tradeState[1];

# --- AUTOMATIC CHART BACKGROUND COLORING ---
AssignBackgroundColor(
    if !isBeforeCutoff then Color.CURRENT
    else if isTargetZone then Color.DARK_GRAY
    else if IsChoppyMarket then CreateColor(60, 15, 15) # Dark Red Lockout
    else if tradeState == 1 then CreateColor(15, 50, 15) # Active Call Trend
    else if tradeState == -1 then CreateColor(40, 15, 50) # Active Put Trend
    else Color.CURRENT
);

# Plot Range Lines
plot RangeHigh = if postZone and isBeforeCutoff then morningHigh else Double.NaN;
plot RangeLow = if postZone and isBeforeCutoff then morningLow else Double.NaN;

RangeHigh.SetDefaultColor(Color.GRAY);
RangeHigh.SetStyle(Curve.SHORT_DASH);
RangeLow.SetDefaultColor(Color.GRAY);
RangeLow.SetStyle(Curve.SHORT_DASH);

# --- ON-SCREEN DASHBOARD ---
AddLabel(1, if isTargetZone then " PHASE: SOWING (Observe Morning Range) "
            else if !isBeforeCutoff then " !!! HARVEST OVER: LIQUIDATE ALL 0DTE OPTIONS !!! "
            else if IsChoppyMarket then " LOCKOUT: CHOPPY / LOW-VOLUME CONDITIONS "
            else if tradeState == 1 then " HARVESTING: ACTIVE CALLS (Riding 9 EMA) "
            else if tradeState == -1 then " HARVESTING: ACTIVE PUTS (Riding 9 EMA) "
            else " PHASE: WATCHING (Waiting for High-Volume Breakout) ",
            if !isBeforeCutoff then Color.RED
            else if isTargetZone then Color.YELLOW
            else if IsChoppyMarket then Color.LIGHT_RED
            else if tradeState == 1 then Color.GREEN
            else if tradeState == -1 then Color.MAGENTA
            else Color.WHITE
);

AddLabel(postZone, " AM Range Size: $" + AsText(rangeSize) + " (Min Required: $" + AsText(MinRangeSize) + ") ", if rangeSize >= MinRangeSize then Color.GREEN else Color.RED);

Alert(BullishBreakout, "0DTE Bullish Breakout Confirmed!", Alert.BAR, Sound.DING);
Alert(BearishBreakout, "0DTE Bearish Breakdown Confirmed!", Alert.BAR, Sound.Bell);
Alert(!isBeforeCutoff and isBeforeCutoff[1], "3:30 PM Cutoff Hit! Flatten positions!", Alert.BAR, Sound.Ring);
 
I found this interesting script using AI. But there is one error popping up on Line 50. Would a coding expert please fix it for me? I'll insert XXX next to it so you can find it. Thanks in advance!

Code:
# SPY 0DTE Sowing & Harvesting Terminal Study
# Target Timeframe: 5-Minute Chart

input ZoneStart = 0930;
input ZoneEnd = 1015;
input CutoffTime = 1530; # 3:30 PM EST Hard Expiration Cutoff
input VolumeMultiplier = 1.6;
input MinRangeSize = 1.50; # Minimum SPY range size required to trade

def isTargetZone = SecondsFromTime(ZoneStart) >= 0 and SecondsToTime(ZoneEnd) > 0;
def postZone = SecondsFromTime(ZoneEnd) >= 0 and SecondsToTime(1600) > 0;
def isBeforeCutoff = SecondsToTime(CutoffTime) > 0;

# Capture High/Low of the Morning Sowing Window
def morningHigh = if isTargetZone and !isTargetZone then high else if isTargetZone then Max(high, morningHigh) else morningHigh;
def morningLow = if isTargetZone and !isTargetZone then low else if isTargetZone then Min(low, morningLow) else morningLow;
def rangeSize = morningHigh - morningLow;

# Calculate Target Projections
def rangeSizeCalc = if rangeSize == 0 then 1 else rangeSize;
plot BullTarget1 = if postZone and isBeforeCutoff then morningHigh + rangeSizeCalc else Double.NaN;
plot BullTarget2 = if postZone and isBeforeCutoff then morningHigh + (rangeSizeCalc * 2) else Double.NaN;
plot BearTarget1 = if postZone and isBeforeCutoff then morningLow - rangeSizeCalc else Double.NaN;
plot BearTarget2 = if postZone and isBeforeCutoff then morningLow - (rangeSizeCalc * 2) else Double.NaN;

BullTarget1.SetDefaultColor(Color.GREEN);
BullTarget1.SetStyle(Curve.MEDIUM_DASH);
BullTarget2.SetDefaultColor(Color.DARK_GREEN);
BullTarget2.SetStyle(Curve.SOLID);
BearTarget1.SetDefaultColor(Color.RED);
BearTarget1.SetStyle(Curve.MEDIUM_DASH);
BearTarget2.SetDefaultColor(Color.DARK_RED);
BearTarget2.SetStyle(Curve.SOLID);

# Relative Volume Filter
def avgVol = Average(volume, 20);
def highVol = volume > (avgVol * VolumeMultiplier);

# Trailing Stop Mechanics (9 EMA)
def ema9 = MovingAverage(AverageType.EXPONENTIAL, close, 9);

# Chop Lockout Detector
def IsChoppyMarket = postZone and isBeforeCutoff and (rangeSize < MinRangeSize or Average(volume, 10) < avgVol * 0.8);

# Breakout Triggers
def BullishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] <= morningHigh and close > morningHigh and highVol;
def BearishBreakout = postZone and isBeforeCutoff and !IsChoppyMarket and close[1] >= morningLow and close < morningLow and highVol;

# Track Active Trades to maintain background color
XXX type state = {BULL, BEAR, NONE};
def tradeState = if BullishBreakout then state.BULL else if BearishBreakout then state.BEAR else if (state == state.BULL and close < ema9) or (state == state.BEAR and close > ema9) or !isBeforeCutoff then state.NONE else state;

# --- AUTOMATIC CHART BACKGROUND COLORING ---
AssignBackgroundColor(
if !isBeforeCutoff then Color.CURRENT
else if isTargetZone then Color.DARK_GRAY
else if IsChoppyMarket then CreateColor(60, 15, 15) # Dark Red Lockout
else if tradeState == state.BULL then CreateColor(15, 50, 15) # Active Call Trend
else if tradeState == state.BEAR then CreateColor(40, 15, 50) # Active Put Trend
else Color.CURRENT
);

# Plot Range Lines
plot RangeHigh = if postZone and isBeforeCutoff then morningHigh else Double.NaN;
plot RangeLow = if postZone and isBeforeCutoff then morningLow else Double.NaN;
RangeHigh.SetDefaultColor(Color.GRAY);
RangeHigh.SetStyle(Curve.SHORT_DASH);
RangeLow.SetDefaultColor(Color.GRAY);
RangeLow.SetStyle(Curve.SHORT_DASH);

# --- ON-SCREEN DASHBOARD ---
AddLabel(1,
if isTargetZone then " PHASE: SOWING (Observe Morning Range) "
else if !isBeforeCutoff then " !!! HARVEST OVER: LIQUIDATE ALL 0DTE OPTIONS !!! "
else if IsChoppyMarket then " LOCKOUT: CHOPPY / LOW-VOLUME CONDITIONS "
else if tradeState == state.BULL then " HARVESTING: ACTIVE CALLS (Riding 9 EMA) "
else if tradeState == state.BEAR then " HARVESTING: ACTIVE PUTS (Riding 9 EMA) "
else " PHASE: WATCHING (Waiting for High-Volume Breakout) ",
if !isBeforeCutoff then Color.RED else if isTargetZone then Color.YELLOW else if IsChoppyMarket then Color.LIGHT_RED else if tradeState == state.BULL then Color.GREEN else if tradeState == state.BEAR then Color.MAGENTA else Color.WHITE
);

AddLabel(postZone, " AM Range Size: $" + AsText(rangeSize) + " (Min Required: $" + AsText(MinRangeSize) + ") ", if rangeSize >= MinRangeSize then Color.GREEN else Color.RED);

Alert(BullishBreakout, "0DTE Bullish Breakout Confirmed!", Alert.BAR, Sound.Chime);
Alert(BearishBreakout, "0DTE Bearish Breakdown Confirmed!", Alert.BAR, Sound.Bell);
Alert(!isBeforeCutoff and isBeforeCutoff[1], "3:30 PM Cutoff Hit! Flatten positions!", Alert.BAR, Sound.Ring);
Try this still a work in progess but no errors:---
Code:
# SPY 0DTE Sowing & Harvesting Terminal V2.0
# Target chart: SPY, 5-minute, regular-session trading workflow
#
# This is a chart study and alert engine only. It cannot place, manage,
# or liquidate an options position.


declare upper;

#=============================================================================
# INPUTS
#=============================================================================

input ZoneStart = 0930;
input ZoneEnd = 1015;
input CutoffTime = 1530;
input SessionEnd = 1600;

input RangeQualification = {default ATR_ADAPTIVE, FIXED_DOLLARS};
input FixedMinimumRange = 1.50;
input DailyATRLength = 14;
input MinimumRangeATRPercent = 25.0;
input AdaptiveMinimumFloor = 0.75;

input RequireVolumeConfirmation = yes;
input VolumeBaseline = {default SESSION_PHASE, TRAILING_AVERAGE};
input VolumeMultiplier = 1.60;
input QuietTapeRelativeVolume = 0.80;
input TrailingVolumeLength = 20;
input MinimumPhaseBaselineBars = 4;

input BreakoutBuffer = 0.05;
input EMALength = 9;
input MaximumEntriesPerDirection = 2;

input ShowRangeLines = yes;
input ShowTargets = yes;
input ShowActiveEMA = yes;
input ShowSignalArrows = yes;
input ShowSignalBubbles = yes;
input ShowBackground = yes;
input ShowDashboard = yes;
input ShowCutoffLine = yes;
input EnableAlerts = yes;
input EnableTargetAlerts = yes;

#=============================================================================
# CHART AND SESSION VALIDATION
#=============================================================================

def isFiveMinute = GetAggregationPeriod() == AggregationPeriod.FIVE_MIN;

def isTargetZone =
    SecondsFromTime(ZoneStart) >= 0 and
    SecondsTillTime(ZoneEnd) > 0;

def postZone =
    SecondsFromTime(ZoneEnd) >= 0 and
    SecondsTillTime(SessionEnd) > 0;

def tradeWindow =
    SecondsFromTime(ZoneEnd) >= 0 and
    SecondsTillTime(CutoffTime) > 0;

def afterCutoff =
    SecondsFromTime(CutoffTime) >= 0 and
    SecondsTillTime(SessionEnd) > 0;

def beforeOpen = SecondsTillTime(ZoneStart) > 0;
def afterClose = SecondsFromTime(SessionEnd) >= 0;
def zoneStartBar = isTargetZone and !isTargetZone[1];
def cutoffEvent = afterCutoff and !afterCutoff[1];

#=============================================================================
# MORNING SOWING RANGE
#=============================================================================

def morningHigh = CompoundValue(1,
    if zoneStartBar then high
    else if isTargetZone then Max(high, morningHigh[1])
    else morningHigh[1],
    Double.NaN);

def morningLow = CompoundValue(1,
    if zoneStartBar then low
    else if isTargetZone then Min(low, morningLow[1])
    else morningLow[1],
    Double.NaN);

def rangeSessionDate = CompoundValue(1,
    if zoneStartBar then GetYYYYMMDD()
    else rangeSessionDate[1],
    0);

def rangeSize = morningHigh - morningLow;

def rangeReady =
    postZone and
    rangeSessionDate == GetYYYYMMDD() and
    !IsNaN(morningHigh) and
    !IsNaN(morningLow) and
    rangeSize > 0;

#=============================================================================
# ADAPTIVE RANGE QUALIFICATION
# Uses the prior completed daily ATR so the threshold does not move intraday.
#=============================================================================

def dailyHigh = high(period = AggregationPeriod.DAY);
def dailyLow = low(period = AggregationPeriod.DAY);
def dailyClose = close(period = AggregationPeriod.DAY);
def dailyTrueRange = TrueRange(dailyHigh, dailyClose, dailyLow);
def dailyATR = MovingAverage(AverageType.WILDERS, dailyTrueRange, DailyATRLength);
def completedDailyATR = dailyATR[1];

def adaptiveMinimumRange =
    if IsNaN(completedDailyATR) then FixedMinimumRange
    else Max(AdaptiveMinimumFloor,
             completedDailyATR * MinimumRangeATRPercent / 100.0);

def requiredRange =
    if RangeQualification == RangeQualification.FIXED_DOLLARS
    then FixedMinimumRange
    else adaptiveMinimumRange;

def rangeQualified = rangeReady and rangeSize >= requiredRange;
def rangeLockout = rangeReady and !rangeQualified;

#=============================================================================
# SESSION-PHASE RELATIVE VOLUME
#
# Phase 1: 10:15-11:30
# Phase 2: 11:30-14:00
# Phase 3: 14:00-15:30
#
# Each new phase initially uses the completed prior phase (or the opening-range
# average), then transitions to its own developing average. This prevents a
# 2:00 PM bar from being compared directly with the 9:30 AM volume surge.
#=============================================================================

def openingVolumeSum = CompoundValue(1,
    if zoneStartBar then volume
    else if isTargetZone then openingVolumeSum[1] + volume
    else openingVolumeSum[1],
    0);

def openingVolumeBars = CompoundValue(1,
    if zoneStartBar then 1
    else if isTargetZone then openingVolumeBars[1] + 1
    else openingVolumeBars[1],
    0);

def openingAverageVolume =
    if openingVolumeBars > 0
    then openingVolumeSum / openingVolumeBars
    else Double.NaN;

def earlyPhase =
    SecondsFromTime(ZoneEnd) >= 0 and
    SecondsTillTime(1130) > 0;

def middayPhase =
    SecondsFromTime(1130) >= 0 and
    SecondsTillTime(1400) > 0;

def latePhase =
    SecondsFromTime(1400) >= 0 and
    SecondsTillTime(CutoffTime) > 0;

def phaseNumber =
    if earlyPhase then 1
    else if middayPhase then 2
    else if latePhase then 3
    else 0;

def phaseChanged = phaseNumber != phaseNumber[1];

def phaseVolumeSum = CompoundValue(1,
    if !tradeWindow then 0
    else if phaseChanged then volume
    else phaseVolumeSum[1] + volume,
    0);

def phaseVolumeBars = CompoundValue(1,
    if !tradeWindow then 0
    else if phaseChanged then 1
    else phaseVolumeBars[1] + 1,
    0);

def completedPriorPhaseAverage = CompoundValue(1,
    if zoneStartBar then Double.NaN
    else if phaseChanged and phaseNumber > 1 and phaseVolumeBars[1] > 0
         then phaseVolumeSum[1] / phaseVolumeBars[1]
    else completedPriorPhaseAverage[1],
    Double.NaN);

def developingPhaseAverage =
    if phaseVolumeBars > MinimumPhaseBaselineBars
    then (phaseVolumeSum - volume) / (phaseVolumeBars - 1)
    else if phaseNumber == 1 then openingAverageVolume
    else if !IsNaN(completedPriorPhaseAverage)
         then completedPriorPhaseAverage
    else openingAverageVolume;

def trailingVolumeAverage = Average(volume[1], TrailingVolumeLength);

def selectedVolumeBaseline =
    if VolumeBaseline == VolumeBaseline.SESSION_PHASE
    then developingPhaseAverage
    else trailingVolumeAverage;

def relativeVolume =
    if tradeWindow and selectedVolumeBaseline > 0
    then volume / selectedVolumeBaseline
    else Double.NaN;

def volumeQualified =
    !RequireVolumeConfirmation or
    (!IsNaN(relativeVolume) and relativeVolume >= VolumeMultiplier);

def quietTape =
    tradeWindow and
    rangeQualified and
    !IsNaN(relativeVolume) and
    relativeVolume < QuietTapeRelativeVolume;

#=============================================================================
# TARGETS AND 9 EMA
#=============================================================================

def bullTarget1Level = morningHigh + rangeSize;
def bullTarget2Level = morningHigh + 2 * rangeSize;
def bearTarget1Level = morningLow - rangeSize;
def bearTarget2Level = morningLow - 2 * rangeSize;

def emaTrail = MovingAverage(AverageType.EXPONENTIAL, close, EMALength);
def entryLimit = Max(1, MaximumEntriesPerDirection);

#=============================================================================
# BREAKOUTS, STATE, AND CONTROLLED RE-ENTRY
#=============================================================================

def rawBullishBreakout =
    isFiveMinute and
    tradeWindow and
    rangeQualified and
    close[1] <= morningHigh[1] + BreakoutBuffer and
    close > morningHigh + BreakoutBuffer and
    volumeQualified;

def rawBearishBreakout =
    isFiveMinute and
    tradeWindow and
    rangeQualified and
    close[1] >= morningLow[1] - BreakoutBuffer and
    close < morningLow - BreakoutBuffer and
    volumeQualified;

def tradeState = {default FLAT, BULL, BEAR};
def bullEntries;
def bearEntries;

def BullishBreakout =
    rawBullishBreakout and
    tradeState[1] == tradeState.FLAT and
    bullEntries[1] < entryLimit;

def BearishBreakout =
    rawBearishBreakout and
    tradeState[1] == tradeState.FLAT and
    bearEntries[1] < entryLimit;

def bullEMAExit =
    tradeWindow and
    tradeState[1] == tradeState.BULL and
    close < emaTrail;

def bearEMAExit =
    tradeWindow and
    tradeState[1] == tradeState.BEAR and
    close > emaTrail;

tradeState =
    if zoneStartBar or !tradeWindow then tradeState.FLAT
    else if bullEMAExit or bearEMAExit then tradeState.FLAT
    else if BullishBreakout then tradeState.BULL
    else if BearishBreakout then tradeState.BEAR
    else tradeState[1];

if zoneStartBar {
    bullEntries = 0;
} else if BullishBreakout {
    bullEntries = bullEntries[1] + 1;
} else {
    bullEntries = bullEntries[1];
}

if zoneStartBar {
    bearEntries = 0;
} else if BearishBreakout {
    bearEntries = bearEntries[1] + 1;
} else {
    bearEntries = bearEntries[1];
}

#=============================================================================
# ONE-SHOT TARGET EVENTS FOR EACH ACTIVE TRADE
#=============================================================================

def bullT1Hit;
def bullT2Hit;
def bearT1Hit;
def bearT2Hit;

if BullishBreakout {
    bullT1Hit = high >= bullTarget1Level;
} else if tradeState == tradeState.BULL {
    bullT1Hit = bullT1Hit[1] or high >= bullTarget1Level;
} else {
    bullT1Hit = 0;
}

if BullishBreakout {
    bullT2Hit = high >= bullTarget2Level;
} else if tradeState == tradeState.BULL {
    bullT2Hit = bullT2Hit[1] or high >= bullTarget2Level;
} else {
    bullT2Hit = 0;
}

if BearishBreakout {
    bearT1Hit = low <= bearTarget1Level;
} else if tradeState == tradeState.BEAR {
    bearT1Hit = bearT1Hit[1] or low <= bearTarget1Level;
} else {
    bearT1Hit = 0;
}

if BearishBreakout {
    bearT2Hit = low <= bearTarget2Level;
} else if tradeState == tradeState.BEAR {
    bearT2Hit = bearT2Hit[1] or low <= bearTarget2Level;
} else {
    bearT2Hit = 0;
}

def bullT1Event =
    tradeWindow and
    (tradeState == tradeState.BULL or tradeState[1] == tradeState.BULL) and
    high >= bullTarget1Level and
    !bullT1Hit[1];

def bullT2Event =
    tradeWindow and
    (tradeState == tradeState.BULL or tradeState[1] == tradeState.BULL) and
    high >= bullTarget2Level and
    !bullT2Hit[1];

def bearT1Event =
    tradeWindow and
    (tradeState == tradeState.BEAR or tradeState[1] == tradeState.BEAR) and
    low <= bearTarget1Level and
    !bearT1Hit[1];

def bearT2Event =
    tradeWindow and
    (tradeState == tradeState.BEAR or tradeState[1] == tradeState.BEAR) and
    low <= bearTarget2Level and
    !bearT2Hit[1];

#=============================================================================
# PLOTS
#=============================================================================

plot RangeHigh =
    if ShowRangeLines and rangeReady and postZone
    then morningHigh
    else Double.NaN;

plot RangeLow =
    if ShowRangeLines and rangeReady and postZone
    then morningLow
    else Double.NaN;

RangeHigh.SetDefaultColor(Color.GRAY);
RangeHigh.SetStyle(Curve.SHORT_DASH);
RangeHigh.SetLineWeight(1);
RangeLow.SetDefaultColor(Color.GRAY);
RangeLow.SetStyle(Curve.SHORT_DASH);
RangeLow.SetLineWeight(1);

plot BullTarget1 =
    if ShowTargets and rangeQualified and tradeWindow
    then bullTarget1Level
    else Double.NaN;

plot BullTarget2 =
    if ShowTargets and rangeQualified and tradeWindow
    then bullTarget2Level
    else Double.NaN;

plot BearTarget1 =
    if ShowTargets and rangeQualified and tradeWindow
    then bearTarget1Level
    else Double.NaN;

plot BearTarget2 =
    if ShowTargets and rangeQualified and tradeWindow
    then bearTarget2Level
    else Double.NaN;

BullTarget1.SetDefaultColor(Color.GREEN);
BullTarget1.SetStyle(Curve.MEDIUM_DASH);
BullTarget1.SetLineWeight(2);
BullTarget2.SetDefaultColor(Color.DARK_GREEN);
BullTarget2.SetStyle(Curve.FIRM);
BullTarget2.SetLineWeight(2);
BearTarget1.SetDefaultColor(Color.RED);
BearTarget1.SetStyle(Curve.MEDIUM_DASH);
BearTarget1.SetLineWeight(2);
BearTarget2.SetDefaultColor(Color.DARK_RED);
BearTarget2.SetStyle(Curve.FIRM);
BearTarget2.SetLineWeight(2);

plot BullTrailEMA =
    if ShowActiveEMA and tradeState == tradeState.BULL
    then emaTrail
    else Double.NaN;

plot BearTrailEMA =
    if ShowActiveEMA and tradeState == tradeState.BEAR
    then emaTrail
    else Double.NaN;

BullTrailEMA.SetDefaultColor(Color.LIME);
BullTrailEMA.SetLineWeight(2);
BearTrailEMA.SetDefaultColor(Color.MAGENTA);
BearTrailEMA.SetLineWeight(2);

plot CallSignal =
    if ShowSignalArrows and BullishBreakout
    then low - 2 * TickSize()
    else Double.NaN;

plot PutSignal =
    if ShowSignalArrows and BearishBreakout
    then high + 2 * TickSize()
    else Double.NaN;

CallSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
CallSignal.SetDefaultColor(Color.LIME);
CallSignal.SetLineWeight(3);
PutSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
PutSignal.SetDefaultColor(Color.MAGENTA);
PutSignal.SetLineWeight(3);

AddChartBubble(
    ShowSignalBubbles and BullishBreakout,
    low,
    "CALL | RVOL " + AsText(Round(relativeVolume, 2)),
    Color.GREEN,
    no
);

AddChartBubble(
    ShowSignalBubbles and BearishBreakout,
    high,
    "PUT | RVOL " + AsText(Round(relativeVolume, 2)),
    Color.MAGENTA,
    yes
);

AddVerticalLine(
    ShowCutoffLine and cutoffEvent,
    "3:30 PM 0DTE CUT-OFF",
    Color.RED,
    Curve.SHORT_DASH
);

#=============================================================================
# BACKGROUND -- ACTIVE TRADE ALWAYS HAS PRIORITY OVER QUIET-TAPE STATUS
#=============================================================================

AssignBackgroundColor(
    if !ShowBackground then Color.CURRENT
    else if isTargetZone then Color.DARK_GRAY
    else if tradeState == tradeState.BULL then CreateColor(15, 50, 15)
    else if tradeState == tradeState.BEAR then CreateColor(40, 15, 50)
    else if rangeLockout and tradeWindow then CreateColor(60, 15, 15)
    else if quietTape then CreateColor(50, 38, 10)
    else Color.CURRENT
);

#=============================================================================
# VERTICALLY STACKED DASHBOARD
#=============================================================================

AddLabel(
    ShowDashboard and !isFiveMinute,
    "WARNING: THIS STUDY REQUIRES A 5-MINUTE CHART",
    Color.RED,
    location = Location.TOP_LEFT,
    size = FontSize.SMALL,
    "row ownership" = yes
);

AddLabel(
    ShowDashboard and isFiveMinute,
    if beforeOpen then "PREMARKET: WAIT FOR 9:30 AM ET"
    else if isTargetZone then "PHASE: SOWING | BUILDING 9:30-10:15 RANGE"
    else if afterCutoff then "HARVEST OVER: FLATTEN 0DTE POSITIONS"
    else if afterClose then "SESSION CLOSED"
    else if tradeWindow and !rangeReady then "LOCKOUT: NO VALID MORNING RANGE"
    else if rangeLockout and tradeWindow then "LOCKOUT: MORNING RANGE TOO SMALL"
    else if tradeState == tradeState.BULL then "HARVESTING: ACTIVE CALLS | TRAIL 9 EMA"
    else if tradeState == tradeState.BEAR then "HARVESTING: ACTIVE PUTS | TRAIL 9 EMA"
    else if quietTape then "WAIT: QUIET TAPE | BREAKOUT VOLUME NOT READY"
    else if tradeWindow then "WATCHING: WAIT FOR A CONFIRMED RANGE BREAK"
    else "OUTSIDE ACTIVE WINDOW",
    if beforeOpen then Color.GRAY
    else if isTargetZone then Color.YELLOW
    else if afterCutoff then Color.RED
    else if rangeLockout and tradeWindow then Color.LIGHT_RED
    else if tradeState == tradeState.BULL then Color.GREEN
    else if tradeState == tradeState.BEAR then Color.MAGENTA
    else if quietTape then Color.LIGHT_ORANGE
    else Color.WHITE,
    location = Location.TOP_LEFT,
    size = FontSize.SMALL,
    "row ownership" = yes
);

AddLabel(
    ShowDashboard and isFiveMinute and rangeReady and postZone,
    "AM RANGE: $" + AsText(Round(rangeSize, 2)) +
    " | REQUIRED: $" + AsText(Round(requiredRange, 2)) +
    " | CALLS: " + AsText(bullEntries) + "/" + AsText(entryLimit) +
    " | PUTS: " + AsText(bearEntries) + "/" + AsText(entryLimit),
    if rangeQualified then Color.GREEN else Color.RED,
    location = Location.TOP_LEFT,
    size = FontSize.SMALL,
    "row ownership" = yes
);

AddLabel(
    ShowDashboard and isFiveMinute and tradeWindow and !IsNaN(relativeVolume),
    "PHASE RVOL: " + AsText(Round(relativeVolume, 2)) +
    "x | BREAKOUT NEEDS: " + AsText(Round(VolumeMultiplier, 2)) + "x",
    if relativeVolume >= VolumeMultiplier then Color.GREEN
    else if relativeVolume < QuietTapeRelativeVolume then Color.LIGHT_ORANGE
    else Color.WHITE,
    location = Location.TOP_LEFT,
    size = FontSize.SMALL,
    "row ownership" = yes
);

#=============================================================================
# ALERTS
#=============================================================================

Alert(
    EnableAlerts and BullishBreakout,
    "SPY 0DTE: CALL breakout confirmed",
    Alert.BAR,
    Sound.Chimes
);

Alert(
    EnableAlerts and BearishBreakout,
    "SPY 0DTE: PUT breakdown confirmed",
    Alert.BAR,
    Sound.Bell
);

Alert(
    EnableAlerts and bullEMAExit,
    "SPY 0DTE: CALL trend closed below the 9 EMA",
    Alert.BAR,
    Sound.Ding
);

Alert(
    EnableAlerts and bearEMAExit,
    "SPY 0DTE: PUT trend closed above the 9 EMA",
    Alert.BAR,
    Sound.Ding
);

Alert(
    EnableTargetAlerts and bullT1Event,
    "SPY 0DTE: Bull Target 1 reached",
    Alert.BAR,
    Sound.Ding
);

Alert(
    EnableTargetAlerts and bullT2Event,
    "SPY 0DTE: Bull Target 2 reached",
    Alert.BAR,
    Sound.Ring
);

Alert(
    EnableTargetAlerts and bearT1Event,
    "SPY 0DTE: Bear Target 1 reached",
    Alert.BAR,
    Sound.Ding
);

Alert(
    EnableTargetAlerts and bearT2Event,
    "SPY 0DTE: Bear Target 2 reached",
    Alert.BAR,
    Sound.Ring
);

Alert(
    EnableAlerts and cutoffEvent,
    "3:30 PM ET cutoff reached: flatten all 0DTE positions",
    Alert.BAR,
    Sound.Ring
);
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
970 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top