Indicator TutorialPine Script v5

How to Code the Money Flow Index (MFI) in Pine Script

Money flow, positive/negative periods, and overbought/oversold context

The Money Flow Index (MFI) is a volume-weighted oscillator that blends price and participation to score buying versus selling pressure over a window. Often described as a volume-augmented cousin to RSI, MFI uses typical price times volume to accumulate positive and negative money flow. This tutorial breaks down the formula, standard 80/20 reference bands, and a full Pine Script v5 script using ta.mfi() with histogram cues and alerts.

What Is the Money Flow Index?

MFI measures the ratio of positive money flow to total money flow across n periods, scaled to 0–100. It incorporates volume, unlike RSI, which only uses price changes.

Raw money flow is typical price multiplied by volume for each bar. Bars where typical price rises versus the prior bar contribute to positive flow; declines contribute to negative flow.

Interpretation snapshot

High MFI suggests aggressive buying pressure; low MFI suggests aggressive selling pressure—subject to liquidity and data quality constraints.

Why Traders Use This Indicator

  • Gauge pressure with both price direction and volume participation.
  • Spot potential exhaustion when MFI stretches to 80/20 extremes.
  • Compare MFI peaks to price peaks for divergence analysis.
  • Filter breakouts that lack volume-backed money flow support.
ParameterDefaultDescription
Length14Summation window for positive and negative money flow—mirrors common RSI periods for familiarity.
Overbought / oversold80 / 20Traditional bands; trending names may require stricter levels or confirmation.
Volume sourcevolumeSpot instruments use bar volume; thin markets may distort MFI—interpret carefully.
Typical pricehlc3Classic MFI uses typical price for money flow; deviations change sensitivity.

How MFI Is Calculated

Typical Price: TP = (high + low + close) / 3

Raw Money Flow: RMF = TP × volume

Classify each bar: if TP > TP[1], add RMF to positive money flow; if TP < TP[1], add to negative money flow; if equal, handle per specification (often ignored or split).

Money Flow Ratio: MFR = sum(positive MF, n) / sum(negative MF, n)

MFI: 100 - (100 / (1 + MFR))—the same logistic form as RSI but driven by volume-weighted flows.

Pine’s ta.mfi(source, length) aligns with this construction using built-in volume.

Signal Interpretation

Above 80: Often treated as overbought; in strong trends MFI can remain elevated—context matters.

Below 20: Often treated as oversold; verify with structure to avoid catching falling knives.

Midline 50: Some traders treat crosses as broad pressure shifts—usually secondary to 80/20 events.

Divergence: Price higher high with MFI lower high warns bullish pressure may be fading (and vice versa).

Combining with Other Indicators

Pair MFI with VWAP or OBV to cross-check volume narratives.

RSI side-by-side highlights when price-only momentum disagrees with volume-weighted momentum—potential liquidity traps.

Bollinger Bands on price plus MFI extremes can frame volatility squeeze breakouts that actually show flow confirmation.

The Hard Way: Writing Pine Script Manually

Challenges of Manual Coding

Compute money flow sums manually and diff against ta.mfi() bar by bar.

Add a session filter so MFI ignores overnight thin prints on equities.

Plot MFI minus RSI to visualize volume-weighted disagreement.

Require two consecutive closes beyond 80 before signaling exhaustion.

Simulate MFI on tick-approximated volume for education (where data allows).

Pine Script v5
//@version=5
indicator("MFI Tutorial (ta.mfi)", shorttitle="MFI", overlay=false)

// -----------------------------------------------------------------------------
// Inputs
// -----------------------------------------------------------------------------
length = input.int(14, "MFI length", minval=1)
src = input.source(hlc3, "Source (typical price baseline)")
ob = input.float(80.0, "Overbought", step=1.0)
os = input.float(20.0, "Oversold", step=1.0)
showHist = input.bool(true, "Histogram around 50")
showMid = input.bool(true, "Plot 50 midline")

// -----------------------------------------------------------------------------
// Core
// -----------------------------------------------------------------------------
mfi = ta.mfi(src, length)

// -----------------------------------------------------------------------------
// States
// -----------------------------------------------------------------------------
stateOb = mfi > ob
stateOs = mfi < os
crossOb = ta.crossover(mfi, ob)
crossOs = ta.crossunder(mfi, os)
cross50Up = ta.crossover(mfi, 50)
cross50Dn = ta.crossunder(mfi, 50)

// -----------------------------------------------------------------------------
// Plots
// -----------------------------------------------------------------------------
plot(mfi, "MFI", color=stateOb ? color.new(color.red, 0) : stateOs ? color.new(color.green, 0) : color.new(color.teal, 0), linewidth=2)
hline(ob, "OB", color=color.new(color.red, 40))
hline(os, "OS", color=color.new(color.green, 40))
hline(50, "Mid", color=color.new(color.gray, 70), linestyle=hline.style_dotted)

plot(showMid ? 50 : na, "Mid plot", color=color.new(color.gray, 100), linewidth=1, display=display.all)

histColor = mfi >= 50 ? color.new(color.teal, 55) : color.new(color.orange, 55)
plot(showHist ? (mfi - 50) : na, "MFI-50 hist", style=plot.style_histogram, color=histColor)

plotshape(crossOb, "Enter OB", shape.triangledown, location.top, color.new(color.red, 0), size=size.tiny, text="80+")
plotshape(crossOs, "Enter OS", shape.triangleup, location.bottom, color.new(color.green, 0), size=size.tiny, text="20-")

bgcolor(cross50Up ? color.new(color.teal, 92) : cross50Dn ? color.new(color.orange, 92) : na)

// -----------------------------------------------------------------------------
// RSI companion for comparison (data window)
// -----------------------------------------------------------------------------
rsi = ta.rsi(close, length)
plot(rsi, "RSI(length) data window", display=display.data_window)

// -----------------------------------------------------------------------------
// Alerts
// -----------------------------------------------------------------------------
alertcondition(crossOb, "MFI overbought cross", "MFI crossed above overbought level")
alertcondition(crossOs, "MFI oversold cross", "MFI crossed below oversold level")
alertcondition(ta.cross(mfi, 50), "MFI midline cross", "MFI crossed the 50 midpoint")

// -----------------------------------------------------------------------------
// Summary table
// -----------------------------------------------------------------------------
var table tab = table.new(position.top_right, 1, 3, bgcolor=color.new(color.black, 78))
if barstate.islast
    table.cell(tab, 0, 0, "MFI: " + str.tostring(mfi, "#.##"), text_color=color.white)
    table.cell(tab, 0, 1, "RSI: " + str.tostring(rsi, "#.##"), text_color=color.silver)
    table.cell(tab, 0, 2, stateOb ? "Zone: OB" : stateOs ? "Zone: OS" : "Zone: Mid", text_color=color.silver)

Maintenance Note: Low-liquidity symbols can print erratic MFI; consider filtering sessions or increasing length. If volume data is inconsistent on a feed, validate MFI against another data provider before relying on it live.

The Easy Way: Build with Pineify Visual Editor

What if you could create the same indicator without writing a single line of code? Pineify is a visual editor designed for TradingView users who want professional-grade indicators through an intuitive interface.

FeatureManual Pine ScriptPineify Visual Editor
Volume nuanceHand-coded money flow sums are verbose and error-prone.Keeps MFI logic encapsulated and easier to audit.
RSI comparisonMaintaining twin oscillators clutters small scripts.Structured blocks simplify multi-indicator layouts.
Alert wiringMultiple thresholds multiply alertcondition boilerplate.Reusable alert templates reduce copy errors.
VisualizationHistograms, bands, and tables require manual tuning.Accelerates polished, investor-ready charts.
IterationTweaking lengths breaks inline comments and spacing easily.Centralized parameters propagate safely.

Build MFI systems with less repetitive Pine.

Combine MFI with RSI, VWAP, and MA filters visually.

Export consistent v5 for publishing and collaboration.

Scale to multi-condition alerts without losing clarity.

Step-by-Step Tutorial

1

Create oscillator pane

Set overlay=false for MFI beneath price.

2

Insert ta.mfi()

Choose source and length; default hlc3 aligns with textbook TP flows.

3

Draw 80/20 bands

Add hlines and optional fill to emphasize extremes.

4

Add histogram optional

Plot MFI-50 to visualize pressure asymmetry around the midpoint.

5

Compare with RSI

Plot RSI in data window or pane to study disagreements.

6

Publish alerts

Set alertconditions for OB/OS crosses and midline transitions.

Trading Strategies & Pro Tips

Flow-confirmed breakout

Buy resistance breaks when MFI crosses above 50 on rising volume and stays above 50 for two bars.

Pro Tip: Avoid thin prints; require minimum relative volume if available.

Mean reversion with structure

Fade extremes below 20 or above 80 only when price stalls at prior support/resistance.

MFI vs RSI disagreement

When RSI is bullish but MFI lags, wait for MFI to confirm before adding size—signals potential weak participation.

Trend hold filter

In uptrends, ignore short signals unless MFI also breaks below 40, signaling sustained distribution.

Common Mistakes to Avoid

  • Treating 80/20 as automatic reversal points in strong trends.
  • Ignoring bad or missing volume data that distorts money flow sums.
  • Using tiny lengths on illiquid tickers, amplifying noise.
  • Confusing MFI with accumulation/distribution indicators that use different formulas.
  • Overfitting levels to one backtest without forward validation.

Frequently Asked Questions

Compose MFI indicators faster with Pineify

  • Wire ta.mfi() with thresholds, histograms, and alerts efficiently.
  • Export clean Pine v5 for TradingView publishing.
  • Blend MFI with RSI and volume tools in structured flows.

Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.