Paste the complete indicator into TradingView Pine Editor and add it to a standard candle chart. Chart mode uses one shared length and the same chart OHLCV for both formulas. Enable the teaching fixture to replace those inputs with the seven rows above, repeating every seven chart bars; fixture mode fixes length at 3. Read Fixture row and the original MFI in the Data Window. Rows 3, 4 and 5 should match the result table; row 6 should be NA.
Pine v6 comparison with explicit window validation, optional synthetic inputs and completed-bar disagreement alerts. This is an indicator, not an order strategy.pine
//@version=6
indicator("MFI vs CMF: shared-window audit", overlay = false, max_bars_back = 210)
lengthInput = input.int(3, "Shared length", minval = 1, maxval = 200)
useFixture = input.bool(false, "Use repeating seven-bar teaching fixture")
length = useFixture ? 3 : lengthInput
validPrice(float h, float l, float c) =>
not na(h) and not na(l) and not na(c) and l > 0 and h >= l and c >= l and c <= h
compareFlow(float h, float l, float c, float v, int n) =>
float positive = 0.0
float negative = 0.0
float weighted = 0.0
float totalVolume = 0.0
bool valid = true
for i = 0 to n - 1
bool rowValid = validPrice(h[i], l[i], c[i]) and validPrice(h[i + 1], l[i + 1], c[i + 1]) and not na(v[i]) and v[i] >= 0
if rowValid
float tp = (h[i] + l[i] + c[i]) / 3.0
float previousTp = (h[i + 1] + l[i + 1] + c[i + 1]) / 3.0
float rawFlow = tp * v[i]
positive += tp > previousTp ? rawFlow : 0.0
negative += tp < previousTp ? rawFlow : 0.0
float multiplier = h[i] == l[i] ? 0.0 : (2.0 * c[i] - h[i] - l[i]) / (h[i] - l[i])
weighted += multiplier * v[i]
totalVolume += v[i]
else
valid := false
float mfi = valid and positive + negative > 0 ? 100.0 * positive / (positive + negative) : na
float cmf = valid and totalVolume > 0 ? weighted / totalVolume : na
[mfi, cmf]
// Fixture rows 0..6 repeat. Open is supplied in the article but neither formula uses it.
var fixtureHigh = array.from(102.0, 112.0, 107.0, 108.0, 107.0, 110.0, 111.0)
var fixtureLow = array.from(98.0, 108.0, 103.0, 102.0, 107.0, 106.0, 107.0)
var fixtureClose = array.from(100.0, 109.0, 106.0, 106.0, 107.0, 109.0, 110.0)
var fixtureVolume = array.from(100.0, 300.0, 100.0, 200.0, 100.0, 0.0, float(na))
int fixtureRow = bar_index % 7
float h = useFixture ? array.get(fixtureHigh, fixtureRow) : high
float l = useFixture ? array.get(fixtureLow, fixtureRow) : low
float c = useFixture ? array.get(fixtureClose, fixtureRow) : close
float v = useFixture ? array.get(fixtureVolume, fixtureRow) : volume
[mfi, cmf] = compareFlow(h, l, c, v, length)
// Display and evaluate both at the same completed chart bar.
bool displayReady = barstate.isconfirmed and (not useFixture or fixtureRow >= 3)
float closedMfi = displayReady ? mfi : na
float closedCmf = displayReady ? cmf : na
plot(2.0 * closedMfi / 100.0 - 1.0, "MFI rescaled to -1..1", color = color.blue, linewidth = 2, style = plot.style_linebr)
plot(closedCmf, "CMF", color = color.orange, linewidth = 2, style = plot.style_linebr)
plot(closedMfi, "MFI original 0..100", display = display.data_window)
plot(useFixture ? fixtureRow : na, "Fixture row", display = display.data_window)
hline(0.0, "MFI 50 / CMF 0")
bool comparable = not na(closedMfi) and not na(closedCmf)
bool disagree = comparable and ((closedMfi > 50 and closedCmf < 0) or (closedMfi < 50 and closedCmf > 0))
plotchar(disagree, title = "Opposite midpoint sides", char = "!", location = location.top, color = color.purple)
alertcondition(disagree, "MFI/CMF disagreement", "Completed bar: MFI and CMF are on opposite sides of their midpoints.")
formatValue(float value) =>
na(value) ? "NA" : str.tostring(value, "#.0000")
var audit = table.new(position.top_right, 2, 4, border_width = 1)
if barstate.islast
table.cell(audit, 0, 0, useFixture ? "Fixture row" : "Shared length")
table.cell(audit, 1, 0, str.tostring(useFixture ? fixtureRow : length))
table.cell(audit, 0, 1, "MFI (original)")
table.cell(audit, 1, 1, formatValue(closedMfi))
table.cell(audit, 0, 2, "CMF")
table.cell(audit, 1, 2, formatValue(closedCmf))
table.cell(audit, 0, 3, "State")
table.cell(audit, 1, 3, not barstate.isconfirmed ? "Waiting for close" : not comparable ? "Unavailable" : disagree ? "Disagree" : "No strict conflict")