This indicator overlays an EMA stack, Supertrend, and ATR range bands on the price chart and scores long or short confluence from trend, WaveTrend, StochRSI, confirmed divergence, and volume.
//@version=6
// Momentum Confluence Suite [MCS]
// EMA, Supertrend, ADX regime, WaveTrend, StochRSI, confirmed divergence,
// ATR range/stop, volume confirmation, and return-based beta.
// Signals are evaluated only on confirmed bar closes. No system guarantees profit;
// validate settings on the symbol/timeframe before risking capital.
indicator("Momentum Confluence Suite [MCS]", shorttitle="MCS", overlay=true, max_labels_count=500)
// ── Input groups ────────────────────────────────────────────────
string GRP_T = "Trend (EMA)"
string GRP_ST = "Supertrend"
string GRP_RG = "Regime Filter"
string GRP_WT = "WaveTrend"
string GRP_SR = "StochRSI"
string GRP_DV = "Divergence"
string GRP_ATR = "ATR Range & Stop"
string GRP_VL = "Volume"
string GRP_BT = "Beta"
string GRP_SG = "Signals & Display"
int emaFastLen = input.int(21, "EMA Fast", minval=2, group=GRP_T)
int emaSlowLen = input.int(50, "EMA Slow", minval=3, group=GRP_T)
int emaLongLen = input.int(200, "EMA Long / Regime", minval=20, group=GRP_T)
int stLen = input.int(10, "ATR Length", minval=1, group=GRP_ST)
float stMult = input.float(3.0, "Multiplier", minval=0.1, step=0.1, group=GRP_ST)
int adxLen = input.int(14, "DI / ADX Length", minval=2, group=GRP_RG)
float adxThresh = input.float(20.0, "ADX Threshold", minval=5.0, step=0.5, group=GRP_RG)
int wtN1 = input.int(10, "Channel Length", minval=1, group=GRP_WT)
int wtN2 = input.int(21, "Average Length", minval=1, group=GRP_WT)
int rsiLen = input.int(14, "RSI Length", minval=2, group=GRP_SR)
int stochLen = input.int(14, "Stochastic Length", minval=2, group=GRP_SR)
int smoothK = input.int(3, "Smooth K", minval=1, group=GRP_SR)
int smoothD = input.int(3, "Smooth D", minval=1, group=GRP_SR)
int lbL = input.int(5, "Pivot Lookback Left", minval=1, group=GRP_DV)
int lbR = input.int(5, "Pivot Lookback Right", minval=1, group=GRP_DV)
int divMinBars = input.int(5, "Minimum Pivot Gap", minval=1, group=GRP_DV)
int divMaxBars = input.int(60, "Maximum Pivot Gap", minval=2, group=GRP_DV)
int atrLen = input.int(14, "ATR Length", minval=1, group=GRP_ATR)
float atrMult = input.float(2.0, "Stop Multiplier", minval=0.5, step=0.1, group=GRP_ATR)
float rngMult = input.float(1.5, "Range Band Multiplier", minval=0.1, step=0.1, group=GRP_ATR)
bool showBands = input.bool(true, "Show ATR Range Bands", group=GRP_ATR)
int volLen = input.int(20, "Volume SMA Length", minval=2, group=GRP_VL)
float volMult = input.float(1.5, "Volume Multiplier", minval=0.5, step=0.1, group=GRP_VL)
bool useVolFilter = input.bool(true, "Require Volume Confirmation", group=GRP_VL)
string benchSym = input.symbol("AMEX:SPY", "Benchmark Symbol", group=GRP_BT)
int betaLen = input.int(60, "Beta Lookback", minval=10, group=GRP_BT)
float betaMin = input.float(0.7, "Minimum Beta", minval=0.0, step=0.1, group=GRP_BT)
bool useBetaFilter = input.bool(false, "Require Minimum Beta", group=GRP_BT)
int longThresh = input.int(4, "Long Score >=", minval=1, maxval=7, group=GRP_SG)
int shortThresh = input.int(4, "Short Score >= (absolute)", minval=1, maxval=7, group=GRP_SG)
bool longOnly = input.bool(false, "Long Signals Only", group=GRP_SG)
bool showDash = input.bool(true, "Show Dashboard", group=GRP_SG)
bool showBg = input.bool(true, "Regime Background Tint", group=GRP_SG)
// ── 1. EMA stack ────────────────────────────────────────────────
float emaFast = ta.ema(close, emaFastLen)
float emaSlow = ta.ema(close, emaSlowLen)
float emaLong = ta.ema(close, emaLongLen)
// ── 2. Supertrend ───────────────────────────────────────────────
[stLine, stDir] = ta.supertrend(stMult, stLen)
bool stBull = stDir < 0
bool stBear = stDir > 0
// ── 3. Regime filter (EMA + ADX + directional movement) ─────────
[diPlus, diMinus, adxValue] = ta.dmi(adxLen, adxLen)
bool bullRegime = close > emaLong and adxValue > adxThresh and diPlus > diMinus
bool bearRegime = close < emaLong and adxValue > adxThresh and diMinus > diPlus
bool rangeRegime = not bullRegime and not bearRegime
// ── 4. WaveTrend ────────────────────────────────────────────────
float averagePrice = hlc3
float esa = ta.ema(averagePrice, wtN1)
float deviation = ta.ema(math.abs(averagePrice - esa), wtN1)
float channelIndex = deviation == 0.0 ? 0.0 : (averagePrice - esa) / (0.015 * deviation)
float wt1 = ta.ema(channelIndex, wtN2)
float wt2 = ta.sma(wt1, 4)
// ── 5. StochRSI ─────────────────────────────────────────────────
float rsiValue = ta.rsi(close, rsiLen)
float stochRsi = ta.stoch(rsiValue, rsiValue, rsiValue, stochLen)
float k = ta.sma(stochRsi, smoothK)
float d = ta.sma(k, smoothD)
// ── 6. Confirmed regular WaveTrend divergence ───────────────────
float pivotLowValue = ta.pivotlow(wt1, lbL, lbR)
float pivotHighValue = ta.pivothigh(wt1, lbL, lbR)
bool pivotLowFound = not na(pivotLowValue)
bool pivotHighFound = not na(pivotHighValue)
// barssince() is evaluated on every bar to keep history consistent.
int barsSincePriorLow = ta.barssince(pivotLowFound[1])
int barsSincePriorHigh = ta.barssince(pivotHighFound[1])
bool lowGapOk = not na(barsSincePriorLow) and barsSincePriorLow >= divMinBars and barsSincePriorLow <= divMaxBars
bool highGapOk = not na(barsSincePriorHigh) and barsSincePriorHigh >= divMinBars and barsSincePriorHigh <= divMaxBars
float previousLowOsc = ta.valuewhen(pivotLowFound, wt1[lbR], 1)
float previousLowPrice = ta.valuewhen(pivotLowFound, low[lbR], 1)
float previousHighOsc = ta.valuewhen(pivotHighFound, wt1[lbR], 1)
float previousHighPrice = ta.valuewhen(pivotHighFound, high[lbR], 1)
bool bullDiv = pivotLowFound and lowGapOk and not na(previousLowOsc) and not na(previousLowPrice) and wt1[lbR] > previousLowOsc and low[lbR] < previousLowPrice
bool bearDiv = pivotHighFound and highGapOk and not na(previousHighOsc) and not na(previousHighPrice) and wt1[lbR] < previousHighOsc and high[lbR] > previousHighPrice
// ── 7. ATR trailing stop and range ──────────────────────────────
float atrValue = ta.atr(atrLen)
float newLongStop = close - atrValue * atrMult
float newShortStop = close + atrValue * atrMult
var float longStop = na
var float shortStop = na
if stBull
longStop := stBull[1] ? math.max(newLongStop, nz(longStop[1], newLongStop)) : newLongStop
else
longStop := na
if stBear
shortStop := stBear[1] ? math.min(newShortStop, nz(shortStop[1], newShortStop)) : newShortStop
else
shortStop := na
float stopLine = stBull ? longStop : stBear ? shortStop : na
float rangeUpper = emaSlow + atrValue * rngMult
float rangeLower = emaSlow - atrValue * rngMult
// ── 8. Volume confirmation ──────────────────────────────────────
float volumeSma = ta.sma(volume, volLen)
bool volumeAvailable = not na(volume) and volume > 0
float volumeRatio = volumeAvailable and not na(volumeSma) and volumeSma > 0 ? volume / volumeSma : na
// Symbols without volume data are not blocked. Symbols with volume must meet the threshold.
bool volumeConfirmed = not volumeAvailable or (not na(volumeRatio) and volumeRatio >= volMult)
bool volumeOk = not useVolFilter or volumeConfirmed
// ── 9. Beta from percentage returns, not raw price levels ───────
float benchmarkClose = request.security(benchSym, timeframe.period, close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
float assetReturn = not na(close[1]) and close[1] != 0 ? close / close[1] - 1.0 : na
float benchmarkReturn = not na(benchmarkClose[1]) and benchmarkClose[1] != 0 ? benchmarkClose / benchmarkClose[1] - 1.0 : na
float returnCorrelation = ta.correlation(assetReturn, benchmarkReturn, betaLen)
float assetDeviation = ta.stdev(assetReturn, betaLen)
float benchmarkDeviation = ta.stdev(benchmarkReturn, betaLen)
float beta = not na(returnCorrelation) and not na(assetDeviation) and not na(benchmarkDeviation) and benchmarkDeviation != 0.0 ? returnCorrelation * assetDeviation / benchmarkDeviation : na
bool betaOk = not useBetaFilter or (not na(beta) and beta >= betaMin)
// ── Confluence score: seven directional engines (-7 to +7) ─────
bool emaBull = emaFast > emaSlow
bool emaBear = emaFast < emaSlow
int voteEma = emaBull ? 1 : emaBear ? -1 : 0
int voteSupertrend = stBull ? 1 : stBear ? -1 : 0
int voteRegime = bullRegime ? 1 : bearRegime ? -1 : 0
int voteWaveTrend = wt1 > wt2 ? 1 : wt1 < wt2 ? -1 : 0
int voteStochRsi = k > d ? 1 : k < d ? -1 : 0
int voteDivergence = bullDiv ? 1 : bearDiv ? -1 : 0
int voteAtr = not na(stopLine) and close > stopLine ? 1 : not na(stopLine) and close < stopLine ? -1 : 0
int score = voteEma + voteSupertrend + voteRegime + voteWaveTrend + voteStochRsi + voteDivergence + voteAtr
// Longs must be in a bullish regime; shorts must be in a bearish regime.
bool longSetup = bullRegime and score >= longThresh and volumeOk and betaOk
bool shortSetup = bearRegime and score <= -shortThresh and volumeOk and betaOk and not longOnly
int longState = longSetup ? 1 : 0
int shortState = shortSetup ? 1 : 0
bool longTrigger = barstate.isconfirmed and longState == 1 and nz(longState[1], 0) == 0
bool shortTrigger = barstate.isconfirmed and shortState == 1 and nz(shortState[1], 0) == 0
// ── Signals and alerts ──────────────────────────────────────────
if longTrigger
label.new(bar_index, low, "BUY\n+" + str.tostring(score), style=label.style_label_up, color=color.new(#00E5A0, 0), textcolor=color.black, size=size.small)
if shortTrigger
label.new(bar_index, high, "SELL\n" + str.tostring(score), style=label.style_label_down, color=color.new(#FF4D6D, 0), textcolor=color.white, size=size.small)
plotshape(bullDiv, title="Confirmed Bullish Divergence", style=shape.diamond, location=location.belowbar, color=color.new(#00E5A0, 0), size=size.tiny)
plotshape(bearDiv, title="Confirmed Bearish Divergence", style=shape.diamond, location=location.abovebar, color=color.new(#FF4D6D, 0), size=size.tiny)
alertcondition(longTrigger, title="MCS Long", message="MCS LONG signal on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(shortTrigger, title="MCS Short", message="MCS SHORT signal on {{ticker}} ({{interval}}) at {{close}}")
// ── Chart plots ─────────────────────────────────────────────────
plot(emaFast, title="EMA Fast", color=color.new(#00D4FF, 0), linewidth=2)
plot(emaSlow, title="EMA Slow", color=color.new(#FF9F1C, 0), linewidth=2)
plot(stLine, title="Supertrend", color=stBull ? color.new(#00E5A0, 0) : color.new(#FF4D6D, 0), linewidth=2, style=plot.style_linebr)
plot(stopLine, title="ATR Stop", color=color.new(#FFD60A, 0), linewidth=2, style=plot.style_linebr)
pUpper = plot(showBands ? rangeUpper : na, title="ATR Range Upper", color=color.new(#9D4EDD, 85))
pLower = plot(showBands ? rangeLower : na, title="ATR Range Lower", color=color.new(#9D4EDD, 85))
fill(pUpper, pLower, color=color.new(#9D4EDD, 93), title="ATR Range Fill")
color regimeColor = bullRegime ? color.new(#00E5A0, 93) : bearRegime ? color.new(#FF4D6D, 93) : na
bgcolor(showBg ? regimeColor : na, title="Regime Background")
// ── Dashboard ───────────────────────────────────────────────────
var table dashboard = table.new(position.top_right, 2, 11, border_width=1, border_color=color.new(#FFFFFF, 85))
color bullColor = color.new(#00E5A0, 25)
color bearColor = color.new(#FF4D6D, 25)
color flatColor = color.new(#8A8F98, 25)
color textColor = #E8ECF1
f_dashboardRow(int row, string labelText, int state) =>
color cellColor = state > 0 ? bullColor : state < 0 ? bearColor : flatColor
string stateText = state > 0 ? "▲ BULL" : state < 0 ? "▼ BEAR" : "■ FLAT"
table.cell(dashboard, 0, row, labelText, text_color=textColor, text_size=size.small)
table.cell(dashboard, 1, row, stateText, bgcolor=cellColor, text_color=textColor, text_size=size.small)
if barstate.islast and showDash
string scoreText = (score > 0 ? "+" : "") + str.tostring(score)
color scoreColor = score >= longThresh ? bullColor : score <= -shortThresh ? bearColor : flatColor
table.cell(dashboard, 0, 0, "MCS · " + syminfo.ticker, text_color=color.new(#00D4FF, 0), text_size=size.normal)
table.cell(dashboard, 1, 0, "SCORE " + scoreText, bgcolor=scoreColor, text_color=textColor, text_size=size.normal)
f_dashboardRow(1, "EMA " + str.tostring(emaFastLen) + "/" + str.tostring(emaSlowLen), voteEma)
f_dashboardRow(2, "Supertrend", voteSupertrend)
f_dashboardRow(3, "Regime", voteRegime)
f_dashboardRow(4, "WaveTrend", voteWaveTrend)
f_dashboardRow(5, "StochRSI " + str.tostring(math.round(nz(k, 50))), voteStochRsi)
f_dashboardRow(6, "Divergence", voteDivergence)
f_dashboardRow(7, "ATR Stop", voteAtr)
string volumeText = not volumeAvailable ? "N/A" : volumeConfirmed ? "✓ " + str.tostring(volumeRatio, "#.##") + "x" : "✗ " + str.tostring(volumeRatio, "#.##") + "x"
color volumeColor = not volumeAvailable ? flatColor : volumeConfirmed ? bullColor : bearColor
table.cell(dashboard, 0, 8, "Volume", text_color=textColor, text_size=size.small)
table.cell(dashboard, 1, 8, volumeText, bgcolor=volumeColor, text_color=textColor, text_size=size.small)
table.cell(dashboard, 0, 9, "Beta vs " + benchSym, text_color=textColor, text_size=size.small)
table.cell(dashboard, 1, 9, na(beta) ? "N/A" : str.tostring(beta, "#.##"), bgcolor=flatColor, text_color=textColor, text_size=size.small)
string signalText = longSetup ? "BUY" : shortSetup ? "SELL" : rangeRegime ? "STAND ASIDE" : "WAIT"
color signalColor = longSetup ? bullColor : shortSetup ? bearColor : flatColor
table.cell(dashboard, 0, 10, "Signal", text_color=textColor, text_size=size.small)
table.cell(dashboard, 1, 10, signalText, bgcolor=signalColor, text_color=textColor, text_size=size.small)
Overview
Labels mark when the score meets the long or short threshold, with ATR-derived targets and a plotted stop. Settings change EMA lengths, Supertrend and ATR multipliers, ADX and oscillator periods, pivot gaps, volume and optional beta filters, and score thresholds. A dashboard and regime tint can be shown. Signals wait for a confirmed bar close.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
EMA Fast
int
21
emaFastLen
EMA Slow
int
50
emaSlowLen
EMA Long / Regime
int
200
emaLongLen
ATR Length
int
10
stLen
Multiplier
float
3.0
stMult
DI / ADX Length
int
14
adxLen
ADX Threshold
float
20.0
adxThresh
Channel Length
int
10
wtN1
Average Length
int
21
wtN2
RSI Length
int
14
rsiLen
Stochastic Length
int
14
stochLen
Smooth K
int
3
smoothK
Show all 33 settings
Setting
Type
Default
Variable
Smooth D
int
3
smoothD
Pivot Lookback Left
int
5
lbL
Pivot Lookback Right
int
5
lbR
Minimum Pivot Gap
int
5
divMinBars
Maximum Pivot Gap
int
60
divMaxBars
ATR Length
int
14
atrLen
Stop Multiplier
float
2.0
atrMult
Range Band Multiplier
float
1.5
rngMult
Show ATR Range Bands
bool
true
showBands
Volume SMA Length
int
20
volLen
Volume Multiplier
float
1.5
volMult
Require Volume Confirmation
bool
true
useVolFilter
Benchmark Symbol
symbol
AMEX:SPY
benchSym
Beta Lookback
int
60
betaLen
Minimum Beta
float
0.7
betaMin
Require Minimum Beta
bool
false
useBetaFilter
Long Score >=
int
4
longThresh
Short Score >= (absolute)
int
4
shortThresh
Long Signals Only
bool
false
longOnly
Show Dashboard
bool
true
showDash
Regime Background Tint
bool
true
showBg
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
ADX Strategy (original) is an overlay indicator that identifies trend-following trade signals using the Average Directional Index (ADX) combined with a Simple Moving Average filter. A long signal is generated when the ADX rises above a user-defined trend level
Double Screened Decision is a multi-timeframe trend and momentum indicator overlaid directly on the price chart. It dynamically selects a higher timeframe based on the active chart resolution to evaluate higher-timeframe MACD, Bollinger Bands, RSI, and Stochas
This indicator plots Bollinger Bands on the price chart alongside a baseline moving average to identify trend-aligned swing setups. It calculates trend bias by comparing the Bollinger Bands basis to a customizable moving average, such as a 200 EMA. Trade signa
Index Intraday Analyst overlays CBOE cash indices such as SPX, NDX, RUT, and DJX. It plots a multi-EMA ribbon, a session-anchored TWAP with standard-deviation bands, and session levels such as prior-day high, low, and close, the opening range, and the high and
RePaNoCHa V4 is an overlay alert indicator that plots a Tillson T3 moving average on the price chart and colors it by whether the line is rising or falling. It combines that slope with a range filter, ADX, parabolic SAR, RSI, and MACD to mark long and short si
This indicator plots an exponential moving average and marks momentum-based trade sessions on the price chart. It treats RSI between 45 and 55 as chop and shades the background when momentum is present. Long and short labels appear when MACD crosses its signal