1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
245 lines of Pine Script v6 from SMT Divergence [r1ckylel].pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
SMT Divergence [r1ckylel].pine
//@version=6
indicator("SMT Divergence [r1ckylel]", overlay = true, max_lines_count = 500, max_labels_count = 500)
// USAGE: Use standard intraday candles. In Manual mode, compare NQ with ES
// using equivalent contract expiries, sessions, and data providers.
// Automatic mode selects the correlated continuous CME contract.
// Reference levels are confirmed pivots paired chronologically, one to one.
// Both levels must remain unbroken when the pair is formed. Signals that
// occurred before both pivots were available are not reconstructed.
// Subsequent breaks are evaluated intrabar when Real-time SMTs is enabled.
// Wicks count; touching a level exactly does NOT count as a break.
// Up to 'capacity' pairs are tracked per side. Exceeding this limit removes
// the oldest pair and its drawing, even if the divergence is still valid.
// Historical OHLC data cannot reconstruct the tick order within a candle.
// Compact input layout. TradingView controls the width of input fields.
const string G_MAIN = "SMT"
const string G_ADVANCED = "ADVANCED"
const string G_ALERTS = "ALERTS"
string assetMode = input.string("Automatic", "Correlated Asset", options = ["Automatic", "Manual"], group = G_MAIN, display = display.none, tooltip = "Automatic: NQ/MNQ → ES1!, ES/MES → NQ1!. Uses continuous CME contracts. For a specific expiry or another market, select Manual.")
string manualAsset = input.symbol("CME_MINI:ES1!", "", group = G_MAIN, active = assetMode == "Manual", display = display.none)
string sensitivity = input.string("Sensitive", "Sensitivity", options = ["Very sensitive", "Sensitive", "Balanced", "Conservative", "Custom"], group = G_MAIN, display = display.none, tooltip = "Pivot bars on each side: 1 / 3 / 5 / 8. Custom uses the value in Advanced. The reference pivot must be confirmed before monitoring a live break.")
int maxVisible = input.int(5, "Maximum visible SMTs", minval = 1, maxval = 50, group = G_MAIN, display = display.none, tooltip = "Total bullish + bearish lines after overlap filtering. Prioritizes the most recent endpoints, then the most recent origins. Hidden pairs continue to be monitored.")
bool realTime = input.bool(true, "Real-time SMTs", group = G_MAIN, tooltip = "Enabled: update and invalidate during the current candle. Disabled: update only at candle close.")
color bullColor = input.color(color.black, "Bullish:", group = G_MAIN, inline = "bull")
string bullWeight = input.string("Thin", "", options = ["Thin", "Medium", "Thick"], group = G_MAIN, inline = "bull", display = display.none)
string bullStyleInput = input.string("Solid", "", options = ["Solid", "Dashed", "Dotted"], group = G_MAIN, inline = "bull", display = display.none)
color bearColor = input.color(color.black, "Bearish:", group = G_MAIN, inline = "bear")
string bearWeight = input.string("Thin", "", options = ["Thin", "Medium", "Thick"], group = G_MAIN, inline = "bear", display = display.none)
string bearStyleInput = input.string("Solid", "", options = ["Solid", "Dashed", "Dotted"], group = G_MAIN, inline = "bear", display = display.none)
bool labelsOn = input.bool(true, "Label", group = G_MAIN, inline = "label")
color customTextColor = input.color(color.gray, "", group = G_MAIN, inline = "label", active = labelsOn)
string labelSizeInput = input.string("Tiny", "Size", options = ["Tiny", "Small", "Normal", "Large"], group = G_MAIN, inline = "label", active = labelsOn, display = display.none)
bool showSymbol = input.bool(true, "Show symbol", group = G_MAIN, inline = "label", active = labelsOn)
bool showBull = input.bool(true, "Show bullish", group = G_ADVANCED, inline = "show")
bool showBear = input.bool(true, "Show bearish", group = G_ADVANCED, inline = "show")
bool filterOverlap = input.bool(true, "One SMT per endpoint", group = G_ADVANCED, tooltip = "Same end candle and price: lowest bullish origin or highest bearish origin wins. Ties favor the most recent origin.")
int customStrength = input.int(3, "Custom sensitivity", minval = 1, maxval = 30, group = G_ADVANCED, active = sensitivity == "Custom", display = display.none)
int tolerance = input.int(3, "Pivot matching distance (bars)", minval = 0, maxval = 30, group = G_ADVANCED, display = display.none)
string bullText = input.string("SMT", "Bullish label", group = G_ADVANCED, active = labelsOn, display = display.none)
string bearText = input.string("SMT", "Bearish label", group = G_ADVANCED, active = labelsOn, display = display.none)
string labelMode = input.string("Text only", "Label style", options = ["Text only", "Background"], group = G_ADVANCED, active = labelsOn, display = display.none)
bool textMatch = input.bool(false, "Match label to line color", group = G_ADVANCED, active = labelsOn)
int labelTransparency = input.int(85, "Background transparency", minval = 0, maxval = 100, group = G_ADVANCED, active = labelsOn and labelMode == "Background", display = display.none)
int capacity = input.int(100, "Tracked pairs per side", minval = 10, maxval = 200, group = G_ADVANCED, display = display.none, tooltip = "Includes hidden and pending pairs. When full, the oldest pair is removed even if still valid. Separate from the visible line limit.")
bool showStatus = input.bool(true, "Show missing-data warning", group = G_ADVANCED)
bool bullAlerts = input.bool(true, "Bullish alerts", group = G_ALERTS, inline = "alerts")
bool bearAlerts = input.bool(true, "Bearish alerts", group = G_ALERTS, inline = "alerts", tooltip = "Create alerts separately in TradingView. Use Once per bar for real-time alerts. Only visible signals notify. Sent alerts cannot be erased; recreate alerts after changing settings.")
bool nqChart = syminfo.root == "NQ" or syminfo.root == "MNQ"
bool esChart = syminfo.root == "ES" or syminfo.root == "MES"
string other = assetMode == "Manual" ? manualAsset : nqChart ? "CME_MINI:ES1!" : "CME_MINI:NQ1!"
if barstate.isfirst and assetMode == "Automatic" and not nqChart and not esChart
runtime.error("Automatic supports NQ, MNQ, ES and MES. Choose Manual for other symbols.")
string otherShort = array.last(str.split(other, ":"))
int strength = sensitivity == "Very sensitive" ? 1 : sensitivity == "Sensitive" ? 3 : sensitivity == "Balanced" ? 5 : sensitivity == "Conservative" ? 8 : customStrength
int bullWidth = bullWeight == "Thin" ? 1 : bullWeight == "Medium" ? 2 : 3
int bearWidth = bearWeight == "Thin" ? 1 : bearWeight == "Medium" ? 2 : 3
string bullStyle = bullStyleInput == "Dashed" ? line.style_dashed : bullStyleInput == "Dotted" ? line.style_dotted : line.style_solid
string bearStyle = bearStyleInput == "Dashed" ? line.style_dashed : bearStyleInput == "Dotted" ? line.style_dotted : line.style_solid
string labelSize = labelSizeInput == "Small" ? size.small : labelSizeInput == "Normal" ? size.normal : labelSizeInput == "Large" ? size.large : size.tiny
if barstate.isfirst and (not chart.is_standard or not timeframe.isintraday)
runtime.error("Use standard candles and an intraday timeframe.")
if barstate.isfirst and ticker.standard(other) == ticker.standard(syminfo.tickerid)
runtime.error("Select a comparison symbol different from the chart symbol.")
[bh, bl, bt] = request.security(other, timeframe.period, [high, low, time], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
bool synced = not na(bt) and bt == time and not na(bh) and not na(bl)
// Do not use stale prices to fill gaps when calculating pivots.
float otherHigh = synced ? bh : na
float otherLow = synced ? bl : na
float coverage = ta.sma(synced ? 1.0 : 0.0, strength * 2 + 1)
float ahp = ta.pivothigh(high, strength, strength)
float alp = ta.pivotlow(low, strength, strength)
float bhp = ta.pivothigh(otherHigh, strength, strength)
float blp = ta.pivotlow(otherLow, strength, strength)
// Normal persistent structures (var) participate in Pine rollback. The current
// candle's accumulated highs and lows allow signal creation and invalidation
// to be recalculated on every update.
type Pair
float a
float b
int anchor
bool sweptA = false
bool sweptB = false
float endPrice = na
int endTime = na
bool notified = false
bool selected = false
line ln = na
label tag = na
type Pending
float price = na
int stamp = na
bool swept = false
var array<Pair> highs = array.new<Pair>()
var array<Pair> lows = array.new<Pair>()
var Pending ha = Pending.new()
var Pending hb = Pending.new()
var Pending la = Pending.new()
var Pending lb = Pending.new()
clearDrawing(Pair p) =>
line.delete(p.ln)
label.delete(p.tag)
p.ln := na
p.tag := na
process(array<Pair> pairs, bool isHigh, float remote, array<Pair> candidates) =>
// First pass: update ALL pairs, including hidden ones.
// Separate state updates from rendering to keep selection independent
// of the order in which pairs are updated.
if array.size(pairs) > 0
for i = array.size(pairs) - 1 to 0
Pair p = array.get(pairs, i)
p.selected := false
p.sweptA := p.sweptA or (isHigh ? high > p.a : low < p.a)
p.sweptB := p.sweptB or (synced and (isHigh ? remote > p.b : remote < p.b))
if p.sweptA and p.sweptB
clearDrawing(p)
array.remove(pairs, i)
else if synced and p.sweptA != p.sweptB
float endpoint = isHigh ? high : low
bool extends = na(p.endPrice) or (isHigh ? endpoint > p.endPrice : endpoint < p.endPrice)
if extends
p.endPrice := endpoint
p.endTime := time
// A shared endpoint means the same end candle and price. Independent
// divergences are not grouped merely because their lines intersect.
// Bullish: lowest origin wins. Bearish: highest origin wins.
// When origin prices tie, select the most recent origin.
if array.size(pairs) > 0
for i = 0 to array.size(pairs) - 1
Pair p = array.get(pairs, i)
if not na(p.endTime)
bool visible = isHigh ? showBear : showBull
if filterOverlap
for j = 0 to array.size(pairs) - 1
if j != i
Pair q = array.get(pairs, j)
bool sameEnd = not na(q.endTime) and q.endTime == p.endTime and q.endPrice == p.endPrice
bool stronger = isHigh ? q.a > p.a : q.a < p.a
bool tieWinner = q.a == p.a and (q.anchor > p.anchor or (q.anchor == p.anchor and j > i))
if sameEnd and (stronger or tieWinner)
visible := false
break
if visible
array.push(candidates, p)
// Limit is global, after overlap filtering. Select references, not copies.
selectVisible(array<Pair> candidates) =>
int count = math.min(maxVisible, array.size(candidates))
if count > 0
for slot = 0 to count - 1
int best = 0
if array.size(candidates) > 1
for i = 1 to array.size(candidates) - 1
Pair candidate = array.get(candidates, i)
Pair incumbent = array.get(candidates, best)
if candidate.endTime > incumbent.endTime or (candidate.endTime == incumbent.endTime and candidate.anchor > incumbent.anchor)
best := i
Pair chosen = array.remove(candidates, best)
chosen.selected := true
renderPairs(array<Pair> pairs, bool isHigh, color tint) =>
bool appeared = false
if array.size(pairs) > 0
for i = 0 to array.size(pairs) - 1
Pair p = array.get(pairs, i)
if not p.selected
clearDrawing(p)
else
if na(p.ln)
p.ln := line.new(p.anchor, p.a, p.endTime, p.endPrice, xloc = xloc.bar_time, color = tint, width = isHigh ? bearWidth : bullWidth, style = isHigh ? bearStyle : bullStyle)
if labelsOn
string caption = (isHigh ? bearText : bullText) + (showSymbol ? " " + otherShort : "")
p.tag := label.new(p.endTime, p.endPrice, caption, xloc = xloc.bar_time, style = labelMode == "Text only" ? label.style_none : (isHigh ? label.style_label_down : label.style_label_up), color = color.new(tint, labelTransparency), textcolor = textMatch ? tint : customTextColor, size = labelSize)
else
line.set_xy2(p.ln, p.endTime, p.endPrice)
if not na(p.tag)
label.set_xy(p.tag, p.endTime, p.endPrice)
if not p.notified
appeared := true
p.notified := true
appeared
pairPending(Pending a, Pending b, array<Pair> pairs) =>
if not na(a.stamp) and not na(b.stamp)
int maxDistance = int(timeframe.in_seconds() * 1000 * tolerance)
if math.abs(a.stamp - b.stamp) <= maxDistance and not a.swept and not b.swept
array.push(pairs, Pair.new(a.price, b.price, a.stamp))
a.stamp := na
b.stamp := na
if array.size(pairs) > capacity
Pair expired = array.shift(pairs)
clearDrawing(expired)
bool newBear = false
bool newBull = false
// Default is intrabar. In close-only mode, committed drawings stay until close.
if realTime or barstate.isconfirmed
array<Pair> candidates = array.new<Pair>()
process(highs, true, bh, candidates)
process(lows, false, bl, candidates)
selectVisible(candidates)
newBear := renderPairs(highs, true, bearColor)
newBull := renderPairs(lows, false, bullColor)
// Reference levels are fixed when their pivots are confirmed. In real-time
// mode, the breakout candle does NOT need to close. Updating pending levels
// before pairing prevents the use of levels that have already been swept.
if not na(ha.stamp)
ha.swept := ha.swept or high > ha.price
if not na(la.stamp)
la.swept := la.swept or low < la.price
if synced
if not na(hb.stamp)
hb.swept := hb.swept or bh > hb.price
if not na(lb.stamp)
lb.swept := lb.swept or bl < lb.price
if barstate.isconfirmed and coverage == 1.0
if not na(ahp)
ha := Pending.new(ahp, time[strength], false)
if not na(alp)
la := Pending.new(alp, time[strength], false)
if not na(bhp)
hb := Pending.new(bhp, time[strength], false)
if not na(blp)
lb := Pending.new(blp, time[strength], false)
pairPending(ha, hb, highs)
pairPending(la, lb, lows)
alertcondition(newBear and bearAlerts, "Provisional bearish SMT", "Provisional bearish SMT divergence. The signal may invalidate; a sent alert cannot be withdrawn.")
alertcondition(newBull and bullAlerts, "Provisional bullish SMT", "Provisional bullish SMT divergence. The signal may invalidate; a sent alert cannot be withdrawn.")
var table status = table.new(position.bottom_right, 1, 1)
if barstate.islast
table.cell(status, 0, 0, (synced or not showStatus) ? "" : "No synchronized data for the comparison symbol", text_color = color.orange, text_size = size.small)
Overview
SMT Divergence pairs confirmed swing highs and lows from both instruments and draws a line and label when one market breaks its reference level and the other does not. The correlated asset is chosen automatically or manually. Settings cover pivot sensitivity, maximum visible signals, overlap filtering, line and label styling, and separate bullish and bearish alerts. In real-time mode signals are provisional and can disappear when invalidated.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Correlated Asset
string
Automatic
assetMode
manualAsset
symbol
CME_MINI:ES1!
manualAsset
Sensitivity
string
Sensitive
sensitivity
Maximum visible SMTs
int
5
maxVisible
Real-time SMTs
bool
true
realTime
Bullish:
color
color.black
bullColor
bullWeight
string
Thin
bullWeight
bullStyleInput
string
Solid
bullStyleInput
Bearish:
color
color.black
bearColor
bearWeight
string
Thin
bearWeight
bearStyleInput
string
Solid
bearStyleInput
Label
bool
true
labelsOn
Show all 29 settings
Setting
Type
Default
Variable
customTextColor
color
color.gray
customTextColor
Size
string
Tiny
labelSizeInput
Show symbol
bool
true
showSymbol
Show bullish
bool
true
showBull
Show bearish
bool
true
showBear
One SMT per endpoint
bool
true
filterOverlap
Custom sensitivity
int
3
customStrength
Pivot matching distance (bars)
int
3
tolerance
Bullish label
string
SMT
bullText
Bearish label
string
SMT
bearText
Label style
string
Text only
labelMode
Match label to line color
bool
false
textMatch
Background transparency
int
85
labelTransparency
Tracked pairs per side
int
100
capacity
Show missing-data warning
bool
true
showStatus
Bullish alerts
bool
true
bullAlerts
Bearish alerts
bool
true
bearAlerts
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.
Bandz HTF projects higher-timeframe candle structures, ranges, and key levels directly onto the main price chart. It visualizes multiple configurable timeframe lanes from intraday to weekly alongside primary and macro range levels, including 25%, 50%, and 75%
Nexural QWAP calculates a volume-weighted average price combined with a cumulative volume delta order flow engine directly on the main price chart. It analyzes market structure by detecting volume absorption, liquidity sweeps, price-CVD divergence, stacked imb
Larper Suite is an overlay indicator for ICT and quarterly-theory analysis. It plots true session, daily, weekly, monthly, and yearly opens plus prior and current high-low liquidity levels. It tracks multi-asset SMT, swing divergence, fair-value-gap fills, and
Apex Protocol v3.0 - The SMC Engine is an overlay indicator designed for Smart Money Concepts (SMC) trading strategies. It detects market structure shifts (MSS), breaks of structure (BOS), M5 Fair Value Gaps (FVG), Order Blocks (OB), and Asian session ranges.
MTF Divergence Scanner detects regular and hidden divergences in RSI or MACD oscillators across multiple timeframes. It scans up to three timeframes simultaneously using request.security with confirmed-bar logic to identify reversal or continuation signals. Th
Strong Wealth Magnet is an overlay indicator that scores price action and draws selected narrative layers on the price chart. Up to three modes can run together, including a Pro layout and a Six Eyes view that reviews past signals over a chosen window and labe