Open the selected up-volume symbol, initially USI:UVOL, on a standard 1D TradingView chart, then paste this Pine v6 indicator into Pine Editor. Select a compatible down-volume feed. The indicator uses completed chart bars and matching source timestamps, reads close from each statistical index, and keeps gaps as NA. It updates once per available daily bar. It deliberately stops on intraday, multi-day or nonstandard charts.
Pine v6 syntax validated with Pineify. Syntax validation does not verify live feed values, permissions or provider revisions.pine
//@version=6
indicator("McClellan Volume Oscillator: daily audit", overlay=false, precision=4)
upSymbol = input.symbol("USI:UVOL", "Up-volume symbol")
downSymbol = input.symbol("USI:DVOL", "Down-volume symbol")
ratioAdjusted = input.bool(false, "Ratio-adjusted input x 1000")
rawDivisor = input.float(1.0, "Raw input divisor (same units for both feeds)", minval=0.000001)
startDate = input.time(timestamp("01 Jan 2020 00:00 +0000"), "Earliest seed date")
if not timeframe.isdaily or timeframe.multiplier != 1 or not chart.is_standard
runtime.error("Use a standard 1D chart.")
if ticker.standard(syminfo.tickerid) != upSymbol
runtime.error("Open the selected up-volume symbol as the chart to use its session calendar.")
[upVolume, upTime] = request.security(upSymbol, "1D", [close, time], gaps=barmerge.gaps_on, lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
[downVolume, downTime] = request.security(downSymbol, "1D", [close, time], gaps=barmerge.gaps_on, lookahead=barmerge.lookahead_off, ignore_invalid_symbol=true)
validPair = not na(upVolume) and not na(downVolume) and not na(upTime) and not na(downTime) and upTime == downTime and upTime == time and upVolume >= 0 and downVolume >= 0 and upVolume + downVolume > 0
float x = validPair ? (ratioAdjusted ? 1000.0 * (upVolume - downVolume) / (upVolume + downVolume) : (upVolume - downVolume) / rawDivisor) : na
var float fast = na
var float slow = na
var int samples = 0
if barstate.isconfirmed
if time < startDate or not validPair
fast := na
slow := na
samples := 0
else
fast := na(fast) ? x : 0.10 * x + 0.90 * fast
slow := na(slow) ? x : 0.05 * x + 0.95 * slow
samples += 1
ready = barstate.isconfirmed and validPair and time >= startDate
plot(ready ? fast - slow : na, "Volume oscillator", color=color.blue, linewidth=2, style=plot.style_linebr)
hline(0, "Zero", color=color.gray)
plot(ready ? x : na, "Input X", display=display.data_window)
plot(ready ? fast : na, "10% trend (19 EMA)", display=display.data_window)
plot(ready ? slow : na, "5% trend (39 EMA)", display=display.data_window)
plot(upVolume, "Up volume: feed units", display=display.data_window)
plot(downVolume, "Down volume: feed units", display=display.data_window)
plot(samples, "Valid observations since seed", display=display.data_window)
var table status = table.new(position.top_right, 1, 2)
if barstate.islast
state = time < startDate ? "Before seed date" : not barstate.isconfirmed ? "Waiting for daily close" : not validPair ? "Unavailable: check feeds and dates" : "Valid observations: " + str.tostring(samples)
table.cell(status, 0, 0, ratioAdjusted ? "Ratio-adjusted x 1000" : "Raw difference / divisor", text_color=color.white, bgcolor=color.navy)
table.cell(status, 0, 1, state, text_color=color.white, bgcolor=color.navy)