// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=6
indicator("EMA + Supertrend + OBV", shorttitle="EMA + Supertrend + OBV", overlay=true, max_labels_count=50)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// INPUTS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i_emaFast = input.int(21, "Fast EMA", minval=1, group="⚙ EMA Stack")
i_emaMid = input.int(50, "Mid EMA", minval=1, group="⚙ EMA Stack")
i_emaSlow = input.int(200, "Slow EMA", minval=1, group="⚙ EMA Stack")
i_emaFill = input.bool(true, "Show ribbon fill", group="⚙ EMA Stack")
i_stFactor = input.float(3.0, "Factor", minval=0.5, step=0.1, group="⚙ Supertrend")
i_stAtr = input.int(10, "ATR Period", minval=1, group="⚙ Supertrend")
i_stShow = input.bool(true, "Show Supertrend line", group="⚙ Supertrend")
i_vsAtrLen = input.int(20, "ATR Period", minval=1, group="⚙ Vol Stop")
i_vsMult = input.float(3.25, "ATR Multiplier", minval=0.5, step=0.05, group="⚙ Vol Stop")
i_vsShow = input.bool(true, "Show Vol Stop line", group="⚙ Vol Stop")
i_obvMaLen = input.int(20, "OBV MA Length", minval=2, group="⚙ OBV")
i_obvLook = input.int(5, "Slope lookback", minval=1, group="⚙ OBV")
i_volAtrLen = input.int(14, "ATR Length", minval=1, group="⚙ Volatility")
i_volLook = input.int(100, "Percentile Lookback", minval=10, group="⚙ Volatility")
i_discLow = input.float(1.00, "Discount — Low Vol", minval=0.0, maxval=1.5, step=0.05, group="⚙ Volatility")
i_discNormal = input.float(1.00, "Discount — Normal Vol", minval=0.0, maxval=1.5, step=0.05, group="⚙ Volatility")
i_discElevated = input.float(0.75, "Discount — Elevated Vol", minval=0.0, maxval=1.5, step=0.05, group="⚙ Volatility")
i_discExtreme = input.float(0.50, "Discount — Extreme Vol", minval=0.0, maxval=1.5, step=0.05, group="⚙ Volatility")
i_tf1 = input.timeframe("60", "Timeframe 1", group="📊 MTF Heatmap")
i_tf2 = input.timeframe("1D", "Timeframe 2", group="📊 MTF Heatmap")
i_tf3 = input.timeframe("1W", "Timeframe 3", group="📊 MTF Heatmap")
i_tf4 = input.timeframe("1M", "Timeframe 4", group="📊 MTF Heatmap")
i_showHM = input.bool(true, "Show heatmap", group="📊 MTF Heatmap")
i_netPctLook = input.int(100, "Net Score Percentile Lookback", minval=10, group="📊 MTF Heatmap")
i_hmPos = input.string("Middle Right", "Position", options=["Top Right", "Top Left", "Bottom Right", "Bottom Left", "Middle Right", "Middle Left"], group="📊 MTF Heatmap")
i_hmSize = input.string("Normal", "Text size", options=["Tiny", "Small", "Normal", "Large", "Huge"], group="📊 MTF Heatmap")
i_showCross = input.bool(true, "Show GC / DC labels", group="📊 Chart")
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// CORE CALCULATIONS (evaluated per-TF via request.security)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// --- EMA ---
emaF = ta.ema(close, i_emaFast)
emaM = ta.ema(close, i_emaMid)
emaS = ta.ema(close, i_emaSlow)
bullStack = emaF > emaM and emaM > emaS
bearStack = emaF < emaM and emaM < emaS
pAboveAll = close > emaF and close > emaM and close > emaS
pBelowAll = close < emaF and close < emaM and close < emaS
goldenCross = ta.crossover(emaM, emaS)
deathCross = ta.crossunder(emaM, emaS)
emaScore = bullStack and pAboveAll ? 2 : bullStack ? 1 : bearStack and pBelowAll ? -2 : bearStack ? -1 : close > emaS ? 1 : close < emaS ? -1 : 0
pVs200 = close > emaS ? 1 : -1
// --- Supertrend ---
[stValue, stDir] = ta.supertrend(i_stFactor, i_stAtr)
stBull = stDir == -1
stFlipBull = stBull and not stBull[1]
stFlipBear = not stBull and stBull[1]
stScore = stBull ? 1 : -1
// --- Vol Stop (Chandelier-style ATR trailing stop, period 20 / mult 3.25 by default) ---
vsAtr = i_vsMult * ta.atr(i_vsAtrLen)
vsLongStop = ta.highest(high, i_vsAtrLen) - vsAtr
vsLongStopPrev = nz(vsLongStop[1], vsLongStop)
vsLongStop := close[1] > vsLongStopPrev ? math.max(vsLongStop, vsLongStopPrev) : vsLongStop
vsShortStop = ta.lowest(low, i_vsAtrLen) + vsAtr
vsShortStopPrev = nz(vsShortStop[1], vsShortStop)
vsShortStop := close[1] < vsShortStopPrev ? math.min(vsShortStop, vsShortStopPrev) : vsShortStop
var int vsDir = 1
vsDir := close > vsShortStopPrev ? 1 : close < vsLongStopPrev ? -1 : vsDir
vsFlipBull = vsDir == 1 and vsDir[1] == -1
vsFlipBear = vsDir == -1 and vsDir[1] == 1
vsScore = vsDir
// --- Heikin-Ashi Candle Color ---
haClose = (open + high + low + close) / 4
var float haOpen = na
haOpen := na(haOpen[1]) ? (open + close) / 2 : (haOpen[1] + haClose[1]) / 2
haBull = haClose > haOpen
haScore = haBull ? 1 : -1
// --- OBV (ta.cum — MTF safe) ---
obvRaw = ta.cum(ta.change(close) > 0 ? volume : ta.change(close) < 0 ? -volume : 0.0)
obvMa = ta.sma(obvRaw, i_obvMaLen)
obvAbv = obvRaw > obvMa
obvSlope = ta.change(obvMa, i_obvLook)
obvRise = obvSlope > 0
obvFall = obvSlope < 0
obvScore = obvAbv and obvRise ? 1 : not obvAbv and obvFall ? -1 : 0
// Extra data for detailed table
obvAbvInt = obvAbv ? 1 : 0
obvSlopeN = obvRise ? 1 : obvFall ? -1 : 0
// --- Volatility (ATR percentile regime) ---
// Ranks current ATR against its own trailing history (0-100). Low percentile = calm/favorable,
// high percentile = turbulent/unfavorable for trend-confluence purposes.
atrVal = ta.atr(i_volAtrLen)
atrPct = ta.percentrank(atrVal, i_volLook)
volScore = atrPct >= 75 ? -2 : atrPct >= 50 ? -1 : atrPct >= 25 ? 1 : 2 // Extreme/Elevated negative, Normal/Low positive
atrPctR = math.round(atrPct)
// Confidence discount multiplier — same regime buckets shown in the table
volMult(pct) =>
pct >= 75 ? i_discExtreme : pct >= 50 ? i_discElevated : pct >= 25 ? i_discNormal : i_discLow
discCurrent = volMult(atrPctR)
// --- Totals ---
totalScore = emaScore + stScore + obvScore + vsScore + haScore
confScore = totalScore * discCurrent // volatility-adjusted confluence score
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MTF DATA (6 values per timeframe)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[e1, s1, o1, p1, oa1, os1, v1, ap1, vs1, ha1] = request.security(syminfo.tickerid, i_tf1, [emaScore, stScore, obvScore, pVs200, obvAbvInt, obvSlopeN, volScore, atrPctR, vsScore, haScore], barmerge.gaps_off, barmerge.lookahead_off)
[e2, s2, o2, p2, oa2, os2, v2, ap2, vs2, ha2] = request.security(syminfo.tickerid, i_tf2, [emaScore, stScore, obvScore, pVs200, obvAbvInt, obvSlopeN, volScore, atrPctR, vsScore, haScore], barmerge.gaps_off, barmerge.lookahead_off)
[e3, s3, o3, p3, oa3, os3, v3, ap3, vs3, ha3] = request.security(syminfo.tickerid, i_tf3, [emaScore, stScore, obvScore, pVs200, obvAbvInt, obvSlopeN, volScore, atrPctR, vsScore, haScore], barmerge.gaps_off, barmerge.lookahead_off)
[e4, s4, o4, p4, oa4, os4, v4, ap4, vs4, ha4] = request.security(syminfo.tickerid, i_tf4, [emaScore, stScore, obvScore, pVs200, obvAbvInt, obvSlopeN, volScore, atrPctR, vsScore, haScore], barmerge.gaps_off, barmerge.lookahead_off)
t1 = math.round(e1 + s1 + o1 + vs1 + ha1)
t2 = math.round(e2 + s2 + o2 + vs2 + ha2)
t3 = math.round(e3 + s3 + o3 + vs3 + ha3)
t4 = math.round(e4 + s4 + o4 + vs4 + ha4)
// Net score across all timeframes — Daily and Weekly weighted 2x, each leg discounted by its own volatility regime
// Max possible per TF is now 6 (EMA ±2, Supertrend ±1, OBV ±1, Vol Stop ±1, HA Candle ±1)
// Max total: |t1|=6 (1H) + |t2|*2=12 (Daily) + |t3|*2=12 (Weekly) + |t4|=6 (Monthly) = 36
netScoreMax = 36
dT1 = t1 * volMult(ap1)
dT2 = t2 * volMult(ap2)
dT3 = t3 * volMult(ap3)
dT4 = t4 * volMult(ap4)
netScore = dT1 + (2 * dT2) + (2 * dT3) + dT4
netScorePct = int(math.round((netScore / netScoreMax) * 100))
// Net Score percentile — ranks today's reading against its own trailing history,
// which is a more honest "how extreme is this right now" read than % of theoretical max
netScorePctile = int(math.round(ta.percentrank(netScore, i_netPctLook)))
// Alignment
bullTFs = (t1 > 0 ? 1 : 0) + (t2 > 0 ? 1 : 0) + (t3 > 0 ? 1 : 0) + (t4 > 0 ? 1 : 0)
bearTFs = (t1 < 0 ? 1 : 0) + (t2 < 0 ? 1 : 0) + (t3 < 0 ? 1 : 0) + (t4 < 0 ? 1 : 0)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// REGIME
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Max total score is now 6 (EMA ±2, Supertrend ±1, OBV ±1, Vol Stop ±1, HA Candle ±1)
isStrongBull = confScore >= 5
isBull = confScore >= 1 and confScore < 5
isNeutral = confScore == 0
isBear = confScore <= -1 and confScore > -5
isStrongBear = confScore <= -5
var int prevC = 0
currC = isStrongBull ? 4 : isBull ? 3 : isNeutral ? 2 : isBear ? 1 : 0
trendChanged = currC != prevC
prevC := currC
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// HELPER FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Heatmap palette
cBS = #00E676 // strong bull
cBM = #4CAF50 // bull
cBW = #81C784 // weak bull
cN = #546E7A // neutral
cRW = #E57373 // weak bear
cRM = #F44336 // bear
cRS = #FF1744 // strong bear
hmCol(int score, int mx) =>
r = mx != 0 ? score / mx : 0.0
r >= 0.8 ? cBS : r >= 0.4 ? cBM : r > 0 ? cBW : r == 0 ? cN : r > -0.4 ? cRW : r > -0.8 ? cRM : cRS
// EMA stack label
emaLbl(int sc) =>
sc == 2 ? "FULL BULL ▲▲" : sc == 1 ? "BULLISH ▲" : sc == -1 ? "BEARISH ▼" : sc == -2 ? "FULL BEAR ▼▼" : "MIXED ◆"
// Supertrend label
stLbl(int sc) =>
sc == 1 ? "BULLISH ▲" : "BEARISH ▼"
// Price vs 200 label
p200Lbl(int sc) =>
sc == 1 ? "ABOVE ▲" : "BELOW ▼"
// OBV flow label
obvFlowLbl(int abv, int slope) =>
abv == 1 and slope == 1 ? "ACCUM ▲" :
abv == 0 and slope == -1 ? "DISTRIB ▼" :
abv == 1 and slope == 0 ? "ABOVE MA" :
abv == 0 and slope == 0 ? "BELOW MA" :
abv == 1 and slope == -1 ? "WEAKENING" :
abv == 0 and slope == 1 ? "BUILDING" :
"NEUTRAL"
obvFlowCol(int abv, int slope) =>
abv == 1 and slope == 1 ? cBS :
abv == 0 and slope == -1 ? cRS :
abv == 1 ? cBW :
abv == 0 and slope == 1 ? cBW :
abv == 0 ? cRW :
cN
// Regime label (max total score is now 6)
regLbl(int sc) =>
sc >= 5 ? "STRONG BULL" : sc >= 1 ? "BULLISH" : sc == 0 ? "NEUTRAL" : sc >= -4 ? "BEARISH" : "STRONG BEAR"
// Volatility regime label (ATR percentile)
volLbl(int pct) =>
pct >= 75 ? "EXTREME · " + str.tostring(pct) + "%" :
pct >= 50 ? "ELEVATED · " + str.tostring(pct) + "%" :
pct >= 25 ? "NORMAL · " + str.tostring(pct) + "%" :
"LOW · " + str.tostring(pct) + "%"
// Net Score percentile label — where today's reading ranks vs. its own trailing history
netPctileLbl(int pct) =>
pct >= 90 ? "EXTREME HIGH · " + str.tostring(pct) + "%" :
pct >= 75 ? "HIGH · " + str.tostring(pct) + "%" :
pct >= 25 ? "TYPICAL · " + str.tostring(pct) + "%" :
pct >= 10 ? "LOW · " + str.tostring(pct) + "%" :
"EXTREME LOW · " + str.tostring(pct) + "%"
// Strength bar (5 dots)
strBar(int sc) =>
a = math.abs(sc)
a >= 4 ? "● ● ● ● ●" : a >= 3 ? "● ● ● ● ○" : a >= 2 ? "● ● ● ○ ○" : a >= 1 ? "● ● ○ ○ ○" : "○ ○ ○ ○ ○"
// Score text with sign
scoreTxt(int sc) =>
(sc > 0 ? "+" : "") + str.tostring(sc)
// Percentage text with sign
pctTxt(int pct) =>
(pct > 0 ? "+" : "") + str.tostring(pct) + "%"
// TF display name
tfN(simple string tf) =>
switch tf
"1" => "1m"
"3" => "3m"
"5" => "5m"
"15" => "15m"
"30" => "30m"
"45" => "45m"
"60" => "1H"
"120" => "2H"
"180" => "3H"
"240" => "4H"
"1D" => "Daily"
"1W" => "Weekly"
"1M" => "Monthly"
=> tf
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// CHART OVERLAY
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cEF = #42A5F5
cEM = #FFA726
cES = #EF5350
eFCol = bullStack ? cBS : bearStack ? cRS : cEF
eMCol = bullStack ? cBS : bearStack ? cRS : cEM
eSCol = bullStack ? color.new(cBS, 40) : bearStack ? color.new(cRS, 40) : color.new(cES, 40)
pF = plot(emaF, "EMA Fast", color=eFCol, linewidth=2)
pM = plot(emaM, "EMA Mid", color=eMCol, linewidth=2)
pS = plot(emaS, "EMA Slow", color=eSCol, linewidth=3)
ribC = bullStack ? color.new(cBS, 85) : bearStack ? color.new(cRS, 85) : color.new(cN, 93)
fill(pF, pM, color=i_emaFill ? ribC : na, title="EMA Ribbon")
stC = stBull ? cBS : cRS
plot(i_stShow ? stValue : na, "Supertrend", color=stC, linewidth=2, style=plot.style_linebr)
plotshape(i_stShow and stFlipBull, "ST↑", shape.triangleup, location.belowbar, cBS, size=size.tiny)
plotshape(i_stShow and stFlipBear, "ST↓", shape.triangledown, location.abovebar, cRS, size=size.tiny)
// Vol Stop — plotted as circles so it reads as a secondary/slower stop, distinct from the solid Supertrend line
plot(i_vsShow and vsDir == 1 ? vsLongStop : na, "Vol Stop (Long)", color=cBS, style=plot.style_circles, linewidth=2)
plot(i_vsShow and vsDir == -1 ? vsShortStop : na, "Vol Stop (Short)", color=cRS, style=plot.style_circles, linewidth=2)
plotshape(i_vsShow and vsFlipBull, "VS↑", shape.diamond, location.belowbar, cBS, size=size.tiny)
plotshape(i_vsShow and vsFlipBear, "VS↓", shape.diamond, location.abovebar, cRS, size=size.tiny)
// Heikin-Ashi last candle color — marker shown only on the current/last bar
plotshape(barstate.islast and haBull, "HA Bullish", shape.square, location.belowbar, cBS, size=size.small)
plotshape(barstate.islast and not haBull, "HA Bearish", shape.square, location.abovebar, cRS, size=size.small)
if i_showCross and goldenCross
label.new(bar_index, emaS, "GC", color=color.new(cBS, 20), textcolor=color.white, style=label.style_label_up, size=size.small)
if i_showCross and deathCross
label.new(bar_index, emaS, "DC", color=color.new(cRS, 20), textcolor=color.white, style=label.style_label_down, size=size.small)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MTF HEATMAP TABLE — Professional Layout
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
tPos = switch i_hmPos
"Top Right" => position.top_right
"Top Left" => position.top_left
"Bottom Right" => position.bottom_right
"Bottom Left" => position.bottom_left
"Middle Right" => position.middle_right
"Middle Left" => position.middle_left
=> position.middle_right
tSz = switch i_hmSize
"Tiny" => size.tiny
"Small" => size.small
"Normal" => size.normal
"Large" => size.large
"Huge" => size.huge
=> size.normal
hSz = switch i_hmSize
"Tiny" => size.tiny
"Small" => size.tiny
"Normal" => size.small
"Large" => size.normal
"Huge" => size.large
=> size.small
// Table colors
cDark = color.new(#0d1117, 0)
cRow1 = color.new(#161b22, 0)
cRow2 = color.new(#0d1117, 0)
cBrd = color.new(#21262d, 0)
cHead = color.new(#1c2128, 0)
cDim = color.new(#6e7681, 0)
cWht = color.new(#e6edf3, 0)
cWhtD = color.new(#8b949e, 0)
// Section header colors
cSecBlue = color.new(#1f3a5f, 0)
cSecPurple = color.new(#3b1f5f, 0)
cSecTeal = color.new(#1a3f3f, 0)
cSecGold = color.new(#3f351a, 0)
cSecOrange = color.new(#4a2a10, 0)
if i_showHM and barstate.islast
var tbl = table.new(tPos, 5, 21, bgcolor=cDark, border_width=1, border_color=cBrd, frame_width=2, frame_color=color.new(#30363d, 0))
// ── ROW 0 — Title ──
table.cell(tbl, 0, 0, " T R E N D E N G I N E ", text_color=cWht, text_size=hSz, bgcolor=color.new(#010409, 0), text_halign=text.align_center)
table.merge_cells(tbl, 0, 0, 4, 0)
// ── ROW 1 — TF Headers ──
table.cell(tbl, 0, 1, "", text_color=cDim, text_size=hSz, bgcolor=cHead, text_halign=text.align_center)
table.cell(tbl, 1, 1, tfN(i_tf1), text_color=cWht, text_size=hSz, bgcolor=cHead, text_halign=text.align_center)
table.cell(tbl, 2, 1, tfN(i_tf2), text_color=cWht, text_size=hSz, bgcolor=cHead, text_halign=text.align_center)
table.cell(tbl, 3, 1, tfN(i_tf3), text_color=cWht, text_size=hSz, bgcolor=cHead, text_halign=text.align_center)
table.cell(tbl, 4, 1, tfN(i_tf4), text_color=cWht, text_size=hSz, bgcolor=cHead, text_halign=text.align_center)
// ── ROW 2 — Section: STRUCTURE ──
table.cell(tbl, 0, 2, " S T R U C T U R E ", text_color=color.new(cEF, 0), text_size=size.tiny, bgcolor=cSecBlue, text_halign=text.align_center)
table.merge_cells(tbl, 0, 2, 4, 2)
// ── ROW 3 — EMA Stack ──
table.cell(tbl, 0, 3, " EMA Stack ", text_color=cEF, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 3, emaLbl(int(e1)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(e1), 2), 25), text_halign=text.align_center)
table.cell(tbl, 2, 3, emaLbl(int(e2)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(e2), 2), 25), text_halign=text.align_center)
table.cell(tbl, 3, 3, emaLbl(int(e3)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(e3), 2), 25), text_halign=text.align_center)
table.cell(tbl, 4, 3, emaLbl(int(e4)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(e4), 2), 25), text_halign=text.align_center)
// ── ROW 4 — Price vs EMA 200 ──
table.cell(tbl, 0, 4, " Price/200 ", text_color=cWhtD, text_size=tSz, bgcolor=cRow2, text_halign=text.align_left)
table.cell(tbl, 1, 4, p200Lbl(int(p1)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(p1), 1), 35), text_halign=text.align_center)
table.cell(tbl, 2, 4, p200Lbl(int(p2)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(p2), 1), 35), text_halign=text.align_center)
table.cell(tbl, 3, 4, p200Lbl(int(p3)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(p3), 1), 35), text_halign=text.align_center)
table.cell(tbl, 4, 4, p200Lbl(int(p4)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(p4), 1), 35), text_halign=text.align_center)
// ── ROW 5 — Section: DIRECTION ──
table.cell(tbl, 0, 5, " D I R E C T I O N ", text_color=color.new(#AB47BC, 0), text_size=size.tiny, bgcolor=cSecPurple, text_halign=text.align_center)
table.merge_cells(tbl, 0, 5, 4, 5)
// ── ROW 6 — Supertrend ──
table.cell(tbl, 0, 6, " SuperTrend ", text_color=#AB47BC, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 6, stLbl(int(s1)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(s1), 1), 25), text_halign=text.align_center)
table.cell(tbl, 2, 6, stLbl(int(s2)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(s2), 1), 25), text_halign=text.align_center)
table.cell(tbl, 3, 6, stLbl(int(s3)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(s3), 1), 25), text_halign=text.align_center)
table.cell(tbl, 4, 6, stLbl(int(s4)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(s4), 1), 25), text_halign=text.align_center)
// ── ROW 7 — Vol Stop ──
table.cell(tbl, 0, 7, " Vol Stop ", text_color=#AB47BC, text_size=tSz, bgcolor=cRow2, text_halign=text.align_left)
table.cell(tbl, 1, 7, stLbl(int(vs1)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(vs1), 1), 25), text_halign=text.align_center)
table.cell(tbl, 2, 7, stLbl(int(vs2)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(vs2), 1), 25), text_halign=text.align_center)
table.cell(tbl, 3, 7, stLbl(int(vs3)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(vs3), 1), 25), text_halign=text.align_center)
table.cell(tbl, 4, 7, stLbl(int(vs4)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(vs4), 1), 25), text_halign=text.align_center)
// ── ROW 8 — HA Candle ──
table.cell(tbl, 0, 8, " HA Candle ", text_color=#AB47BC, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 8, stLbl(int(ha1)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(ha1), 1), 25), text_halign=text.align_center)
table.cell(tbl, 2, 8, stLbl(int(ha2)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(ha2), 1), 25), text_halign=text.align_center)
table.cell(tbl, 3, 8, stLbl(int(ha3)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(ha3), 1), 25), text_halign=text.align_center)
table.cell(tbl, 4, 8, stLbl(int(ha4)), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(int(ha4), 1), 25), text_halign=text.align_center)
// ── ROW 9 — Section: VOLUME ──
table.cell(tbl, 0, 9, " V O L U M E F L O W ", text_color=color.new(#26A69A, 0), text_size=size.tiny, bgcolor=cSecTeal, text_halign=text.align_center)
table.merge_cells(tbl, 0, 9, 4, 9)
// ── ROW 10 — OBV Flow ──
table.cell(tbl, 0, 10, " OBV Flow ", text_color=#26A69A, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 10, obvFlowLbl(int(oa1), int(os1)), text_color=cWht, text_size=tSz, bgcolor=color.new(obvFlowCol(int(oa1), int(os1)), 25), text_halign=text.align_center)
table.cell(tbl, 2, 10, obvFlowLbl(int(oa2), int(os2)), text_color=cWht, text_size=tSz, bgcolor=color.new(obvFlowCol(int(oa2), int(os2)), 25), text_halign=text.align_center)
table.cell(tbl, 3, 10, obvFlowLbl(int(oa3), int(os3)), text_color=cWht, text_size=tSz, bgcolor=color.new(obvFlowCol(int(oa3), int(os3)), 25), text_halign=text.align_center)
table.cell(tbl, 4, 10, obvFlowLbl(int(oa4), int(os4)), text_color=cWht, text_size=tSz, bgcolor=color.new(obvFlowCol(int(oa4), int(os4)), 25), text_halign=text.align_center)
// ── ROW 11 — Section: VOLATILITY ──
table.cell(tbl, 0, 11, " V O L A T I L I T Y ", text_color=color.new(#FFAB40, 0), text_size=size.tiny, bgcolor=cSecOrange, text_halign=text.align_center)
table.merge_cells(tbl, 0, 11, 4, 11)
// ── ROW 12 — ATR Percentile Regime ──
table.cell(tbl, 0, 12, " Volatility ", text_color=#FFAB40, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 12, volLbl(ap1), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(v1, 2), 25), text_halign=text.align_center)
table.cell(tbl, 2, 12, volLbl(ap2), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(v2, 2), 25), text_halign=text.align_center)
table.cell(tbl, 3, 12, volLbl(ap3), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(v3, 2), 25), text_halign=text.align_center)
table.cell(tbl, 4, 12, volLbl(ap4), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(v4, 2), 25), text_halign=text.align_center)
// ── ROW 13 — Section: CONFLUENCE ──
table.cell(tbl, 0, 13, " C O N F L U E N C E ", text_color=color.new(#FFB74D, 0), text_size=size.tiny, bgcolor=cSecGold, text_halign=text.align_center)
table.merge_cells(tbl, 0, 13, 4, 13)
// ── ROW 14 — Score ──
table.cell(tbl, 0, 14, " Score ", text_color=cWht, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 14, scoreTxt(t1), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(t1, 6), 15), text_halign=text.align_center)
table.cell(tbl, 2, 14, scoreTxt(t2), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(t2, 6), 15), text_halign=text.align_center)
table.cell(tbl, 3, 14, scoreTxt(t3), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(t3, 6), 15), text_halign=text.align_center)
table.cell(tbl, 4, 14, scoreTxt(t4), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(t4, 6), 15), text_halign=text.align_center)
// ── ROW 15 — Strength Bar ──
table.cell(tbl, 0, 15, " Strength ", text_color=cWhtD, text_size=tSz, bgcolor=cRow2, text_halign=text.align_left)
table.cell(tbl, 1, 15, strBar(t1), text_color=hmCol(t1, 6), text_size=tSz, bgcolor=cRow2, text_halign=text.align_center)
table.cell(tbl, 2, 15, strBar(t2), text_color=hmCol(t2, 6), text_size=tSz, bgcolor=cRow2, text_halign=text.align_center)
table.cell(tbl, 3, 15, strBar(t3), text_color=hmCol(t3, 6), text_size=tSz, bgcolor=cRow2, text_halign=text.align_center)
table.cell(tbl, 4, 15, strBar(t4), text_color=hmCol(t4, 6), text_size=tSz, bgcolor=cRow2, text_halign=text.align_center)
// ── ROW 16 — Regime (volatility-discounted) ──
dT1r = int(math.round(dT1))
dT2r = int(math.round(dT2))
dT3r = int(math.round(dT3))
dT4r = int(math.round(dT4))
table.cell(tbl, 0, 16, " Regime ", text_color=cWht, text_size=tSz, bgcolor=cRow1, text_halign=text.align_left)
table.cell(tbl, 1, 16, regLbl(dT1r), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(dT1r, 6), 10), text_halign=text.align_center)
table.cell(tbl, 2, 16, regLbl(dT2r), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(dT2r, 6), 10), text_halign=text.align_center)
table.cell(tbl, 3, 16, regLbl(dT3r), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(dT3r, 6), 10), text_halign=text.align_center)
table.cell(tbl, 4, 16, regLbl(dT4r), text_color=cWht, text_size=tSz, bgcolor=color.new(hmCol(dT4r, 6), 10), text_halign=text.align_center)
// ── ROW 17 — Net Score (weighted % of max, Daily & Weekly count 2x) ──
netCol = color.new(hmCol(int(math.round(netScore)), netScoreMax), 10)
table.cell(tbl, 0, 17, " Net Score ", text_color=cWht, text_size=tSz, bgcolor=cSecGold, text_halign=text.align_left)
table.cell(tbl, 1, 17, pctTxt(netScorePct), text_color=cWht, text_size=tSz, bgcolor=netCol, text_halign=text.align_center)
table.merge_cells(tbl, 1, 17, 4, 17)
// ── ROW 18 — Net Score Percentile (vs. its own trailing history) ──
netPctileSigned = int(math.round((netScorePctile - 50) * 2)) // rescale 0-100 to -100..100 for hmCol
netPctileCol = color.new(hmCol(netPctileSigned, 100), 10)
table.cell(tbl, 0, 18, " Net %ile ", text_color=cWht, text_size=tSz, bgcolor=cRow2, text_halign=text.align_left)
table.cell(tbl, 1, 18, netPctileLbl(netScorePctile), text_color=cWht, text_size=tSz, bgcolor=netPctileCol, text_halign=text.align_center)
table.merge_cells(tbl, 1, 18, 4, 18)
// ── ROW 19 — Divider ──
for col = 0 to 4
table.cell(tbl, col, 19, "", bgcolor=color.new(#30363d, 0), text_size=size.tiny)
// ── ROW 20 — MTF Alignment Summary ──
alignText = bullTFs == 4 ? "◆ 4/4 BULLISH — Full alignment" :
bullTFs == 3 ? "▲ 3/4 BULLISH — Strong alignment" :
bearTFs == 4 ? "◆ 4/4 BEARISH — Full alignment" :
bearTFs == 3 ? "▼ 3/4 BEARISH — Strong alignment" :
bullTFs == 2 and bearTFs == 2 ? "◆ SPLIT 2/2 — No clear bias" :
bullTFs > bearTFs ? "▲ " + str.tostring(bullTFs) + "/4 Bullish — Partial" :
bearTFs > bullTFs ? "▼ " + str.tostring(bearTFs) + "/4 Bearish — Partial" :
"◆ MIXED — Wait for clarity"
alignCol = bullTFs >= 3 ? cBS : bearTFs >= 3 ? cRS : cN
table.cell(tbl, 0, 20, alignText, text_color=alignCol, text_size=hSz, bgcolor=color.new(#010409, 0), text_halign=text.align_center)
table.merge_cells(tbl, 0, 20, 4, 20)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ALERTS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
alertcondition(trendChanged and isStrongBull, "Strong bullish", "Trend Engine: STRONG BULLISH — All components aligned.")
alertcondition(trendChanged and isStrongBear, "Strong bearish", "Trend Engine: STRONG BEARISH — All components aligned.")
alertcondition(goldenCross, "Golden cross", "Trend Engine: GOLDEN CROSS detected.")
alertcondition(deathCross, "Death cross", "Trend Engine: DEATH CROSS detected.")
alertcondition(stFlipBull, "ST flip bull", "Trend Engine: Supertrend flipped BULLISH.")
alertcondition(stFlipBear, "ST flip bear", "Trend Engine: Supertrend flipped BEARISH.")
alertcondition(vsFlipBull, "Vol Stop flip bull", "Trend Engine: Vol Stop flipped BULLISH.")
alertcondition(vsFlipBear, "Vol Stop flip bear", "Trend Engine: Vol Stop flipped BEARISH.")
alertcondition(trendChanged, "Trend change", "Trend Engine: Classification changed — check heatmap.")
Show all 506 lines