What it does
The Money Flow Index (MFI) is an oscillator from 0 to 100 that combines price and volume. It is often described as a volume-weighted version of the RSI. Readings near the top suggest strong buying pressure over the lookback, readings near the bottom strong selling pressure.
This version shades the overbought and oversold zones, highlights the pane while MFI is inside one, and marks the bar where MFI leaves a zone. Exits are often more useful than entries into a zone, because an oscillator can stay overbought for a long time in a strong trend.
How it works
- Typical price = the chosen source (hlc3 by default).
- Raw money flow = typical price × volume.
- Over the last MFI length bars, money flow is summed separately for bars where typical price rose (positive flow) and fell (negative flow).
- MFI = 100 − 100 ÷ (1 + positive flow ÷ negative flow).
- Exit from overbought: a closed bar where MFI is below the overbought level after being at or above it on the previous bar, with at least the minimum number of bars spent inside the zone. Exit from oversold mirrors this.
- Alerts are also available for entering each zone.
How to use it
An exit from oversold says selling pressure measured by MFI has eased. An exit from overbought says buying pressure has eased. Neither is a reversal signal on its own, but many traders use them as timing triggers in the direction of a larger setup, for example looking for long entries near support only after MFI leaves the oversold zone.
Some practical points:
- In strong trends, consider only the exit that agrees with the trend (oversold exits in an uptrend).
- Raising "Min bars inside zone" filters brief one-bar touches.
- Levels of 80/20 are common; 70/30 gives more frequent signals.
Settings
| Input | Default | What it changes |
|---|---|---|
| MFI length | 14 | Lookback of the oscillator. |
| Source | hlc3 | Typical price used. |
| Overbought level | 80 | Upper zone threshold. |
| Oversold level | 20 | Lower zone threshold. |
| Min bars inside zone before an exit counts | 1 | Filters short zone visits. |
| Shade pane while MFI is in a zone | on | Background highlight. |
| Show zone-exit markers | on | Triangles on exit bars. |
Limitations
- MFI depends on volume. On spot forex, TradingView shows tick volume from one data feed, which is a proxy for activity.
- Like other oscillators, MFI can stay in a zone for many bars during trends, and exits can be followed by a quick return into the zone.
- It reacts to closing prices of completed bars; the signal appears only when the bar closes.
- Symbols without volume data produce no MFI and the script stops with an error.
- Not a standalone system. Combine it with price structure and risk management.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_MFIZones.pine
66 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/money-flow-index-zones/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
indicator("FXP Money Flow Index Zones", shorttitle = "FXP MFI Zones", overlay = false)
// ── Inputs ─────────────────────────────────────────────────────────────
grpM = "Money Flow Index"
mfiLen = input.int(14, "MFI length", minval = 2, group = grpM)
src = input.source(hlc3, "Source", group = grpM)
grpZ = "Zones"
obLevel = input.float(80.0, "Overbought level", minval = 50.0, maxval = 100.0, step = 1.0, group = grpZ)
osLevel = input.float(20.0, "Oversold level", minval = 0.0, maxval = 50.0, step = 1.0, group = grpZ)
minStay = input.int(1, "Min bars inside zone before an exit counts", minval = 1, group = grpZ)
grpVis = "Display"
showBg = input.bool(true, "Shade pane while MFI is in a zone", group = grpVis)
showSig = input.bool(true, "Show zone-exit markers", group = grpVis)
obCol = input.color(color.red, "Overbought color", group = grpVis)
osCol = input.color(color.teal, "Oversold color", group = grpVis)
// ── Calculation ────────────────────────────────────────────────────────
float mfi = ta.mfi(src, mfiLen)
bool inOB = mfi >= obLevel
bool inOS = mfi <= osLevel
// Consecutive bars spent inside each zone, counted up to the previous bar.
var int obBars = 0
var int osBars = 0
int obBarsPrev = obBars
int osBarsPrev = osBars
obBars := inOB ? obBars + 1 : 0
osBars := inOS ? osBars + 1 : 0
bool enterOB = inOB and not inOB[1]
bool enterOS = inOS and not inOS[1]
// Exits: MFI leaves the zone after spending at least minStay bars inside it.
bool exitOB = barstate.isconfirmed and not inOB and inOB[1] and obBarsPrev >= minStay and not na(mfi)
bool exitOS = barstate.isconfirmed and not inOS and inOS[1] and osBarsPrev >= minStay and not na(mfi)
float cumVol = ta.cum(nz(volume))
if barstate.islast and cumVol == 0
runtime.error("This symbol provides no volume data, so MFI cannot be calculated.")
// ── Plots ──────────────────────────────────────────────────────────────
hOB = hline(obLevel, "Overbought", color = color.new(color.red, 40), linestyle = hline.style_dashed)
hMid = hline(50, "Midline", color = color.new(color.gray, 60), linestyle = hline.style_dotted)
hOS = hline(osLevel, "Oversold", color = color.new(color.teal, 40), linestyle = hline.style_dashed)
hTop = hline(100, "Top", color = color.new(color.gray, 100))
hBot = hline(0, "Bottom", color = color.new(color.gray, 100))
fill(hOB, hTop, color = color.new(color.red, 92), title = "Overbought zone")
fill(hOS, hBot, color = color.new(color.teal, 92), title = "Oversold zone")
plot(mfi, "MFI", color = inOB ? obCol : inOS ? osCol : color.purple, linewidth = 2)
bgcolor(showBg and inOB ? color.new(obCol, 88) : showBg and inOS ? color.new(osCol, 88) : na)
plotshape(showSig and exitOB ? mfi : na, "Exit from overbought", shape.triangledown, location.absolute, obCol, size = size.tiny)
plotshape(showSig and exitOS ? mfi : na, "Exit from oversold", shape.triangleup, location.absolute, osCol, size = size.tiny)
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(exitOB, "MFI exited overbought", "FXP MFI Zones: MFI dropped back below overbought on {{ticker}} {{interval}}")
alertcondition(exitOS, "MFI exited oversold", "FXP MFI Zones: MFI rose back above oversold on {{ticker}} {{interval}}")
alertcondition(enterOB and barstate.isconfirmed, "MFI entered overbought", "FXP MFI Zones: MFI entered the overbought zone on {{ticker}} {{interval}}")
alertcondition(enterOS and barstate.isconfirmed, "MFI entered oversold", "FXP MFI Zones: MFI entered the oversold zone on {{ticker}} {{interval}}")

