TradingView indicator comparisons

MFI vs CMF: typical-price flow vs close location

MFI assigns price-times-volume to positive or negative flow according to the change in typical price. CMF weights volume by the close within each bar's high-low range. They can disagree on identical data: the three-bar example below gives MFI 75.75 and CMF −0.0556. A gap up can raise typical price while the candle closes near its low. Choose the measurement your rule needs; neither calculation identifies actual net cash entering a security.

See the decision guide

Two formulas answer different questions

TradingView documents MFI as a 0 to 100 oscillator and CMF on a −1 to +1 scale. For positive prices, nonnegative volume and valid candles, the formulas below produce those bounds. The sign of an individual CMF contribution does not depend on the previous close, the candle open or the direction of typical price.

  • The MFI form above is equivalent to 100 − 100 / (1 + P/N) when N is positive. It also gives 100 for positive-only flow and 0 for negative-only flow without dividing by a zero negative-flow sum.
  • Both are transformations of OHLCV. Neither observes buyer-initiated trades, seller-initiated trades, fund subscriptions or redemptions. A disagreement is a difference between measurements, not evidence that one data feed is wrong.
CalculationMFICMF
Price inputTP = (high + low + close) / 3Multiplier = (2 × close − high − low) / (high − low)
Per-bar contributionRaw flow = TP × volume; assign to P if TP rises, N if it falls; neither if unchanged.Money flow volume = multiplier × volume. Upper-half close is positive; lower-half close is negative.
Rolling result over N bars100 × sum(P) / (sum(P) + sum(N))sum(money flow volume) / sum(volume)
Midpoint meaning50 means positive and negative classified flow sums match.0 means positive and negative close-location contributions cancel.
Common chart conventionTradingView default length is 14; 80 and 20 are conventional extreme levels.TradingView describes 20 or 21 periods as common. Zero is the sign boundary.

Use this same OHLCV sequence for both indicators

These seven bars are invented arithmetic inputs, not market history or a backtest. Prices are positive units; volumes are hypothetical traded units. Use a three-bar window so every contribution is visible. Bar 0 supplies the initial typical-price reference. Open is included to show the gaps, although neither formula uses it. Values displayed below are rounded; retain the fractions during calculation.

BarOpen / high / low / closeVolumeTypical priceMFI assignmentCMF multiplier × volume
0100 / 102 / 98 / 100100100Reference0 × 100 = 0
1110 / 112 / 108 / 109300329/3 = 109.6667Positive: 32,900−0.5 × 300 = −150
2105 / 107 / 103 / 106100316/3 = 105.3333Negative: 31,600/3+0.5 × 100 = +50
3104 / 108 / 102 / 106200316/3 = 105.3333Unchanged: neither+1/3 × 200 = +200/3
4107 / 107 / 107 / 107100107Positive: 10,700Zero-range policy: 0
5107 / 110 / 106 / 1090325/3 = 108.3333Positive direction, zero flow+0.5 × 0 = 0
6109 / 111 / 107 / 110Missing328/3 = 109.3333UnavailableUnavailable

Recalculate the window before interpreting the conflict

At bar 3, the window is bars 1, 2 and 3. MFI has P = 32,900 and N = 31,600/3, so MFI = 100 × 98,700 / 130,300 = 75.7483. CMF has weighted volume = −150 + 50 + 200/3 = −100/3 and total volume = 600, so CMF = −1/18 = −0.055556. More classified MFI flow is positive, yet the volume-weighted close location is below the range midpoint.

  • Bar 1 gaps entirely above bar 0, but closes at 109 in a 108 to 112 range. Its typical price rises, while its CMF multiplier is −0.5. With length 1, that pair alone gives MFI 100 and CMF −0.5.
  • Bar 2 gaps below bar 1 and its typical price falls, yet its close is in the upper half of its range. With length 1 it gives MFI 0 and CMF +0.5. This is the opposite conflict on the same dataset.
  • Bar 3 has exactly the same TP as bar 2 because both H + L + C sums equal 316. MFI omits its flow from P and N. CMF includes +200/3 in its numerator and 200 in its denominator.
  • At bar 4, MFI = 100 × 10,700 / (10,700 + 31,600/3) = 50.3925. CMF = (50 + 200/3) / 400 = 0.291667. The zero-range bar dilutes CMF toward zero under the stated policy.
  • A zero-volume new bar can still coincide with a changed indicator because an old bar leaves the rolling window. Bar 5 reaches MFI 100 for that reason, not because new positive traded flow arrived.
Completed barWindowMFICMFWhat changed
0, 1, 2Fewer than 3 classified barsNANAShared comparison waits for a full window and its prior price.
31, 2, 375.7483−0.055556The gap-up bar has the largest volume and negative close location.
42, 3, 450.3925+0.291667Bar 1 leaves; bar 4 adds positive MFI flow and CMF denominator volume.
53, 4, 5100.0000+0.222222The only negative MFI bar leaves; the new zero-volume bar adds no flow.
64, 5, 6NANAMissing volume invalidates the fixed window; it is not replaced with zero.

Make undefined inputs explicit

The script uses custom, fixed-window handling so the comparison can be audited. It does not claim exact parity with every built-in indicator around missing data or startup. It requires N current bars with reported volume and N + 1 valid high/low/close observations. CMF by itself needs only N price bars; this paired comparison deliberately waits for the extra MFI reference.

  • Multiplying every volume in the window by the same positive constant leaves both outputs unchanged. Multiplying all prices, including the prior TP, by a positive constant also leaves them unchanged. Changing volume only on one bar generally does not.
  • Volume can describe traded shares, contracts or price updates, depending on the feed. TradingView documents missing and tick-volume cases. Keep the exchange-qualified symbol, provider, timeframe and session fixed; a tick-weighted result is not total traded capital.
  • The length counts chart bars, not elapsed calendar days. A bar absent from the chart is not an explicit NA row; the script cannot infer an unreported observation. Use standard candles and record session and adjustment settings.
ConditionPolicy in the example and script
Flat TPUse strict greater/less comparisons on unrounded TP; equality contributes to neither MFI sum. If P + N = 0, MFI is NA, not 50. CMF may remain defined.
High equals lowWhen close also equals that price, assign CMF multiplier 0 and retain volume in the denominator. MFI still compares TP to the prior TP.
One reported zero volumeKeep the bar in the N-bar window with zero flow. Do not skip it or extend the lookback.
All N volumes zeroBoth denominators are zero: return NA for both. Do not emit disagreement alerts.
Missing H, L, C or current-window volumeReturn NA for both for that window. Never use nz(volume) to fabricate a zero observation. A missing price also invalidates the next bar's MFI reference.
Invalid candle or negative volumeReject nonpositive prices, high below low, close outside the range, or negative volume. Open does not enter either formula.

Run the comparison in Pine Script v6

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.

  • The blue plot is 2 × MFI / 100 − 1 so both lines fit one pane. MFI 50 maps to zero; the original value remains in the table and Data Window. Rescaling does not make equal heights equivalent evidence or convert MFI 80 into a calibrated CMF threshold.
  • Both plots and the disagreement condition wait for barstate.isconfirmed. The alert is a state test on every eligible closed bar, not only the first crossing. Create the alert with Once Per Bar Close; equality at MFI 50 or CMF 0 is not a strict conflict.
  • Fixture rows 0 through 2 are deliberately hidden at the start of every seven-bar cycle, so only windows inside that cycle are displayed. Row 6 is unavailable because its volume is missing. The fixture does not represent the chart symbol or its prices.
  • A successful syntax check establishes acceptance of the source, not executed numerical parity. The table values were independently recalculated with exact fractions; confirm the fixture in TradingView before adapting the indicator.
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")

Choose a rule by what it is intended to measure

Use MFI when the rule asks how price-times-volume is split between rising and falling typical-price bars. Use CMF when it asks whether volume coincides with closes near the upper or lower part of each candle. The gap example explains why using both can select fewer events. It does not establish that those events are more reliable.

  • For a strategy experiment, define entries, exits, position size, execution timing, fees and slippage before comparing outcomes. Keep the evaluation dates and data fixed; reserve separate data to evaluate any chosen thresholds.
  • Ask the Pine Script AI Coding Agent to adapt the checked comparison with your selected rule, shared inputs and NA policy. Request a syntax check explicitly, then review the first differing bars and the resulting order logic. Code generation does not replace that calculation review.
Where Pineify fits

Pine Script AI Coding Agent

Draft a comparison or strategy from your chosen MFI and CMF rules, request a syntax check, and review the shared data, bar-close timing and missing-value policy.

Adapt the comparison rules

Frequently asked questions

Educational indicator comparison, not investment advice. The OHLCV fixture is synthetic and contains no trades or return estimates. Syntax validation does not prove numerical behavior or trading performance; review the calculations and test any order rules separately.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.