# HTF Loopback Support/Resistance Zones (Completed HTF Bars)
#============================================================
# Clark2001
# Script logic and optimization aided by ChatGPT (OpenAI)
# ============================================================
# - Levels come from the last N COMPLETED higher timeframe bars
# - No pivots: pure loopback HTF range
# - Multi-level R1..R5 and S1..S5
# - Strength via touch counts, strongest level highlighted
# ============================================================
declare upper;
# ---------- Inputs ----------
input higherTF = AggregationPeriod.FOUR_HOURS; # HTF (HOUR, FOUR_HOURS, DAY, etc.)
input loopbackHTFBars = 5; # How many completed HTF bars to use (1–10 recommended)
input maxLevelsEachSide = 5; # Up to 5 resistance + 5 support levels
input zoneSize = 0.5; # Zone thickness in price units (full height)
input showResistance = yes;
input showSupport = yes;
input showLines = yes;
# Strength settings
input touchLookbackBars = 500; # How many base-TF bars to scan for touches
input touchToleranceTicks = 4; # Distance from level in ticks to count as a touch
input strongMinTouches = 3; # Minimum touches to qualify as "strong"
# Bubble controls
input showLevelLabels = yes; # Turn touch-count bubbles on/off
# ---------- Global Colors ----------
DefineGlobalColor("ResZone", Color.DARK_GRAY);
DefineGlobalColor("SupZone", Color.DARK_GRAY);
DefineGlobalColor("ResLine", Color.GREEN);
DefineGlobalColor("SupLine", Color.RED);
DefineGlobalColor("StrongResZone", Color.YELLOW);
DefineGlobalColor("StrongSupZone", Color.YELLOW);
DefineGlobalColor("StrongResLine", Color.WHITE);
DefineGlobalColor("StrongSupLine", Color.WHITE);
# ---------- Multi-timeframe Core ----------
def cap = GetAggregationPeriod();
def aggTF = higherTF; # force all charts to use the same HTF series
# HTF series
def HtfH = High(period = aggTF);
def HtfL = Low(period = aggTF);
def HtfO = Open(period = aggTF);
def bn = BarNumber();
def lastBar = HighestAll(if !IsNaN(close) then bn else 0);
# Detect new HTF bar (when HTF open changes)
def newHTF = HtfO != HtfO[1];
# Running high/low inside the CURRENT HTF bar
def curH = if newHTF then HtfH else Max(curH[1], HtfH);
def curL = if newHTF then HtfL else Min(curL[1], HtfL);
# High/low of the JUST COMPLETED HTF bar
def compH = if newHTF then curH[1] else Double.NaN;
def compL = if newHTF then curL[1] else Double.NaN;
def newComp = !IsNaN(compH) and !IsNaN(compL);
# ---------- Stack last up to 10 completed HTF highs/lows ----------
# h1/l1 = most recent completed, h10/l10 = oldest in memory
def h1 = if newComp then compH else h1[1];
def h2 = if newComp then h1[1] else h2[1];
def h3 = if newComp then h2[1] else h3[1];
def h4 = if newComp then h3[1] else h4[1];
def h5 = if newComp then h4[1] else h5[1];
def h6 = if newComp then h5[1] else h6[1];
def h7 = if newComp then h6[1] else h7[1];
def h8 = if newComp then h7[1] else h8[1];
def h9 = if newComp then h8[1] else h9[1];
def h10 = if newComp then h9[1] else h10[1];
def l1 = if newComp then compL else l1[1];
def l2 = if newComp then l1[1] else l2[1];
def l3 = if newComp then l2[1] else l3[1];
def l4 = if newComp then l3[1] else l4[1];
def l5 = if newComp then l4[1] else l5[1];
def l6 = if newComp then l5[1] else l6[1];
def l7 = if newComp then l6[1] else l7[1];
def l8 = if newComp then l7[1] else l8[1];
def l9 = if newComp then l8[1] else l9[1];
def l10 = if newComp then l9[1] else l10[1];
# Clamp loopback between 1 and 10
def lb = if loopbackHTFBars < 1 then 1 else if loopbackHTFBars > 10 then 10 else loopbackHTFBars;
# Range high over last lb completed HTF bars
def rangeHighNow =
if lb <= 1 then h1 else
if lb == 2 then Max(h1, h2) else
if lb == 3 then Max(Max(h1, h2), h3) else
if lb == 4 then Max(Max(Max(h1, h2), h3), h4) else
if lb == 5 then Max(Max(Max(Max(h1, h2), h3), h4), h5) else
if lb == 6 then Max(Max(Max(Max(Max(h1, h2), h3), h4), h5), h6) else
if lb == 7 then Max(Max(Max(Max(Max(Max(h1, h2), h3), h4), h5), h6), h7) else
if lb == 8 then Max(Max(Max(Max(Max(Max(Max(h1, h2), h3), h4), h5), h6), h7), h8) else
if lb == 9 then Max(Max(Max(Max(Max(Max(Max(Max(h1, h2), h3), h4), h5), h6), h7), h8), h9) else
Max(Max(Max(Max(Max(Max(Max(Max(Max(h1, h2), h3), h4), h5), h6), h7), h8), h9), h10);
# Range low over last lb completed HTF bars
def rangeLowNow =
if lb <= 1 then l1 else
if lb == 2 then Min(l1, l2) else
if lb == 3 then Min(Min(l1, l2), l3) else
if lb == 4 then Min(Min(Min(l1, l2), l3), l4) else
if lb == 5 then Min(Min(Min(Min(l1, l2), l3), l4), l5) else
if lb == 6 then Min(Min(Min(Min(Min(l1, l2), l3), l4), l5), l6) else
if lb == 7 then Min(Min(Min(Min(Min(Min(l1, l2), l3), l4), l5), l6), l7) else
if lb == 8 then Min(Min(Min(Min(Min(Min(Min(l1, l2), l3), l4), l5), l6), l7), l8) else
if lb == 9 then Min(Min(Min(Min(Min(Min(Min(Min(l1, l2), l3), l4), l5), l6), l7), l8), l9) else
Min(Min(Min(Min(Min(Min(Min(Min(Min(l1, l2), l3), l4), l5), l6), l7), l8), l9), l10);
# Freeze range at last bar
def rangeHigh = HighestAll(if bn == lastBar then rangeHighNow else Double.NaN);
def rangeLow = HighestAll(if bn == lastBar then rangeLowNow else Double.NaN);
def rangeSize = Max(rangeHigh - rangeLow, 0);
# ---------- Build Level Centers from Range ----------
def lvlCount = Max(1, maxLevelsEachSide);
def step = if rangeSize > 0 then rangeSize / (2 * lvlCount) else 0;
# Resistances from top downward
def r1Dyn = if maxLevelsEachSide >= 1 then rangeHigh - 0 * step else Double.NaN;
def r2Dyn = if maxLevelsEachSide >= 2 then rangeHigh - 1 * step else Double.NaN;
def r3Dyn = if maxLevelsEachSide >= 3 then rangeHigh - 2 * step else Double.NaN;
def r4Dyn = if maxLevelsEachSide >= 4 then rangeHigh - 3 * step else Double.NaN;
def r5Dyn = if maxLevelsEachSide >= 5 then rangeHigh - 4 * step else Double.NaN;
# Supports from bottom upward
def s1Dyn = if maxLevelsEachSide >= 1 then rangeLow + 0 * step else Double.NaN;
def s2Dyn = if maxLevelsEachSide >= 2 then rangeLow + 1 * step else Double.NaN;
def s3Dyn = if maxLevelsEachSide >= 3 then rangeLow + 2 * step else Double.NaN;
def s4Dyn = if maxLevelsEachSide >= 4 then rangeLow + 3 * step else Double.NaN;
def s5Dyn = if maxLevelsEachSide >= 5 then rangeLow + 4 * step else Double.NaN;
# Freeze level centers at last bar
def R1 = HighestAll(if bn == lastBar then r1Dyn else Double.NaN);
def R2 = HighestAll(if bn == lastBar then r2Dyn else Double.NaN);
def R3 = HighestAll(if bn == lastBar then r3Dyn else Double.NaN);
def R4 = HighestAll(if bn == lastBar then r4Dyn else Double.NaN);
def R5 = HighestAll(if bn == lastBar then r5Dyn else Double.NaN);
def S1 = HighestAll(if bn == lastBar then s1Dyn else Double.NaN);
def S2 = HighestAll(if bn == lastBar then s2Dyn else Double.NaN);
def S3 = HighestAll(if bn == lastBar then s3Dyn else Double.NaN);
def S4 = HighestAll(if bn == lastBar then s4Dyn else Double.NaN);
def S5 = HighestAll(if bn == lastBar then s5Dyn else Double.NaN);
# ---------- Zone Geometry ----------
def halfZ = zoneSize / 2;
def useR1 = showResistance and maxLevelsEachSide >= 1 and !IsNaN(R1);
def useR2 = showResistance and maxLevelsEachSide >= 2 and !IsNaN(R2);
def useR3 = showResistance and maxLevelsEachSide >= 3 and !IsNaN(R3);
def useR4 = showResistance and maxLevelsEachSide >= 4 and !IsNaN(R4);
def useR5 = showResistance and maxLevelsEachSide >= 5 and !IsNaN(R5);
def useS1 = showSupport and maxLevelsEachSide >= 1 and !IsNaN(S1);
def useS2 = showSupport and maxLevelsEachSide >= 2 and !IsNaN(S2);
def useS3 = showSupport and maxLevelsEachSide >= 3 and !IsNaN(S3);
def useS4 = showSupport and maxLevelsEachSide >= 4 and !IsNaN(S4);
def useS5 = showSupport and maxLevelsEachSide >= 5 and !IsNaN(S5);
def R1Top = R1 + halfZ;
def R1Bot = R1 - halfZ;
def R2Top = R2 + halfZ;
def R2Bot = R2 - halfZ;
def R3Top = R3 + halfZ;
def R3Bot = R3 - halfZ;
def R4Top = R4 + halfZ;
def R4Bot = R4 - halfZ;
def R5Top = R5 + halfZ;
def R5Bot = R5 - halfZ;
def S1Top = S1 + halfZ;
def S1Bot = S1 - halfZ;
def S2Top = S2 + halfZ;
def S2Bot = S2 - halfZ;
def S3Top = S3 + halfZ;
def S3Bot = S3 - halfZ;
def S4Top = S4 + halfZ;
def S4Bot = S4 - halfZ;
def S5Top = S5 + halfZ;
def S5Bot = S5 - halfZ;
# ---------- Strength: Touch Count per Level ----------
def tol = touchToleranceTicks * TickSize();
def nearR1 = if useR1 and AbsValue(close - R1) <= tol then 1 else 0;
def nearR2 = if useR2 and AbsValue(close - R2) <= tol then 1 else 0;
def nearR3 = if useR3 and AbsValue(close - R3) <= tol then 1 else 0;
def nearR4 = if useR4 and AbsValue(close - R4) <= tol then 1 else 0;
def nearR5 = if useR5 and AbsValue(close - R5) <= tol then 1 else 0;
def nearS1 = if useS1 and AbsValue(close - S1) <= tol then 1 else 0;
def nearS2 = if useS2 and AbsValue(close - S2) <= tol then 1 else 0;
def nearS3 = if useS3 and AbsValue(close - S3) <= tol then 1 else 0;
def nearS4 = if useS4 and AbsValue(close - S4) <= tol then 1 else 0;
def nearS5 = if useS5 and AbsValue(close - S5) <= tol then 1 else 0;
def rawR1Touches = Sum(nearR1, touchLookbackBars);
def rawR2Touches = Sum(nearR2, touchLookbackBars);
def rawR3Touches = Sum(nearR3, touchLookbackBars);
def rawR4Touches = Sum(nearR4, touchLookbackBars);
def rawR5Touches = Sum(nearR5, touchLookbackBars);
def rawS1Touches = Sum(nearS1, touchLookbackBars);
def rawS2Touches = Sum(nearS2, touchLookbackBars);
def rawS3Touches = Sum(nearS3, touchLookbackBars);
def rawS4Touches = Sum(nearS4, touchLookbackBars);
def rawS5Touches = Sum(nearS5, touchLookbackBars);
def R1Touches = HighestAll(if bn == lastBar then rawR1Touches else Double.NaN);
def R2Touches = HighestAll(if bn == lastBar then rawR2Touches else Double.NaN);
def R3Touches = HighestAll(if bn == lastBar then rawR3Touches else Double.NaN);
def R4Touches = HighestAll(if bn == lastBar then rawR4Touches else Double.NaN);
def R5Touches = HighestAll(if bn == lastBar then rawR5Touches else Double.NaN);
def S1Touches = HighestAll(if bn == lastBar then rawS1Touches else Double.NaN);
def S2Touches = HighestAll(if bn == lastBar then rawS2Touches else Double.NaN);
def S3Touches = HighestAll(if bn == lastBar then rawS3Touches else Double.NaN);
def S4Touches = HighestAll(if bn == lastBar then rawS4Touches else Double.NaN);
def S5Touches = HighestAll(if bn == lastBar then rawS5Touches else Double.NaN);
def maxRTouches = Max(Max(Max(Max(R1Touches, R2Touches), R3Touches), R4Touches), R5Touches);
def maxSTouches = Max(Max(Max(Max(S1Touches, S2Touches), S3Touches), S4Touches), S5Touches);
def strongR1 = useR1 and R1Touches == maxRTouches and maxRTouches >= strongMinTouches;
def strongR2 = useR2 and R2Touches == maxRTouches and maxRTouches >= strongMinTouches;
def strongR3 = useR3 and R3Touches == maxRTouches and maxRTouches >= strongMinTouches;
def strongR4 = useR4 and R4Touches == maxRTouches and maxRTouches >= strongMinTouches;
def strongR5 = useR5 and R5Touches == maxRTouches and maxRTouches >= strongMinTouches;
def strongS1 = useS1 and S1Touches == maxSTouches and maxSTouches >= strongMinTouches;
def strongS2 = useS2 and S2Touches == maxSTouches and maxSTouches >= strongMinTouches;
def strongS3 = useS3 and S3Touches == maxSTouches and maxSTouches >= strongMinTouches;
def strongS4 = useS4 and S4Touches == maxSTouches and maxSTouches >= strongMinTouches;
def strongS5 = useS5 and S5Touches == maxSTouches and maxSTouches >= strongMinTouches;
# ---------- Clouds ----------
AddCloud(if useR1 then R1Top else Double.NaN, if useR1 then R1Bot else Double.NaN,
GlobalColor("ResZone"), GlobalColor("ResZone"));
AddCloud(if useR2 then R2Top else Double.NaN, if useR2 then R2Bot else Double.NaN,
GlobalColor("ResZone"), GlobalColor("ResZone"));
AddCloud(if useR3 then R3Top else Double.NaN, if useR3 then R3Bot else Double.NaN,
GlobalColor("ResZone"), GlobalColor("ResZone"));
AddCloud(if useR4 then R4Top else Double.NaN, if useR4 then R4Bot else Double.NaN,
GlobalColor("ResZone"), GlobalColor("ResZone"));
AddCloud(if useR5 then R5Top else Double.NaN, if useR5 then R5Bot else Double.NaN,
GlobalColor("ResZone"), GlobalColor("ResZone"));
AddCloud(if useS1 then S1Top else Double.NaN, if useS1 then S1Bot else Double.NaN,
GlobalColor("SupZone"), GlobalColor("SupZone"));
AddCloud(if useS2 then S2Top else Double.NaN, if useS2 then S2Bot else Double.NaN,
GlobalColor("SupZone"), GlobalColor("SupZone"));
AddCloud(if useS3 then S3Top else Double.NaN, if useS3 then S3Bot else Double.NaN,
GlobalColor("SupZone"), GlobalColor("SupZone"));
AddCloud(if useS4 then S4Top else Double.NaN, if useS4 then S4Bot else Double.NaN,
GlobalColor("SupZone"), GlobalColor("SupZone"));
AddCloud(if useS5 then S5Top else Double.NaN, if useS5 then S5Bot else Double.NaN,
GlobalColor("SupZone"), GlobalColor("SupZone"));
# Strong overlays
AddCloud(if strongR1 then R1Top else Double.NaN, if strongR1 then R1Bot else Double.NaN,
GlobalColor("StrongResZone"), GlobalColor("StrongResZone"));
AddCloud(if strongR2 then R2Top else Double.NaN, if strongR2 then R2Bot else Double.NaN,
GlobalColor("StrongResZone"), GlobalColor("StrongResZone"));
AddCloud(if strongR3 then R3Top else Double.NaN, if strongR3 then R3Bot else Double.NaN,
GlobalColor("StrongResZone"), GlobalColor("StrongResZone"));
AddCloud(if strongR4 then R4Top else Double.NaN, if strongR4 then R4Bot else Double.NaN,
GlobalColor("StrongResZone"), GlobalColor("StrongResZone"));
AddCloud(if strongR5 then R5Top else Double.NaN, if strongR5 then R5Bot else Double.NaN,
GlobalColor("StrongResZone"), GlobalColor("StrongResZone"));
AddCloud(if strongS1 then S1Top else Double.NaN, if strongS1 then S1Bot else Double.NaN,
GlobalColor("StrongSupZone"), GlobalColor("StrongSupZone"));
AddCloud(if strongS2 then S2Top else Double.NaN, if strongS2 then S2Bot else Double.NaN,
GlobalColor("StrongSupZone"), GlobalColor("StrongSupZone"));
AddCloud(if strongS3 then S3Top else Double.NaN, if strongS3 then S3Bot else Double.NaN,
GlobalColor("StrongSupZone"), GlobalColor("StrongSupZone"));
AddCloud(if strongS4 then S4Top else Double.NaN, if strongS4 then S4Bot else Double.NaN,
GlobalColor("StrongSupZone"), GlobalColor("StrongSupZone"));
AddCloud(if strongS5 then S5Top else Double.NaN, if strongS5 then S5Bot else Double.NaN,
GlobalColor("StrongSupZone"), GlobalColor("StrongSupZone"));
# ---------- Center Lines ----------
plot R1Line = if showLines and useR1 then R1 else Double.NaN;
plot R2Line = if showLines and useR2 then R2 else Double.NaN;
plot R3Line = if showLines and useR3 then R3 else Double.NaN;
plot R4Line = if showLines and useR4 then R4 else Double.NaN;
plot R5Line = if showLines and useR5 then R5 else Double.NaN;
plot S1Line = if showLines and useS1 then S1 else Double.NaN;
plot S2Line = if showLines and useS2 then S2 else Double.NaN;
plot S3Line = if showLines and useS3 then S3 else Double.NaN;
plot S4Line = if showLines and useS4 then S4 else Double.NaN;
plot S5Line = if showLines and useS5 then S5 else Double.NaN;
R1Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
R2Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
R3Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
R4Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
R5Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
S1Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
S2Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
S3Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
S4Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
S5Line.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
R1Line.SetLineWeight(2);
R2Line.SetLineWeight(2);
R3Line.SetLineWeight(2);
R4Line.SetLineWeight(2);
R5Line.SetLineWeight(2);
S1Line.SetLineWeight(2);
S2Line.SetLineWeight(2);
S3Line.SetLineWeight(2);
S4Line.SetLineWeight(2);
S5Line.SetLineWeight(2);
R1Line.AssignValueColor(if strongR1 then GlobalColor("StrongResLine") else GlobalColor("ResLine"));
R2Line.AssignValueColor(if strongR2 then GlobalColor("StrongResLine") else GlobalColor("ResLine"));
R3Line.AssignValueColor(if strongR3 then GlobalColor("StrongResLine") else GlobalColor("ResLine"));
R4Line.AssignValueColor(if strongR4 then GlobalColor("StrongResLine") else GlobalColor("ResLine"));
R5Line.AssignValueColor(if strongR5 then GlobalColor("StrongResLine") else GlobalColor("ResLine"));
S1Line.AssignValueColor(if strongS1 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"));
S2Line.AssignValueColor(if strongS2 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"));
S3Line.AssignValueColor(if strongS3 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"));
S4Line.AssignValueColor(if strongS4 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"));
S5Line.AssignValueColor(if strongS5 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"));
# ---------- Level Labels ----------
AddChartBubble(showLevelLabels and useR1 and bn == lastBar,
R1,
"R1 (" + AsText(R1Touches) + ")",
if strongR1 then GlobalColor("StrongResLine") else GlobalColor("ResLine"),
yes);
AddChartBubble(showLevelLabels and useR2 and bn == lastBar,
R2,
"R2 (" + AsText(R2Touches) + ")",
if strongR2 then GlobalColor("StrongResLine") else GlobalColor("ResLine"),
yes);
AddChartBubble(showLevelLabels and useR3 and bn == lastBar,
R3,
"R3 (" + AsText(R3Touches) + ")",
if strongR3 then GlobalColor("StrongResLine") else GlobalColor("ResLine"),
yes);
AddChartBubble(showLevelLabels and useR4 and bn == lastBar,
R4,
"R4 (" + AsText(R4Touches) + ")",
if strongR4 then GlobalColor("StrongResLine") else GlobalColor("ResLine"),
yes);
AddChartBubble(showLevelLabels and useR5 and bn == lastBar,
R5,
"R5 (" + AsText(R5Touches) + ")",
if strongR5 then GlobalColor("StrongResLine") else GlobalColor("ResLine"),
yes);
AddChartBubble(showLevelLabels and useS1 and bn == lastBar,
S1,
"S1 (" + AsText(S1Touches) + ")",
if strongS1 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"),
no);
AddChartBubble(showLevelLabels and useS2 and bn == lastBar,
S2,
"S2 (" + AsText(S2Touches) + ")",
if strongS2 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"),
no);
AddChartBubble(showLevelLabels and useS3 and bn == lastBar,
S3,
"S3 (" + AsText(S3Touches) + ")",
if strongS3 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"),
no);
AddChartBubble(showLevelLabels and useS4 and bn == lastBar,
S4,
"S4 (" + AsText(S4Touches) + ")",
if strongS4 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"),
no);
AddChartBubble(showLevelLabels and useS5 and bn == lastBar,
S5,
"S5 (" + AsText(S5Touches) + ")",
if strongS5 then GlobalColor("StrongSupLine") else GlobalColor("SupLine"),
no);