# ════════════════════════════════════════════════════════════════════════════════════
# LS Traders Dynamic Index — Redesigned Edition
# Stateful color system: every line reports its own state at a glance
# v10 — Overblow signal as persistent dots on 50-line (shows extreme duration)
# ════════════════════════════════════════════════════════════════════════════════════
#
# COLOR LANGUAGE
# bright green : RSI above upper BB (extreme bullish blowout)
# olive : RSI > 70 inside bands (normal overbought)
# bright red : RSI below lower BB (extreme bearish blowout)
# orange : RSI < 30 inside bands (normal oversold)
# gray : neutral / mid-range
#
# ════════════════════════════════════════════════════════════════════════════════════
declare lower;
# ════════════════════════════════════════════════════════
# INPUTS — RSI
# ════════════════════════════════════════════════════════
input averageType = {default SMA, EMA};
input Period = 13;
input sm1 = 2; # Fast smoother (divergence)
input sm2 = 7; # Slow smoother (50-line bias)
# ════════════════════════════════════════════════════════
# INPUTS — Bollinger Bands of Raw RSI
# ════════════════════════════════════════════════════════
input BBlength = 34;
input BBsdMult = 1.62;
# ════════════════════════════════════════════════════════
# INPUTS — Divergence
# ════════════════════════════════════════════════════════
input divergenceLength = 14;
input divergenceType = {default regular, reverse};
# ════════════════════════════════════════════════════════
# INPUTS — Display Toggles
# ════════════════════════════════════════════════════════
input showSqueeze = yes; # Plum cloud, BB width < 16
input showPreSqueeze = yes; # Yellow cloud, BB width 15-20
input showCrossArrows = yes; # Cyan cross-against-bias arrows
input showVerticals = yes; # Vertical lines on BB cross events
input showOverblowDots = yes; # Persistent dots on 50-line during BB-breach extremes
input overblowOB_level = 75; # Red dots: RSI >= this AND > uBB
input overblowOS_level = 25; # Green dots: RSI <= this AND < lBB
# ════════════════════════════════════════════════════════
# GLOBAL COLORS
# ════════════════════════════════════════════════════════
DefineGlobalColor("Plum", CreateColor(221, 160, 221));
DefineGlobalColor("BlowoutUp", CreateColor(0, 255, 0));
DefineGlobalColor("BlowoutDown", CreateColor(255, 30, 30));
DefineGlobalColor("Overbought", CreateColor(65, 90, 75)); # olive
DefineGlobalColor("Oversold", CreateColor(40, 80, 80)); # orange
DefineGlobalColor("FastNeutral", CreateColor(130, 200, 130)); # dim green
DefineGlobalColor("SlowNeutral", CreateColor(200, 140, 60)); # dim orange
DefineGlobalColor("ChopBlue", CreateColor(0, 150, 200));
DefineGlobalColor("MidUp", CreateColor(50, 220, 100));
DefineGlobalColor("MidDown", CreateColor(220, 60, 60));
# ════════════════════════════════════════════════════════
# CORE CALCULATIONS
# ════════════════════════════════════════════════════════
def RegRSI = reference RSI(Period);
def smRSI1 = if averageType == averageType.SMA
then Average(RegRSI, sm1)
else ExpAverage(RegRSI, sm1);
def smRSI2 = if averageType == averageType.SMA
then Average(RegRSI, sm2)
else ExpAverage(RegRSI, sm2);
def BBmid = Average(RegRSI, BBlength);
def SDBB = StDev(RegRSI, BBlength);
def uBB = BBmid + BBsdMult * SDBB;
def lBB = BBmid - BBsdMult * SDBB;
# ════════════════════════════════════════════════════════
# STATE ENGINE — drives all coloring
# 1 = blowout up (RSI > uBB)
# 2 = overbought (RSI > 70 inside bands)
# -1 = blowout dn (RSI < lBB)
# -2 = oversold (RSI < 30 inside bands)
# 0 = neutral
# ════════════════════════════════════════════════════════
def state =
if RegRSI > uBB then 1
else if RegRSI > 70 then 2
else if RegRSI < lBB then -1
else if RegRSI < 30 then -2
else 0;
# ════════════════════════════════════════════════════════
# PLOTS — RSI LINES (state-colored)
# Fast line uses bright variants, slow line uses dimmer variants
# so they remain visually distinct at all times
# ════════════════════════════════════════════════════════
plot RSI1 = smRSI1;
RSI1.SetLineWeight(2);
RSI1.SetStyle(Curve.FIRM);
RSI1.HideBubble();
RSI1.HideTitle();
RSI1.AssignValueColor(
if state == 1 then GlobalColor("BlowoutUp")
else if state == 2 then GlobalColor("Overbought")
else if state == -1 then GlobalColor("BlowoutDown")
else if state == -2 then GlobalColor("Oversold")
else GlobalColor("FastNeutral")
);
plot RSI2 = smRSI2;
RSI2.SetLineWeight(3);
RSI2.SetStyle(Curve.FIRM);
RSI2.HideBubble();
RSI2.HideTitle();
RSI2.AssignValueColor(
if state == 1 then GlobalColor("BlowoutUp")
else if state == 2 then GlobalColor("Overbought")
else if state == -1 then GlobalColor("BlowoutDown")
else if state == -2 then GlobalColor("Oversold")
else GlobalColor("SlowNeutral")
);
# ════════════════════════════════════════════════════════
# PLOTS — BB MIDLINE (momentum-state colored)
# Bright lime when RSI rising and above mid (real strength)
# Bright red when RSI falling and below mid (real weakness)
# Dim variants when slope and side disagree (transition)
# ════════════════════════════════════════════════════════
plot BBmidline = BBmid;
BBmidline.SetLineWeight(3);
BBmidline.SetStyle(Curve.FIRM);
BBmidline.HideBubble();
BBmidline.HideTitle();
BBmidline.AssignValueColor(
if RegRSI > BBmid and BBmid >= BBmid[1] then GlobalColor("MidUp")
else if RegRSI < BBmid and BBmid <= BBmid[1] then GlobalColor("MidDown")
else if BBmid >= BBmid[1] then Color.DARK_GREEN
else Color.DARK_RED
);
# ════════════════════════════════════════════════════════
# PLOTS — BB UPPER/LOWER BANDS
# Brighten when RSI is breaching them, dim otherwise
# ════════════════════════════════════════════════════════
plot uBBline = uBB;
uBBline.SetStyle(Curve.LONG_DASH);
uBBline.SetLineWeight(2);
uBBline.HideBubble();
uBBline.HideTitle();
uBBline.AssignValueColor(
if state == 1 then GlobalColor("BlowoutUp") else GlobalColor("Plum")
);
plot lBBline = lBB;
lBBline.SetStyle(Curve.LONG_DASH);
lBBline.SetLineWeight(2);
lBBline.HideBubble();
lBBline.HideTitle();
lBBline.AssignValueColor(
if state == -1 then GlobalColor("BlowoutDown") else GlobalColor("Plum")
);
# ════════════════════════════════════════════════════════
# PLOTS — STATIC GRID
# ════════════════════════════════════════════════════════
plot OB = 68;
OB.SetStyle(Curve.SHORT_DASH);
OB.SetDefaultColor(CreateColor(80, 30, 30));
OB.SetLineWeight(1);
OB.HideTitle();
plot OS = 32;
OS.SetStyle(Curve.SHORT_DASH);
OS.SetDefaultColor(CreateColor(30, 80, 30));
OS.SetLineWeight(1);
OS.HideTitle();
# ════════════════════════════════════════════════════════
# 50 LINE — Chopband-style
# Orange in dead zone (40-60), trade-blue when committed
# Slope-aware: rises greenish, falls reddish at extremes
# ════════════════════════════════════════════════════════
plot ML = 50;
ML.SetStyle(Curve.LONG_DASH);
ML.SetLineWeight(2);
ML.HideTitle();
ML.AssignValueColor(
if smRSI2 >= 40 and smRSI2 <= 60 then Color.ORANGE
else if smRSI2 > 60 then GlobalColor("ChopBlue")
else GlobalColor("ChopBlue")
);
# ════════════════════════════════════════════════════════
# CLOUD — RSI2 vs BB midline (bias zone)
# ════════════════════════════════════════════════════════
AddCloud(smRSI2, BBmid,
CreateColor(0, 100, 50), CreateColor(100, 20, 20)
);
# ════════════════════════════════════════════════════════
# ARROWS — BB cross events (raw RSI)
# ════════════════════════════════════════════════════════
plot ArrowUp = if RegRSI crosses above lBB then lBB else Double.NaN;
ArrowUp.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
ArrowUp.SetLineWeight(4);
ArrowUp.SetDefaultColor(Color.WHITE);
ArrowUp.HideTitle();
ArrowUp.HideBubble();
plot ArrowDN = if RegRSI crosses below uBB then uBB else Double.NaN;
ArrowDN.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
ArrowDN.SetLineWeight(4);
ArrowDN.SetDefaultColor(Color.LIGHT_GRAY);
ArrowDN.HideTitle();
ArrowDN.HideBubble();
# ════════════════════════════════════════════════════════
# ARROWS — RSI cross against BB-midline bias
# ════════════════════════════════════════════════════════
plot ArrowUpST =
if showCrossArrows and RegRSI crosses above smRSI2 and smRSI2 < BBmid
then smRSI2 else Double.NaN;
ArrowUpST.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
ArrowUpST.SetLineWeight(3);
ArrowUpST.SetDefaultColor(Color.CYAN);
ArrowUpST.HideTitle();
ArrowUpST.HideBubble();
plot ArrowDNST =
if showCrossArrows and RegRSI crosses below smRSI2 and smRSI2 > BBmid
then smRSI2 else Double.NaN;
ArrowDNST.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
ArrowDNST.SetLineWeight(3);
ArrowDNST.SetDefaultColor(Color.CYAN);
ArrowDNST.HideTitle();
ArrowDNST.HideBubble();
# ════════════════════════════════════════════════════════
# OVERBLOW DOTS — Persistent dots on 50-line during extremes
# Red dots while raw RSI > upper BB AND >= overbought threshold
# Green dots while raw RSI < lower BB AND <= oversold threshold
# Dots persist for the full duration of the extreme — cluster size
# is a visual read on how long the overblow has held.
# ════════════════════════════════════════════════════════
def buyOverblow = RegRSI < lBB and RegRSI <= overblowOS_level;
def sellOverblow = RegRSI > uBB and RegRSI >= overblowOB_level;
plot OverblowBuyDot = if showOverblowDots and buyOverblow then 50 else Double.NaN;
OverblowBuyDot.SetPaintingStrategy(PaintingStrategy.LINE_VS_POINTS);
OverblowBuyDot.SetLineWeight(5);
OverblowBuyDot.SetDefaultColor(CreateColor(50, 255, 100));
OverblowBuyDot.HideTitle();
OverblowBuyDot.HideBubble();
plot OverblowSellDot = if showOverblowDots and sellOverblow then 50 else Double.NaN;
OverblowSellDot.SetPaintingStrategy(PaintingStrategy.LINE_VS_POINTS);
OverblowSellDot.SetLineWeight(5);
OverblowSellDot.SetDefaultColor(CreateColor(255, 50, 50));
OverblowSellDot.HideTitle();
OverblowSellDot.HideBubble();
# ════════════════════════════════════════════════════════
# VERTICAL LINES — BB cross events on smRSI1
# ════════════════════════════════════════════════════════
AddVerticalLine(
showVerticals and smRSI1 > lBB and smRSI1[1] <= lBB,
"↑", Color.GREEN, Curve.FIRM
);
AddVerticalLine(
showVerticals and smRSI1 < uBB and smRSI1[1] >= uBB,
"↓", Color.RED, Curve.FIRM
);
# ════════════════════════════════════════════════════════
# DIVERGENCE ENGINE — RSI1 (fast) vs Price
# regular : Price HH + RSI LH = bear | Price LL + RSI HL = bull
# reverse : Price LH + RSI HH = bear | Price HL + RSI LL = bull (hidden)
# Logic preserved verbatim from v6
# ════════════════════════════════════════════════════════
def K = smRSI1;
def pivotTop = K[1] == Highest(K, divergenceLength + 1) and K[1] > K[2];
def pivotBottom = K[1] == Lowest(K, divergenceLength + 1) and K[1] < K[2];
# ─── Bearish: track last pivot high ───
def hiBars;
def hiInd;
def hiPrice;
if pivotTop {
hiBars = 1;
hiInd = K[1];
hiPrice = Max(high[2], Max(high[1], high[0]));
} else {
hiBars = hiBars[1] + 1;
hiInd = hiInd[1];
hiPrice = hiPrice[1];
}
def xDownBars;
def xDowns;
if K < K[1] and K[1] >= K[2] {
xDownBars = 1;
xDowns = xDowns[1] + 1;
} else {
xDownBars = xDownBars[1] + 1;
xDowns = if pivotTop[1] then 0 else xDowns[1];
}
def bearCond;
switch (divergenceType) {
case regular:
bearCond =
K < K[1] and
high[1] == Highest(high, divergenceLength + 1) and
hiBars[1] > xDownBars[1] and
xDowns == 1 and
hiPrice[1] < high[1] and
hiInd[1] > K[1];
case reverse:
bearCond =
K < K[1] and
hiPrice[1] > high[1] and
hiInd[1] < K[1];
}
plot bearishd = if bearCond then K[1] else Double.NaN;
bearishd.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
bearishd.SetDefaultColor(Color.MAGENTA);
bearishd.SetLineWeight(3);
bearishd.HideTitle();
bearishd.HideBubble();
# Bearish trendline
def countBear = if bearCond[-1] then countBear[1] + 1 else countBear[1];
def recentBear = countBear == HighestAll(countBear);
def secHigh = HighestAll(if bearCond[-1] and recentBear then K else Double.NaN);
def FH_bar = HighestAll(if recentBear and bearCond[-1] and K == secHigh
then GetValue(BarNumber(), hiBars) else Double.NaN);
plot bearTrendline =
if recentBear and bearCond[-1] and K == secHigh then Max(K[1], K[0])
else if FH_bar == BarNumber() then K
else Double.NaN;
bearTrendline.EnableApproximation();
bearTrendline.SetDefaultColor(Color.MAGENTA);
bearTrendline.SetLineWeight(2);
bearTrendline.SetStyle(Curve.SHORT_DASH);
bearTrendline.HideBubble();
bearTrendline.HideTitle();
# ─── Bullish: track last pivot low ───
def loBars;
def loInd;
def loPrice;
if pivotBottom {
loBars = 1;
loInd = K[1];
loPrice = Min(low[2], Min(low[1], low[0]));
} else {
loBars = loBars[1] + 1;
loInd = loInd[1];
loPrice = loPrice[1];
}
def xUpBars;
def xUps;
if K > K[1] and K[1] <= K[2] {
xUpBars = 1;
xUps = xUps[1] + 1;
} else {
xUpBars = xUpBars[1] + 1;
xUps = if pivotBottom[1] then 0 else xUps[1];
}
def bullCond;
switch (divergenceType) {
case regular:
bullCond =
K > K[1] and
low[1] == Lowest(low, divergenceLength + 1) and
loBars[1] > xUpBars[1] and
xUps == 1 and
loPrice[1] > low[1] and
loInd[1] < K[1];
case reverse:
bullCond =
K > K[1] and
loPrice[1] < low[1] and
loInd[1] > K[1];
}
plot bullishd = if bullCond then K[1] else Double.NaN;
bullishd.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
bullishd.SetDefaultColor(Color.YELLOW);
bullishd.SetLineWeight(3);
bullishd.HideTitle();
bullishd.HideBubble();
# Bullish trendline
def countBull = if bullCond[-1] then countBull[1] + 1 else countBull[1];
def recentBull = countBull == HighestAll(countBull);
def secLow = HighestAll(if bullCond[-1] and recentBull then K else Double.NaN);
def FL_bar = HighestAll(if recentBull and bullCond[-1] and K == secLow
then GetValue(BarNumber(), loBars) else Double.NaN);
plot bullTrendline =
if recentBull and bullCond[-1] and K == secLow then Min(K[1], K[0])
else if FL_bar == BarNumber() then K[0]
else Double.NaN;
bullTrendline.EnableApproximation();
bullTrendline.SetDefaultColor(Color.YELLOW);
bullTrendline.SetLineWeight(2);
bullTrendline.SetStyle(Curve.SHORT_DASH);
bullTrendline.HideBubble();
bullTrendline.HideTitle();
# ════════════════════════════════════════════════════════
# SQUEEZE & PRE-SQUEEZE CLOUDS
# ════════════════════════════════════════════════════════
def bbWidth = uBB - lBB;
def bbSqueeze = bbWidth < 16;
def bbPreSqueeze = bbWidth >= 15 and bbWidth < 20;
AddCloud(
if showSqueeze and bbSqueeze then uBB else Double.NaN,
if showSqueeze and bbSqueeze then lBB else Double.NaN,
GlobalColor("Plum"), GlobalColor("Plum")
);
AddCloud(
if showPreSqueeze and bbPreSqueeze then uBB else Double.NaN,
if showPreSqueeze and bbPreSqueeze then lBB else Double.NaN,
Color.YELLOW, Color.YELLOW
);
# ════════════════════════════════════════════════════════
# MOMENTUM DISTANCES (defs only — for scans/strategy use)
# ════════════════════════════════════════════════════════
def distToBB_Lower = smRSI2 - smRSI1;
def distToBB_Lower_3P = smRSI2[3] - smRSI1[3];
def distToBB_Lower_1P = smRSI2[1] - smRSI1[1];
def distToBB_Upper = smRSI1 - smRSI2;
def distToBB_Upper_3P = smRSI1[3] - smRSI2[3];
def distToBB_Upper_1P = smRSI1[1] - smRSI2[1];
def pctChange3Lower = (distToBB_Lower - distToBB_Lower_3P) / distToBB_Lower_3P * 100;
def pctChange1Lower = (distToBB_Lower - distToBB_Lower_1P) / distToBB_Lower_1P * 100;
def pctChange3Upper = (distToBB_Upper - distToBB_Upper_3P) / distToBB_Upper_3P * 100;
def pctChange1Upper = (distToBB_Upper - distToBB_Upper_1P) / distToBB_Upper_1P * 100;
# === END ===