Paste the script into Pine Editor. The default Fixture mode uses the first loaded chart bar as bar 0, holds 290 after bar 11, and needs at least 12 completed bars for the full table. Use the Data Window to locate bars and read numerical residuals. The pane shows either percentage units or price units, controlled by the input switch, so the two unit systems never share its plotted scale.
Pine Script v6 indicator with explicit seeds, denominator rejection and completed-bar output. It contains no orders or backtest.pine
//@version=6
indicator("PPO vs MACD: scale audit", overlay = false, precision = 6)
mode = input.string("Fixture", "Source", options = ["Fixture", "Chart close", "Zero", "Negative", "Near zero"])
showPpo = input.bool(true, "Plot percent units (off = price units)")
insertGap = input.bool(false, "Fixture: omit bar 4")
fastLength = input.int(3, "Fast EMA", minval = 1, maxval = 500)
slowLength = input.int(5, "Slow EMA", minval = 2, maxval = 500)
signalLength = input.int(3, "Signal EMA", minval = 1, maxval = 500)
floorValue = input.float(0.000001, "Minimum slow EMA (base price units)", minval = 0.000000000001)
if fastLength >= slowLength
runtime.error("Fast EMA must be shorter than slow EMA")
float fixture = switch bar_index
0 => 100.0
1 => 110.0
2 => 130.0
3 => 160.0
4 => 200.0
5 => 240.0
6 => 260.0
7 => 270.0
8 => 275.0
9 => 280.0
10 => 285.0
=> 290.0
float src = mode == "Chart close" ? close : mode == "Zero" ? 0.0 : mode == "Negative" ? -fixture : mode == "Near zero" ? fixture * 1e-10 : fixture
if insertGap and mode == "Fixture" and bar_index == 4
src := na
metrics(float x, float minSlow) =>
var float fast = na
var float slow = na
var float macdSignal = na
var float ppoSignal = na
float af = 2.0 / (fastLength + 1)
float al = 2.0 / (slowLength + 1)
float ag = 2.0 / (signalLength + 1)
if not na(x)
fast := na(fast) ? x : af * x + (1.0 - af) * fast
slow := na(slow) ? x : al * x + (1.0 - al) * slow
float macd = na(x) ? na : fast - slow
if not na(macd)
macdSignal := na(macdSignal) ? macd : ag * macd + (1.0 - ag) * macdSignal
bool valid = not na(x) and not na(slow) and slow > minSlow
float ppo = valid ? 100.0 * macd / slow : na
if valid
ppoSignal := na(ppoSignal) ? ppo : ag * ppo + (1.0 - ag) * ppoSignal
else
ppoSignal := na
[macd, na(x) ? na : macdSignal, ppo, ppoSignal, slow, valid]
[m, ms, p, ps, slow, valid] = metrics(src, floorValue)
[m10, ms10, p10, ps10, slow10, valid10] = metrics(src * 10.0, floorValue * 10.0)
mh = m - ms
ph = p - ps
bool confirmed = barstate.isconfirmed
bool macdDown = mh < 0 and mh[1] >= 0
bool ppoDown = ph < 0 and ph[1] >= 0
plot(confirmed ? (showPpo ? p : m) : na, "Base oscillator", color.blue, 2, plot.style_linebr)
plot(confirmed ? (showPpo ? ps : ms) : na, "Base signal", color.orange, 1, plot.style_linebr)
plot(confirmed ? (showPpo ? ph : mh) : na, "Base histogram", color.gray, style = plot.style_columns)
plot(confirmed ? (showPpo ? p10 : m10) : na, "10x oscillator, same selected units", color.purple, style = plot.style_circles)
hline(0, "Zero")
plotshape(confirmed and macdDown, "MACD cross below signal", shape.triangledown, location.top, color.blue, text = "M")
plotshape(confirmed and ppoDown, "PPO cross below signal", shape.triangledown, location.bottom, color.orange, text = "P")
plot(bar_index, "Fixture bar", display = display.data_window)
plot(confirmed ? slow : na, "Base slow EMA, price units", display = display.data_window)
plot(confirmed ? slow10 : na, "10x slow EMA, price units", display = display.data_window)
plot(confirmed ? m : na, "MACD, price units", display = display.data_window)
plot(confirmed ? ms : na, "MACD signal, price units", display = display.data_window)
plot(confirmed ? p : na, "PPO, percent", display = display.data_window)
plot(confirmed ? ps : na, "PPO signal, percent", display = display.data_window)
plot(confirmed ? m10 - 10.0 * m : na, "MACD scale residual", display = display.data_window)
plot(confirmed ? ms10 - 10.0 * ms : na, "MACD signal scale residual", display = display.data_window)
plot(confirmed ? p10 - p : na, "PPO scale residual", display = display.data_window)
plot(confirmed ? ps10 - ps : na, "PPO signal scale residual", display = display.data_window)
plot(confirmed ? (valid and valid10 ? 1 : 0) : na, "PPO valid: 1=yes, 0=rejected", display = display.data_window)
bgcolor(confirmed and not valid ? color.new(color.red, 85) : na, title = "PPO rejected")