//@version=6
// ════════════════════════════════════════════════════════════════════════════
// RSI-MACD × Stochastic — MTF Signal Suite
// Combines TH_RSIMACD+ (RSI-based MACD) with CM_Stochastic_MTF (Stoch 14,3,3)
// into a single staged, timeframe-aware buy/sell engine.
//
// Signal grammar:
// B1 / S1 = Stochastic %K/%D cross (EARLY — leads)
// B2 / S2 = RSI-MACD line/signal cross (CONFIRM — lags, validates B1)
// BUY★/SELL★ = Confirmed: B2 within N bars of a B1 + gates
// eB1/eB2 = Estimated (anticipation): cross imminent, location-gated
// Lead TF = faster TF early trigger 'd↑/d↓' + early TOPPING; chart TF confirms.
// Regime filters (validated 1998–2026, research/SECTOR_CONFLUENCE.md):
// OPTIONAL 200-day MA gate (OFF by default), RSI<65 on buys, sells require EXTENDED.
// ════════════════════════════════════════════════════════════════════════════
indicator("RSI-MACD × Stoch — MTF Signal Suite", shorttitle="RM×ST",
overlay=false, max_labels_count=500)
// ───────────────────────────── INPUTS ──────────────────────────────────────
grpR = "RSI-MACD (TH_RSIMACD+)"
rsiLen = input.int(14, "RSI length", minval=1, group=grpR)
fastLen = input.int(14, "Fast MA", minval=1, group=grpR)
baseLen = input.int(60, "Base MA", minval=1, group=grpR)
signalLen = input.int(5, "Signal", minval=1, group=grpR)
grpS = "Stochastic (CM_Stochastic_MTF)"
stochLen = input.int(14, "Stoch length", minval=1, group=grpS)
smoothK = input.int(3, "Smooth %K", minval=1, group=grpS)
smoothD = input.int(3, "Smooth %D", minval=1, group=grpS)
obLevel = input.int(80, "Overbought", group=grpS)
osLevel = input.int(20, "Oversold", group=grpS)
stochType = input.string("Stoch RSI (high amplitude)", "Stochastic type", options=["Stoch RSI (high amplitude)","Regular Stochastic (CM_MTF)"], group=grpS, tooltip="Stoch RSI = stochastic OF the RSI — swings the full 0-100 range and reaches 80/20 far more often. Regular = stochastic of price, matching CM_Stochastic_MTF.")
stochRsiLen = input.int(14, "Stoch RSI: RSI length", minval=1, group=grpS, tooltip="Only used in Stoch RSI mode — the internal RSI length fed to the stochastic.")
stochTrigMode = input.string("K/D cross", "Stage-1 Stoch trigger", options=["K/D cross","Exit 20/80 (research)","Either"], group=grpS, tooltip="K/D cross = %K crosses %D (earliest turn). Exit 20/80 = %K crosses back above 20 / below 80. Either = fire on both.")
grpE = "Signal Engine"
confW = input.int(8, "B1→B2 confirm window (bars)", minval=1, group=grpE, tooltip="Max bars between the Stoch cross (B1) and the RSI-MACD cross (B2) for a Confirmed★ signal.")
fromZoneOnly= input.bool(false, "BUY★/SELL★ only from OS/OB extreme (premium)", group=grpE, tooltip="OFF (default): ★ confirms ANY aligned Stoch+MACD cross, including mid-range. ON: ★ requires the Stoch cross to originate from oversold/overbought (rarer, higher-conviction). Raw B1/B2/S1/S2 always fire on every cross regardless.")
estGap = input.float(8.0, "Stoch anticipation gap", minval=0.5, group=grpE, tooltip="How close %K must be to %D (and closing) to fire an estimated Stoch cross.")
estZoneB = input.int(55, "Anticipate LONGS only when %K below", group=grpE, tooltip="Stops early-long anticipation when Stoch is already high (the 'pinned at 100' problem).")
estZoneS = input.int(45, "Anticipate SHORTS only when %K above", group=grpE)
estHistBars = input.int(2, "Hist rising/falling bars for estimated MACD cross", minval=1, group=grpE)
useReversal = input.bool(true, "Fast-reversal cut-loss (MACD flips right after ★)", group=grpE, tooltip="False-signal protection: after a BUY★, a MACD bear-cross within N bars fires a cut-loss SELL; after a SELL★, a MACD bull-cross within N bars fires a cut-loss BUY.")
revBars = input.int(3, "Fast-reversal window (ticks/bars)", minval=1, group=grpE)
grpM = "Multi-Timeframe"
useMTF = input.bool(true, "Enable MTF trend gate + dashboard", group=grpM)
autoConfirm = input.bool(true, "Auto-pick confirm timeframe (one step up)", group=grpM)
confirmTFman = input.timeframe("1W", "Confirm timeframe (manual)", group=grpM)
anchorTF = input.timeframe("3D", "Anchor timeframe (your sweet spot)", group=grpM)
useAnchorGate= input.bool(false, "Also require 3D-anchor agreement for ★", group=grpM)
rep = input.bool(false, "Use developing HTF bar (earlier, repaints)", group=grpM, tooltip="OFF = confirmed HTF bar: no repaint but up to 1 HTF-bar late. ON = reacts on the forming HTF bar: earlier, but can repaint until it closes.")
grpL = "Lead TF (daily-trigger / 3D-confirm)"
useLead = input.bool(true, "Enable lead-TF early trigger + topping warning", group=grpL, tooltip="A FASTER timeframe (e.g. daily under a 3-day chart) gives the early trigger and the early roll-over while the chart TF confirms. Reads the developing faster bar (provisional until the lead bar closes).")
autoLead = input.bool(true, "Auto-pick lead TF (one step down)", group=grpL)
leadTFman = input.timeframe("1D", "Lead timeframe (manual)", group=grpL)
useLeadGate = input.bool(false, "Require lead-TF agreement for ★ (the +0.87% 'both agree')", group=grpL)
grpD = "Display"
paneMode = input.string("Stoch + MACD fill", "Pane oscillators", options=["Stoch + MACD fill","Stoch only","RSI-MACD only"], group=grpD)
showOverlay = input.bool(true, "Plot signals on price chart", group=grpD)
priceDetail = input.string("★ + stage (B1/B2)", "Price marker detail", options=["★ confirmed only","★ + stage (B1/B2)","All (+estimated +lead)"], group=grpD, tooltip="Declutters the chart. '★ confirmed only' = just BUY★/SELL★/TOP. '★ + stage' also shows B1/B2/S1/S2 triangles. 'All' adds the small estimated + lead-TF (d↑/d↓) early marks. The full read is always in the dashboard regardless of this.")
showPane = input.bool(true, "Plot cross dots in this pane", group=grpD)
showEst = input.bool(true, "Show estimated (anticipation) signals", group=grpD)
showB = input.bool(true, "Show B (buy) crosses on price", group=grpD)
showS = input.bool(true, "Show S (sell) crosses on price", group=grpD)
showTop = input.bool(true, "Show TOP warnings on price", group=grpD)
showBG = input.bool(true, "Background tint on Stoch OB/OS", group=grpD)
showDash = input.bool(true, "Show MTF dashboard table", group=grpD)
normWin = input.int(150, "RSI-MACD normalize window", minval=20, group=grpD)
grpF = "Regime Filters (validated — research/SECTOR_CONFLUENCE.md)"
useMaGate = input.bool(false, "200-day MA gate — suppress buys below it", group=grpF, tooltip="OFF (default): buys fire below the 200-day too — the deepest, most asymmetric value setups sit under the MA. ON: applies the research's risk filter and withholds the price buy endorsement below the MA. The 200-day line + regime row stay as context either way.")
maLen = input.int(200, "MA length", minval=1, group=grpF)
maTF = input.timeframe("1D", "MA timeframe (keeps a true 200-DAY MA on any chart)", group=grpF)
showMaOnPrice = input.bool(true, "Plot the 200-day MA on price", group=grpF)
useRsiBuyFilter = input.bool(true, "RSI below max on buys", group=grpF, tooltip="Validated BUY required RSI<65 — don't buy already-hot momentum.")
buyRsiMax = input.int(65, "Buy RSI max", group=grpF)
requireExtForSell = input.bool(true, "Sells require EXTENDED (RSI>70 or %K>80)", group=grpF, tooltip="Validated: a down-cross from a NON-extended state is a bounce trap. Only EXTENDED + confirmed down-cross is a real top.")
extRsi = input.int(70, "Extended RSI level", group=grpF)
// ───────────────────────────── ENGINE ──────────────────────────────────────
// Compute the full state once; reused locally and inside request.security().
calc() =>
_rsi = ta.rsi(close, rsiLen)
_fast = ta.ema(_rsi, fastLen)
_base = ta.ema(_rsi, baseLen)
_macd = _fast - _base
_sig = ta.ema(_macd, signalLen)
_srsi = ta.rsi(close, stochRsiLen)
_rawk = stochType == "Regular Stochastic (CM_MTF)" ? ta.stoch(close, high, low, stochLen) : ta.stoch(_srsi, _srsi, _srsi, stochLen)
_k = ta.sma(_rawk, smoothK)
_d = ta.sma(_k, smoothD)
[_k, _d, _macd, _sig]
[k, d, macd, signal] = calc()
hist = macd - signal
// Non-repaint MTF helper: confirmed HTF bar unless user opts into the developing one.
secScalar(_tf, _src) =>
request.security(syminfo.tickerid, _tf, rep ? _src : _src[1], lookahead=barmerge.lookahead_off)
// Lead-TF helper: DEVELOPING faster-TF bar (real-time early warning; provisional on the live bar).
secDev(_tf, _src) =>
request.security(syminfo.tickerid, _tf, _src, lookahead=barmerge.lookahead_off)
// Auto confirm timeframe = one step up from the chart TF
tfSec = timeframe.in_seconds()
oneUp = tfSec < 86400 ? "1D" : tfSec < 259200 ? "3D" : tfSec < 604800 ? "1W" : tfSec < 2592000 ? "1M" : tfSec < 7776000 ? "3M" : "12M"
confirmTF = autoConfirm ? oneUp : confirmTFman
// Clamp the anchor so it can never sit BELOW the chart TF. Falls back to the chart TF.
anchorTfEff = timeframe.in_seconds(anchorTF) <= tfSec ? timeframe.period : anchorTF
// Lead (faster) timeframe = one step DOWN, clamped to never be slower than the chart.
oneDown = tfSec > 604800 ? "1W" : tfSec > 259200 ? "3D" : tfSec > 86400 ? "1D" : tfSec > 14400 ? "240" : tfSec > 3600 ? "60" : timeframe.period
leadTFraw = autoLead ? oneDown : leadTFman
leadTfEff = timeframe.in_seconds(leadTFraw) >= tfSec ? timeframe.period : leadTFraw
leadActive = useLead and timeframe.in_seconds(leadTfEff) < tfSec // a genuinely faster TF exists
// Higher / anchor / lead timeframe states (request.security called unconditionally — Pine best practice).
k_c = secScalar(confirmTF, k)
d_c = secScalar(confirmTF, d)
macd_c = secScalar(confirmTF, macd)
sig_c = secScalar(confirmTF, signal)
k_a = secScalar(anchorTfEff, k)
d_a = secScalar(anchorTfEff, d)
macd_a = secScalar(anchorTfEff, macd)
sig_a = secScalar(anchorTfEff, signal)
// Lead (faster) TF state — the early trigger/roll (developing bar)
k_l = secDev(leadTfEff, k)
d_l = secDev(leadTfEff, d)
macd_l = secDev(leadTfEff, macd)
sig_l = secDev(leadTfEff, signal)
// ── Stage-1 (Stochastic) and Stage-2 (RSI-MACD) crosses on the chart TF ──
kdBull = ta.crossover(k, d)
kdBear = ta.crossunder(k, d)
exitOSBull = ta.crossover(k, osLevel)
exitOBBear = ta.crossunder(k, obLevel)
stochBull = stochTrigMode == "K/D cross" ? kdBull : stochTrigMode == "Exit 20/80 (research)" ? exitOSBull : (kdBull or exitOSBull)
stochBear = stochTrigMode == "K/D cross" ? kdBear : stochTrigMode == "Exit 20/80 (research)" ? exitOBBear : (kdBear or exitOBBear)
macdBull = ta.crossover(macd, signal)
macdBear = ta.crossunder(macd, signal)
// ── Regime filters (validated, research/SECTOR_CONFLUENCE.md) ──
rsiVal = ta.rsi(close, rsiLen)
maRaw = ta.sma(close, maLen)
ma200 = secScalar(maTF, maRaw) // true 200-DAY MA on any chart timeframe
aboveMA = close > ma200
hiK = ta.highest(k, confW)
hiRsi = ta.highest(rsiVal, confW)
extendedNow = rsiVal >= extRsi or k >= obLevel
recentExtended = hiK >= obLevel or hiRsi >= extRsi
buyRegimeOK = (not useMaGate or aboveMA) and (not useRsiBuyFilter or rsiVal < buyRsiMax)
sellRegimeOK = not requireExtForSell or recentExtended
// ── Lead-TF triggers & rolls (daily-trigger / 3D-confirm) ──
leadBullState = k_l >= d_l
leadBearState = k_l <= d_l
leadKDup = ta.crossover(k_l, d_l)
leadOSup = ta.crossover(k_l, osLevel)
leadKDdn = ta.crossunder(k_l, d_l)
leadOBdn = ta.crossunder(k_l, obLevel)
leadBull = leadKDup or leadOSup
leadBear = leadKDdn or leadOBdn
leadHiK = ta.highest(k_l, 3)
leadLoK = ta.lowest(k_l, 3)
leadRollTop = k_l < k_l[1] and leadHiK >= obLevel // faster TF rolling DOWN out of overbought
leadRollBot = k_l > k_l[1] and leadLoK <= osLevel // faster TF turning UP out of oversold
leadBuyOK = not (leadActive and useLeadGate) or leadBullState
leadSellOK = not (leadActive and useLeadGate) or leadBearState
topRaw = leadActive and extendedNow and leadRollTop
var bool topArmed = true
if not extendedNow
topArmed := true // re-arm only after stoch leaves overbought/extended
TOPPING = topRaw and topArmed // de-duped: ONE top per extended episode
if TOPPING
topArmed := false
eLeadB = leadActive and leadBull and k < d and not stochBull and buyRegimeOK
eLeadS = leadActive and leadBear and k > d and not stochBear and sellRegimeOK
// Quality grade: did the Stoch cross originate from the OS/OB extreme?
b1FromOS = ta.lowest(d, confW) < osLevel
s1FromOB = ta.highest(d, confW) > obLevel
// Raw crosses fire on EVERY cross (frequent — catch mid-range entries/exits). Quality gates apply to ★ only.
B1 = stochBull
S1 = stochBear
B2 = macdBull
S2 = macdBear
premiumBull = not fromZoneOnly or b1FromOS or d < osLevel + 10
premiumBear = not fromZoneOnly or s1FromOB or d > obLevel - 10
// ── Confirmed signal: B2 lands within confW bars of a B1, with HTF trend gate ──
confirmBullTrend = not useMTF or (macd_c >= sig_c) or b1FromOS
confirmBearTrend = not useMTF or (macd_c <= sig_c) or s1FromOB
anchorBullOK = not useAnchorGate or (k_a >= d_a) or (macd_a >= sig_a)
anchorBearOK = not useAnchorGate or (k_a <= d_a) or (macd_a <= sig_a)
recentB1 = ta.barssince(B1) <= confW
recentS1 = ta.barssince(S1) <= confW
CB = macdBull and recentB1 and confirmBullTrend and anchorBullOK and buyRegimeOK and leadBuyOK and premiumBull
CS = macdBear and recentS1 and confirmBearTrend and anchorBearOK and sellRegimeOK and leadSellOK and premiumBear
// Fast-reversal cut-loss: a MACD flip within revBars of a ★ = the confirmed signal failed (false signal)
barsSinceCB = ta.barssince(CB)
barsSinceCS = ta.barssince(CS)
revExitSell = useReversal and macdBear and barsSinceCB <= revBars // BUY★ failed → cut the long
revExitBuy = useReversal and macdBull and barsSinceCS <= revBars // SELL★ failed → cut the short
// ── Estimated / anticipation signals (location-gated) ──
f_mono(_src, _n, _up) =>
ok = true
for i = 0 to _n - 1
ok := ok and (_up ? _src[i] > _src[i + 1] : _src[i] < _src[i + 1])
ok
gapKD = k - d
estB1 = showEst and k < d and k > k[1] and gapKD > gapKD[1] and gapKD > -estGap and k < estZoneB and not stochBull and buyRegimeOK
estS1 = showEst and k > d and k < k[1] and gapKD < gapKD[1] and gapKD < estGap and k > estZoneS and not stochBear and sellRegimeOK
// MACD-cross anticipation: fire only on the ONSET of the rising/falling streak (not every bar)
b2Setup = hist < 0 and f_mono(hist, estHistBars, true) and macd < signal
s2Setup = hist > 0 and f_mono(hist, estHistBars, false) and macd > signal
estB2 = showEst and b2Setup and not b2Setup[1] and not macdBull and buyRegimeOK
estS2 = showEst and s2Setup and not s2Setup[1] and not macdBear and sellRegimeOK
// ───────────────────────────── COLORS ──────────────────────────────────────
cTeal = #26a69a
cRed = #ef5350
cCyan = #00bcd4
cGold = #d4ac0d
cGray = #b0bec5
brightG = color.new(#00e676, 0)
brightR = color.new(#ff1744, 0)
// Stoch %K turns bright on its cross bar (the "which day did it cross" cue)
kColor = stochBull ? brightG : stochBear ? brightR : color.new(#4f9fff, 0)
dColor = color.new(#ff9800, 0)
// RSI-MACD normalized into the 0-100 pane, centered at 50
mrange = ta.highest(math.max(math.abs(macd), math.abs(signal)), normWin)
sf = mrange > 0 ? 38.0 / mrange : 1.0
macdN = 50 + macd * sf
sigN = 50 + signal * sf
macdNcolor = macd >= signal ? color.new(cCyan, 0) : color.new(cGray, 20)
showStoch = paneMode != "RSI-MACD only"
showRM = paneMode != "Stoch only"
// ───────────────────────────── PLOTS: levels & background ──────────────────
hline(obLevel, "Overbought", color=color.new(color.gray, 60), linestyle=hline.style_dotted)
hline(50, "Mid", color=color.new(color.gray, 80), linestyle=hline.style_dotted)
hline(osLevel, "Oversold", color=color.new(color.gray, 60), linestyle=hline.style_dotted)
stochOB = k > obLevel
stochOS = k < osLevel
bgcolor(showBG and stochOS ? color.new(cTeal, 88) : showBG and stochOB ? color.new(cRed, 88) : na, title="Stoch extension tint")
// ───────────────────────────── PLOTS: oscillators ──────────────────────────
pK = plot(showStoch ? k : na, "Stoch %K", color=kColor, linewidth=2)
pD = plot(showStoch ? d : na, "Stoch %D", color=dColor, linewidth=1)
fill(pK, pD, color=showStoch ? color.new(k >= d ? cTeal : cRed, 90) : na, title="K/D fill")
pM = plot(showRM ? macdN : na, "RSI-MACD line", color=macdNcolor, linewidth=2)
pS = plot(showRM ? sigN : na, "RSI-MACD signal", color=color.new(cGold, 0), linewidth=1)
fill(pM, pS, color=showRM ? color.new(macd >= signal ? cCyan : cRed, 80) : na, title="MACD/Signal fill")
// In-pane cross dots: on each oscillator's own line
plot(showPane and showStoch and stochBull ? k : na, "B1 dot", color=brightG, style=plot.style_circles, linewidth=4)
plot(showPane and showStoch and stochBear ? k : na, "S1 dot", color=brightR, style=plot.style_circles, linewidth=4)
plot(showPane and showRM and macdBull ? macdN : na, "B2 dot", color=color.new(cCyan,0), style=plot.style_circles, linewidth=4)
plot(showPane and showRM and macdBear ? macdN : na, "S2 dot", color=color.new(cRed,0), style=plot.style_circles, linewidth=4)
// ───────────────────────────── SIGNALS ON PRICE (force_overlay) ────────────
ovl = showOverlay
pmStage = priceDetail != "★ confirmed only"
pmAll = priceDetail == "All (+estimated +lead)"
plotshape(ovl and showB and pmStage and B1 and not CB, "B1 (Stoch buy)", style=shape.triangleup, location=location.belowbar, color=color.new(cTeal,0), text="B1", textcolor=color.new(cTeal,0), size=size.tiny, force_overlay=true)
plotshape(ovl and showB and pmStage and B2 and not CB, "B2 (MACD buy)", style=shape.triangleup, location=location.belowbar, color=brightG, text="B2", textcolor=brightG, size=size.tiny, force_overlay=true)
plotshape(ovl and CB, "BUY confirmed", style=shape.labelup, location=location.belowbar, color=color.new(#00c853,0), text="★", textcolor=color.white, size=size.small, force_overlay=true)
plotshape(ovl and showS and pmStage and S1 and not CS, "S1 (Stoch sell)", style=shape.triangledown, location=location.abovebar, color=color.new(cRed,0), text="S1", textcolor=color.new(cRed,0), size=size.tiny, force_overlay=true)
plotshape(ovl and showS and pmStage and S2 and not CS, "S2 (MACD sell)", style=shape.triangledown, location=location.abovebar, color=brightR, text="S2", textcolor=brightR, size=size.tiny, force_overlay=true)
plotshape(ovl and CS, "SELL confirmed", style=shape.labeldown, location=location.abovebar, color=color.new(#d50000,0), text="★", textcolor=color.white, size=size.small, force_overlay=true)
// Fast-reversal cut-loss markers
plotshape(ovl and revExitSell, "Cut-loss SELL (reversal)", style=shape.labeldown, location=location.abovebar, color=color.new(#ff6d00,0), text="CUT", textcolor=color.white, size=size.small, force_overlay=true)
plotshape(ovl and revExitBuy, "Cut-loss BUY (reversal)", style=shape.labelup, location=location.belowbar, color=color.new(#aeea00,0), text="RE-BUY", textcolor=color.black, size=size.small, force_overlay=true)
plotshape(ovl and pmAll and estB1, "Est buy (Stoch)", style=shape.triangleup, location=location.belowbar, color=color.new(cTeal,55), size=size.tiny, force_overlay=true)
plotshape(ovl and pmAll and estB2, "Est buy (MACD)", style=shape.circle, location=location.belowbar, color=color.new(cCyan,40), size=size.tiny, force_overlay=true)
plotshape(ovl and pmAll and estS1, "Est sell (Stoch)",style=shape.triangledown, location=location.abovebar, color=color.new(cRed,55), size=size.tiny, force_overlay=true)
plotshape(ovl and pmAll and estS2, "Est sell (MACD)", style=shape.circle, location=location.abovebar, color=color.new(cRed,40), size=size.tiny, force_overlay=true)
// 200-day MA on price (the regime line)
plot(showMaOnPrice ? ma200 : na, "200-day MA", color=color.new(color.orange, 0), linewidth=2, force_overlay=true)
// Lead-TF (faster) early triggers + topping warning on price
plotshape(ovl and pmAll and eLeadB, "Lead early buy", style=shape.diamond, location=location.belowbar, color=color.new(#2196f3,30), text="d↑", textcolor=color.new(#2196f3,0), size=size.tiny, force_overlay=true)
plotshape(ovl and pmAll and eLeadS, "Lead early sell", style=shape.diamond, location=location.abovebar, color=color.new(#ff9800,30), text="d↓", textcolor=color.new(#ff9800,0), size=size.tiny, force_overlay=true)
plotshape(ovl and showTop and TOPPING, "TOPPING (daily roll)", style=shape.xcross, location=location.abovebar, color=color.new(#ff5252,0), text="TOP", textcolor=color.new(#ff5252,0), size=size.tiny, force_overlay=true)
// ───────────────────────────── MTF DASHBOARD ───────────────────────────────
f_stochTxt(_k, _d) =>
str.tostring(_k, "#.#") + (_k >= _d ? " K>D" : " K<D") + (_k < osLevel and _d < osLevel ? " OS" : _k > obLevel and _d > obLevel ? " OB" : "")
f_macdTxt(_m, _s) =>
(_m >= _s ? "▲ " : "▼ ") + "h=" + str.tostring(_m - _s, "#.##")
f_biasCol(_k, _d, _m, _s) =>
bull = (_k >= _d ? 1 : 0) + (_m >= _s ? 1 : 0)
bull == 2 ? color.new(cTeal, 30) : bull == 0 ? color.new(cRed, 30) : color.new(color.gray, 40)
f_biasTxt(_k, _d, _m, _s) =>
bull = (_k >= _d ? 1 : 0) + (_m >= _s ? 1 : 0)
bull == 2 ? "BULL" : bull == 0 ? "BEAR" : "MIXED"
// Composite recommendation (current chart-TF event / regime)
knifeBuy = (stochBull or macdBull) and useMaGate and not aboveMA
bounceSell = (stochBear or macdBear) and requireExtForSell and not recentExtended
compTxt = revExitSell ? "⚠ CUT LONG — BUY★ failed (MACD flipped)" : revExitBuy ? "⚠ RE-BUY — SELL★ failed (MACD flipped up)" : CB ? "BUY ★ confirmed" : CS ? "SELL ★ confirmed" : TOPPING ? "TOPPING — daily rolling (extended)" : B2 ? "BUY (B2 confirm)" : S2 ? "SELL (S2 confirm)" : B1 ? "BUY (B1 early)" : S1 ? "SELL (S1 early)" : eLeadB ? "Lead (faster TF) turning up…" : eLeadS ? "Lead (faster TF) rolling…" : (estB1 or estB2) ? "Buy setup forming…" : (estS1 or estS2) ? "Sell setup forming…" : knifeBuy ? "Cross below 200d — knife (no buy)" : bounceSell ? "Down-cross, not extended — bounce trap" : (extendedNow and k < d) ? "Extended — don't chase" : (k >= d and macd >= signal) ? "Hold — long bias" : (k < d and macd < signal) ? "Hold — short bias" : "Neutral / mixed"
// Color follows the SAME priority order as compTxt, so text and color can never disagree.
compCol = revExitSell ? color.new(#ff6d00,10) : revExitBuy ? color.new(#aeea00,10) : CB ? color.new(cTeal,20) : CS ? color.new(cRed,20) : TOPPING ? color.new(cRed,20) : B2 ? color.new(cTeal,20) : S2 ? color.new(cRed,20) : B1 ? color.new(cTeal,20) : S1 ? color.new(cRed,20) : eLeadB ? color.new(cTeal,20) : eLeadS ? color.new(cRed,20) : (estB1 or estB2) ? color.new(cTeal,20) : (estS1 or estS2) ? color.new(cRed,20) : knifeBuy ? color.new(cRed,20) : bounceSell ? color.new(cRed,20) : (extendedNow and k < d) ? color.new(cRed,20) : (k >= d and macd >= signal) ? color.new(cTeal,20) : (k < d and macd < signal) ? color.new(cRed,20) : color.new(color.gray,30)
var table dash = table.new(position.top_right, 4, 7, border_width=1, frame_color=color.new(color.gray,50), frame_width=1)
if showDash and barstate.islast
table.cell(dash, 0, 0, "TF", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 1, 0, "Stoch", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 2, 0, "RSI-MACD", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 3, 0, "Bias", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 0, 1, "Chart " + timeframe.period, text_color=color.white, text_size=size.small)
table.cell(dash, 1, 1, f_stochTxt(k, d), text_color=color.white, text_size=size.small)
table.cell(dash, 2, 1, f_macdTxt(macd, signal),text_color=color.white, text_size=size.small)
table.cell(dash, 3, 1, f_biasTxt(k,d,macd,signal), text_color=color.white, text_size=size.small, bgcolor=f_biasCol(k,d,macd,signal))
leadTag = leadRollTop ? " ⤵roll" : leadRollBot ? " ⤴turn" : ""
table.cell(dash, 0, 2, leadActive ? "Lead " + leadTfEff : "Lead —", text_color=color.white, text_size=size.small)
table.cell(dash, 1, 2, leadActive ? f_stochTxt(k_l, d_l) + leadTag : "n/a", text_color=color.white, text_size=size.small)
table.cell(dash, 2, 2, leadActive ? f_macdTxt(macd_l, sig_l) : "n/a", text_color=color.white, text_size=size.small)
table.cell(dash, 3, 2, leadActive ? f_biasTxt(k_l,d_l,macd_l,sig_l) : "—", text_color=color.white, text_size=size.small, bgcolor = leadActive ? f_biasCol(k_l,d_l,macd_l,sig_l) : color.new(color.gray,60))
table.cell(dash, 0, 3, "Confirm " + confirmTF, text_color=color.white, text_size=size.small)
table.cell(dash, 1, 3, f_stochTxt(k_c, d_c), text_color=color.white, text_size=size.small)
table.cell(dash, 2, 3, f_macdTxt(macd_c, sig_c), text_color=color.white, text_size=size.small)
table.cell(dash, 3, 3, f_biasTxt(k_c,d_c,macd_c,sig_c), text_color=color.white, text_size=size.small, bgcolor=f_biasCol(k_c,d_c,macd_c,sig_c))
table.cell(dash, 0, 4, "Anchor " + anchorTfEff, text_color=color.white, text_size=size.small)
table.cell(dash, 1, 4, f_stochTxt(k_a, d_a), text_color=color.white, text_size=size.small)
table.cell(dash, 2, 4, f_macdTxt(macd_a, sig_a), text_color=color.white, text_size=size.small)
table.cell(dash, 3, 4, f_biasTxt(k_a,d_a,macd_a,sig_a), text_color=color.white, text_size=size.small, bgcolor=f_biasCol(k_a,d_a,macd_a,sig_a))
table.cell(dash, 0, 5, "SIGNAL", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 1, 5, "", text_color=color.white, text_size=size.small)
table.cell(dash, 2, 5, "", text_color=color.white, text_size=size.small)
table.cell(dash, 3, 5, "", text_color=color.white, text_size=size.small)
table.merge_cells(dash, 1, 5, 3, 5)
table.cell(dash, 1, 5, compTxt, text_color=color.white, text_size=size.normal, bgcolor=compCol)
regimeTxt = (aboveMA ? "▲ Above 200d" : "▼ Below 200d") + (useMaGate and not aboveMA ? " KNIFE-gated" : not aboveMA ? " (deep value)" : "") + (recentExtended ? " • EXTENDED" : "")
regimeCol = (useMaGate and not aboveMA) ? color.new(cRed, 25) : not aboveMA ? color.new(#2196f3, 35) : recentExtended ? color.new(#d4ac0d, 30) : color.new(cTeal, 35)
table.cell(dash, 0, 6, "Regime", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray,20))
table.cell(dash, 1, 6, "", text_color=color.white, text_size=size.small)
table.cell(dash, 2, 6, "", text_color=color.white, text_size=size.small)
table.cell(dash, 3, 6, "", text_color=color.white, text_size=size.small)
table.merge_cells(dash, 1, 6, 3, 6)
table.cell(dash, 1, 6, regimeTxt, text_color=color.white, text_size=size.small, bgcolor=regimeCol)
// ───────────────────────────── ALERTS ──────────────────────────────────────
alertcondition(B1, "B1 — Stoch buy (early)", "Stage 1 BUY: Stochastic %K crossed above %D")
alertcondition(B2, "B2 — RSI-MACD buy", "Stage 2 BUY: RSI-MACD crossed above Signal")
alertcondition(CB, "BUY ★ confirmed", "CONFIRMED BUY: RSI-MACD confirmed the Stoch cross within window")
alertcondition(S1, "S1 — Stoch sell (early)", "Stage 1 SELL: Stochastic %K crossed below %D")
alertcondition(S2, "S2 — RSI-MACD sell", "Stage 2 SELL: RSI-MACD crossed below Signal")
alertcondition(CS, "SELL ★ confirmed", "CONFIRMED SELL: RSI-MACD confirmed the Stoch cross within window")
alertcondition(revExitSell, "Cut-loss SELL (reversal)", "BUY★ failed: MACD flipped bearish within the reversal window — cut the long")
alertcondition(revExitBuy, "Re-buy (reversal)", "SELL★ failed: MACD flipped bullish within the reversal window — buy back / re-enter long")
alertcondition(estB1 or estB2, "Estimated buy setup", "Anticipation: a buy cross looks imminent")
alertcondition(estS1 or estS2, "Estimated sell setup", "Anticipation: a sell cross looks imminent")
alertcondition(TOPPING, "TOPPING (daily roll)", "Early top: extended + faster-TF rolling over")
alertcondition(eLeadB, "Lead early buy", "Faster TF turned up before the chart TF")
alertcondition(eLeadS, "Lead early sell", "Faster TF rolling over before the chart TF")