Paste this indicator into Pine Editor and add it to a standard intraday candle chart, such as 60 minutes. Choose three exchange-qualified symbols in Settings. All requests use daily bars. RSI and MACD calculations run separately in each requested symbol's context; the code does not reuse the host chart's oscillator for other symbols. The displayed UTC close time identifies the daily bar evaluated, not the time a user first saw the row.
Three daily request contexts, four returned values per request, no orders or alert calls. Defaults: RSI 14; MACD line 12/26; strict price pivots 2/2.pine
//@version=6
indicator("Confirmed RSI / MACD divergence: three symbols", overlay = true)
int leftBars = input.int(2, "Pivot left bars", minval = 1, maxval = 20)
int rightBars = input.int(2, "Pivot right bars", minval = 1, maxval = 20)
int rsiLength = input.int(14, "RSI length", minval = 2, maxval = 100)
int fastLength = input.int(12, "MACD fast EMA", minval = 2, maxval = 100)
int slowLength = input.int(26, "MACD slow EMA", minval = 3, maxval = 200)
string symbol1 = input.symbol("NASDAQ:AAPL", "Symbol 1")
string symbol2 = input.symbol("NASDAQ:MSFT", "Symbol 2")
string symbol3 = input.symbol("NASDAQ:NVDA", "Symbol 3")
if not timeframe.isintraday or timeframe.in_seconds() >= timeframe.in_seconds("1D") or not chart.is_standard
runtime.error("Use standard intraday candles, such as 60 minutes.")
if fastLength >= slowLength
runtime.error("MACD fast length must be less than slow length.")
// A strict price pivot: tied highs/lows do not qualify.
pivot(float source, bool isLow) =>
float value = source[rightBars]
for i = 0 to leftBars + rightBars
if i != rightBars and (na(source[i]) or (isLow ? source[rightBars] >= source[i] : source[rightBars] <= source[i]))
value := na
value
scan() =>
float r = ta.rsi(close, rsiLength)
float m = ta.ema(close, fastLength) - ta.ema(close, slowLength)
float lo = pivot(low, true)
float hi = pivot(high, false)
bool hasLow = not na(lo)
bool hasHigh = not na(hi)
int minimum = math.max(rsiLength, slowLength - 1)
float prevLow = ta.valuewhen(hasLow, lo, 1)
float prevHigh = ta.valuewhen(hasHigh, hi, 1)
float prevLowR = ta.valuewhen(hasLow, r[rightBars], 1)
float prevHighR = ta.valuewhen(hasHigh, r[rightBars], 1)
float prevLowM = ta.valuewhen(hasLow, m[rightBars], 1)
float prevHighM = ta.valuewhen(hasHigh, m[rightBars], 1)
int prevLowIndex = ta.valuewhen(hasLow, bar_index - rightBars, 1)
int prevHighIndex = ta.valuewhen(hasHigh, bar_index - rightBars, 1)
bool ready = bar_index - rightBars >= minimum and not na(r[rightBars]) and not na(m[rightBars])
bool lowPair = hasLow and ready and not na(prevLowR) and not na(prevLowM) and prevLowIndex >= minimum
bool highPair = hasHigh and ready and not na(prevHighR) and not na(prevHighM) and prevHighIndex >= minimum
bool rb = lowPair and lo < prevLow and r[rightBars] > prevLowR
bool rs = highPair and hi > prevHigh and r[rightBars] < prevHighR
bool mb = lowPair and lo < prevLow and m[rightBars] > prevLowM
bool ms = highPair and hi > prevHigh and m[rightBars] < prevHighM
int base = not ready ? -2 : not hasLow and not hasHigh ? 0 : not lowPair and not highPair ? -3 : 3
int rState = rb and rs ? 2 : rb ? 1 : rs ? -1 : base
int mState = mb and ms ? 2 : mb ? 1 : ms ? -1 : base
int pivotTime = hasLow or hasHigh ? time[rightBars] : na
// Every returned field is offset inside the requested daily context.
[rState[1], mState[1], time_close[1], pivotTime[1]]
[r1, m1, t1, p1] = request.security(symbol1, "1D", scan(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on, ignore_invalid_symbol = true)
[r2, m2, t2, p2] = request.security(symbol2, "1D", scan(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on, ignore_invalid_symbol = true)
[r3, m3, t3, p3] = request.security(symbol3, "1D", scan(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on, ignore_invalid_symbol = true)
state(int value) =>
switch value
1 => "Regular bullish"
-1 => "Regular bearish"
2 => "Bullish + bearish"
-2 => "Warm-up"
-3 => "Need eligible pair"
3 => "Pair: no divergence"
=> "No new pivot"
stamp(int value) =>
na(value) ? "N/A" : str.format_time(value, "yyyy-MM-dd HH:mm", "UTC")
var table results = table.new(position.top_right, 5, 4, border_width = 1)
row(int n, string symbol, int r, int m, int t, int p) =>
array<string> cells = array.from(symbol, stamp(t), stamp(p), na(t) ? "No data" : state(r), na(t) ? "No data" : state(m))
for col = 0 to 4
table.cell(results, col, n, array.get(cells, col), text_color = color.white, bgcolor = color.new(color.navy, 15))
if barstate.islast
array<string> headers = array.from("Symbol", "Evaluated close UTC", "Pivot open UTC", "RSI", "MACD line")
for col = 0 to 4
table.cell(results, col, 0, array.get(headers, col), text_color = color.white, bgcolor = color.navy)
row(1, symbol1, r1, m1, t1, p1)
row(2, symbol2, r2, m2, t2, p2)
row(3, symbol3, r3, m3, t3, p3)