Indicator · Pine Script v6 · Own pane

Commodity FlowTrend [WavesUnchained]

by casoon

Commodity FlowTrend plots a money-flow oscillator in a separate pane.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code341
Settings24inputs
LicenceMIT
Last updateyesterday27 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

341 lines of Pine Script v6 from indicators/money_flow/commodity_flow_trend/commodity_flow_trend.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

commodity_flow_trend.pine
//@version=6
// ============================================================================
// Commodity FlowTrend [WavesUnchained]
// Version: 1.0.6
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// MFI + CCI composite oscillator tuned for commodity markets. Combines
// money-flow momentum (MFI) with cycle-sensitive trend context (CCI) to
// identify flow regimes: expansion, accumulation, distribution, and reversal.
//
// Features:
//   ✓ Manual MFI with pluggable smoothing (EMA, SMA, RMA, WMA)
//   ✓ CCI confirmation layer (normalized to MFI scale for visual alignment)
//   ✓ 4-state flow background: bull/bear expansion vs. accumulation/distribution
//   ✓ 4-state color-coded MFI histogram
//   ✓ Extreme-zone reversal signals with optional CCI gating
//   ✓ Midline confirmation dots (MFI crosses 50 with aligned CCI)
//   ✓ Alert conditions for all signal types
//
// Data Contract:
//   Price:     REQUIRED   chart symbol
//   Volume:    REQUIRED   real trade volume — feed must deliver volume and
//                         syminfo.volumetype must not be "tick"; otherwise not available
//   OI:        NO
//   Reference: NO
//   Verdict:   Exchange-only
// ============================================================================

indicator("Commodity FlowTrend [WavesUnchained]", "CFT", format=format.price, precision=2, overlay=false, max_labels_count=500)

// ─────────────────────────────────────────────────────────────────────────────
// INPUTS
// ─────────────────────────────────────────────────────────────────────────────

g_base = "1 · Base"
src    = input.source(hlc3, "Source",         group=g_base)
mfiLen = input.int(14,      "MFI Length",     minval=1, group=g_base)
cciLen = input.int(20,      "CCI Length",     minval=1, group=g_base)
sigLen = input.int(5,       "Signal Length",  minval=1, group=g_base)

g_smooth = "2 · Smoothing"
smoothType = input.string("EMA", "Type",
     options=["EMA", "SMA", "RMA", "WMA", "SuperSmoother", "T3", "KAMA", "JMA Approx"],
     group=g_smooth)
jmaPhase = input.int(50,    "  JMA Phase", minval=-100, maxval=100,             group=g_smooth)
jmaPower = input.float(2.0, "  JMA Power", minval=1.0,  maxval=4.0, step=0.25, group=g_smooth)

g_levels = "3 · Levels"
mfiOB  = input.float(80, "MFI Overbought", group=g_levels)
mfiOS  = input.float(20, "MFI Oversold",   group=g_levels)
mfiMid = input.float(50, "MFI Midline",    group=g_levels)

g_filter = "4 · Signal Filters"
triggerMode     = input.string("MFI Lead", "Trigger Mode", options=["MFI Lead", "CCI Lead"], group=g_filter,
     tooltip="MFI Lead: MFI cross triggers, CCI confirms. CCI Lead is a legacy timing option and still requires MFI momentum confirmation.")
useCCIConfirm   = input.bool(true, "Require CCI Confirmation",   group=g_filter)
useExtremeOnly  = input.bool(true, "Only Signal From OB/OS Zone", group=g_filter)
lookbackExtreme = input.int(8,     "OB/OS Lookback",             minval=1, group=g_filter)
usePriceTrendFilter = input.bool(true, "Use Price Trend Filter (EMA)", group=g_filter,
     tooltip="If enabled, bullish signals are blocked below the EMA, and bearish signals are blocked above it.")
priceTrendLen       = input.int(200, "Price Trend EMA Length", minval=10, group=g_filter)

g_col = "5 · Colors"
colBull = input.color(#26a69a, "Bull", group=g_col)
colBear = input.color(#ef5350, "Bear", group=g_col)

g_display = "6 · Display"
showBg      = input.bool(false, "Show Flow Background",    group=g_display)
showHist    = input.bool(true,  "Show MFI Histogram",      group=g_display)
showCCI     = input.bool(true,  "Show CCI Normalized",     group=g_display)
showSignals  = input.bool(true,  "Show Signals",            group=g_display)
showCCITurn = input.bool(true,  "Show CCI Turn Markers",   group=g_display,
     tooltip="Small diamonds when CCI turns from an extreme zone (±100) — fires before the MFI cross. Bull turn = CCI was below -100 and now reversing up. Bear turn = CCI was above +100 and now reversing down.")

// Echtes Handelsvolumen? Nur "tick" schließt es ausdrücklich aus (Anzahl
// Transaktionen, keine Menge). "n/a" heißt laut Referenz "kein Volumen ODER Art
// nicht angegeben" — die meisten Feeds außer Krypto melden das, auch Futures mit
// echten Kontrakten. Dort entscheidet, ob der Feed überhaupt Volumen liefert.
var bool feedHasVolume = false
if nz(volume) > 0
    feedHasVolume := true
bool volumeIsReal = syminfo.volumetype != "tick" and feedHasVolume
float vol = volumeIsReal ? volume : na

// ─────────────────────────────────────────────────────────────────────────────
// SMOOTHING
// ─────────────────────────────────────────────────────────────────────────────

f_supersmoother(series float x, simple int len) =>
    float a1 = math.exp(-1.414 * math.pi / len)
    float b1 = 2.0 * a1 * math.cos(1.414 * math.pi / len)
    float c2 = b1
    float c3 = -(a1 * a1)
    float c1 = 1.0 - c2 - c3
    var float ss = na
    ss := c1 * (x + nz(x[1])) / 2.0 + c2 * nz(ss[1]) + c3 * nz(ss[2])
    ss

f_t3(series float x, simple int len) =>
    float b  = 0.7
    float e1 = ta.ema(x, len)
    float e2 = ta.ema(e1, len)
    float e3 = ta.ema(e2, len)
    float e4 = ta.ema(e3, len)
    float e5 = ta.ema(e4, len)
    float e6 = ta.ema(e5, len)
    float c1 = -(b * b * b)
    float c2 =  3.0 * b * b + 3.0 * b * b * b
    float c3 = -6.0 * b * b - 3.0 * b - 3.0 * b * b * b
    float c4 =  1.0 + 3.0 * b + b * b * b + 3.0 * b * b
    c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3

f_kama(series float x, simple int len) =>
    float change   = math.abs(x - x[len])
    float vol      = math.sum(math.abs(x - x[1]), len)
    float er       = vol != 0.0 ? change / vol : 0.0
    float sc       = math.pow(er * (2.0 / 3.0 - 2.0 / 31.0) + 2.0 / 31.0, 2)
    var float kama = na
    kama := nz(kama[1], x) + sc * (x - nz(kama[1], x))
    kama

f_jma(series float x, simple int len, simple int phase, simple float power) =>
    var float jma = na
    var float e0  = na
    var float e1  = na
    var float e2  = na
    float phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100.0 + 1.5
    float beta       = 0.45 * (len - 1) / (0.45 * (len - 1) + 2.0)
    float alpha      = math.pow(beta, power)
    e0  := (1.0 - alpha) * x + alpha * nz(e0[1], x)
    e1  := (x - e0) * (1.0 - beta) + beta * nz(e1[1], 0.0)
    e2  := (e0 + phaseRatio * e1 - nz(jma[1], x)) * math.pow(1.0 - alpha, 2.0) + math.pow(alpha, 2.0) * nz(e2[1], 0.0)
    jma := nz(jma[1], x) + e2
    jma

f_smooth(series float x, simple int len, simple string mode) =>
    switch mode
        "SMA"           => ta.sma(x, len)
        "RMA"           => ta.rma(x, len)
        "WMA"           => ta.wma(x, len)
        "SuperSmoother" => f_supersmoother(x, len)
        "T3"            => f_t3(x, len)
        "KAMA"          => f_kama(x, len)
        "JMA Approx"    => f_jma(x, len, jmaPhase, jmaPower)
        =>                 ta.ema(x, len)

// ─────────────────────────────────────────────────────────────────────────────
// MFI
// ─────────────────────────────────────────────────────────────────────────────

rawMoneyFlow  = src * vol
positiveFlow  = src > src[1] ? rawMoneyFlow : 0.0
negativeFlow  = src < src[1] ? rawMoneyFlow : 0.0
positiveSum   = math.sum(positiveFlow, mfiLen)
negativeSum   = math.sum(negativeFlow, mfiLen)

mfiRaw = not volumeIsReal   ? na    :
         negativeSum == 0.0 ? 100.0 :
         positiveSum == 0.0 ? 0.0   :
         100.0 - (100.0 / (1.0 + positiveSum / negativeSum))

mfi       = f_smooth(mfiRaw, sigLen, smoothType)
mfiSignal = f_smooth(mfi,    sigLen, smoothType)
hist      = mfi - mfiSignal


// ─────────────────────────────────────────────────────────────────────────────
// CCI (normalized to 0–100 for visual alignment with MFI)
// ─────────────────────────────────────────────────────────────────────────────

cciRaw  = ta.cci(src, cciLen)
cci     = f_smooth(cciRaw, sigLen, smoothType)
cciNorm = math.max(math.min(50.0 + cci / 4.0, 100.0), 0.0)

// ─────────────────────────────────────────────────────────────────────────────
// FLOW STATE
// ─────────────────────────────────────────────────────────────────────────────

mfiBull    = mfi > mfiMid
mfiBear    = mfi < mfiMid
mfiRising  = mfi > mfi[1]
mfiFalling = mfi < mfi[1]
cciBullish = cci > 0.0
cciBearish = cci < 0.0

bullExpansion    = mfiBull  and cciBullish and mfiRising
bearExpansion    = mfiBear  and cciBearish and mfiFalling
bullAccumulation = not mfiBull and mfiRising  and cci > cci[1]
bearDistribution = not mfiBear and mfiFalling and cci < cci[1]

// ─────────────────────────────────────────────────────────────────────────────
// SIGNALS
// ─────────────────────────────────────────────────────────────────────────────

wasOversold   = ta.lowest(mfi,  lookbackExtreme) <= mfiOS
wasOverbought = ta.highest(mfi, lookbackExtreme) >= mfiOB

bullCross = ta.crossover(mfi,  mfiSignal)
bearCross = ta.crossunder(mfi, mfiSignal)

bullConfirm = not useCCIConfirm or cciBullish or cci > cci[1]
bearConfirm = not useCCIConfirm or cciBearish or cci < cci[1]

// Price-based Trend Filter (EMA)
priceTrend = ta.ema(close, priceTrendLen)
priceTrendBull = not usePriceTrendFilter or close > priceTrend
priceTrendBear = not usePriceTrendFilter or close < priceTrend

// MFI Lead: MFI cross triggers, CCI confirms, price trend filters
bool _bullMFI = bullCross and bullConfirm and (not useExtremeOnly or wasOversold)  and priceTrendBull
bool _bearMFI = bearCross and bearConfirm and (not useExtremeOnly or wasOverbought) and priceTrendBear

// CCI Lead: CCI cross triggers, MFI momentum confirms, price trend filters
// (the CCI confirm term is always true on a CCI zero cross, so MFI direction is
// the actual confirmation; without real volume MFI is na and CCI Lead stays silent
// like every other MFI-based signal)
bool cciCrossUp   = ta.crossover(cci, 0.0)
bool cciCrossDown = ta.crossunder(cci, 0.0)
bool cciWasOS     = ta.lowest(cci,  lookbackExtreme) <= -100.0
bool cciWasOB     = ta.highest(cci, lookbackExtreme) >=  100.0
bool _bullCCI = cciCrossUp   and mfiRising  and (not useExtremeOnly or cciWasOS) and priceTrendBull
bool _bearCCI = cciCrossDown and mfiFalling and (not useExtremeOnly or cciWasOB) and priceTrendBear

bool bullSignal = triggerMode == "CCI Lead" ? _bullCCI : _bullMFI
bool bearSignal = triggerMode == "CCI Lead" ? _bearCCI : _bearMFI

// MFI soft quality filter
bullQuality = mfi > mfiMid
bearQuality = mfi < mfiMid

bullMidConfirm = ta.crossover(mfi,  mfiMid) and cciBullish
bearMidConfirm = ta.crossunder(mfi, mfiMid) and cciBearish

// CCI turn from extreme: local inflection while still in OB/OS zone → early warning
bool cciTurningUp   = cci > cci[1] and cci[1] <= cci[2] and cci[1] < -100.0
bool cciTurningDown = cci < cci[1] and cci[1] >= cci[2] and cci[1] >  100.0

// ─────────────────────────────────────────────────────────────────────────────
// COLORS
// ─────────────────────────────────────────────────────────────────────────────

mfiLineColor = color.from_gradient(mfi, mfiOS, mfiOB, colBull, colBear)
sigColor     = mfi >= mfiSignal ? color.new(colBull, 30) : color.new(colBear, 30)

// 4-state histogram
histRising = hist >= hist[1]
histColor  = hist >= 0.0
     ? (histRising     ? color.new(colBull, 20) : color.new(colBull, 60))
     : (not histRising ? color.new(colBear, 20) : color.new(colBear, 60))

bgColor =
     bullExpansion    ? color.new(colBull, 84) :
     bullAccumulation ? color.new(colBull, 91) :
     bearExpansion    ? color.new(colBear, 84) :
     bearDistribution ? color.new(colBear, 91) :
     na

// ─────────────────────────────────────────────────────────────────────────────
// HLINES
// ─────────────────────────────────────────────────────────────────────────────

hline(100,   "100",        color=color.new(color.gray, 80))
hline(mfiOB, "Overbought", color=color.new(colBear, 45), linestyle=hline.style_dashed)
hline(mfiMid,"Midline",    color=color.new(color.gray, 50), linestyle=hline.style_dotted)
hline(mfiOS, "Oversold",   color=color.new(colBull, 45), linestyle=hline.style_dashed)
hline(0,     "0",          color=color.new(color.gray, 80))

// ─────────────────────────────────────────────────────────────────────────────
// PLOTS
// ─────────────────────────────────────────────────────────────────────────────

bgcolor(showBg ? bgColor : na, title="Flow Background")

// Histogram centered on midline
plot(showHist ? hist + mfiMid : na, "MFI Histogram",
     style=plot.style_columns, color=histColor)

// CCI normalized (behind MFI line)
plot(showCCI ? cciNorm : na, "CCI Normalized",
     color=color.new(color.orange, 15), linewidth=1)

// MFI lines
pSig = plot(mfiSignal, "MFI Signal", color=sigColor,       linewidth=1)
pMFI = plot(mfi,       "MFI",        color=mfiLineColor,   linewidth=2)

// Fill between MFI and signal
fill(pMFI, pSig,
     color=mfi >= mfiSignal ? color.new(colBull, 88) : color.new(colBear, 88),
     title="MFI Fill")

// ─────────────────────────────────────────────────────────────────────────────
// SIGNAL MARKERS
// ─────────────────────────────────────────────────────────────────────────────

plotshape(showSignals and bullSignal ? mfi - 6.0 : na, "Bull Signal",
     style=shape.triangleup, location=location.absolute,
     color=bullQuality ? colBull : color.new(colBull, 50), size=size.tiny)

plotshape(showSignals and bearSignal ? mfi + 6.0 : na, "Bear Signal",
     style=shape.triangledown, location=location.absolute,
     color=bearQuality ? colBear : color.new(colBear, 50), size=size.tiny)

// Midline confirmation dots — placed on the MFI line
plotshape(showSignals and bullMidConfirm ? mfi : na, "Bull Mid Confirm",
     style=shape.circle, location=location.absolute,
     color=color.new(colBull, 0), size=size.tiny)

plotshape(showSignals and bearMidConfirm ? mfi : na, "Bear Mid Confirm",
     style=shape.circle, location=location.absolute,
     color=color.new(colBear, 0), size=size.tiny)

// CCI turn markers — small diamonds on the CCI line, fires before MFI cross
plotshape(showCCITurn and cciTurningUp   ? cciNorm : na, "CCI Bull Turn",
     style=shape.diamond, location=location.absolute,
     color=color.new(colBull, 20), size=size.tiny)
plotshape(showCCITurn and cciTurningDown ? cciNorm : na, "CCI Bear Turn",
     style=shape.diamond, location=location.absolute,
     color=color.new(colBear, 20), size=size.tiny)

// ─────────────────────────────────────────────────────────────────────────────
// DATA VALIDITY WARNING
// ─────────────────────────────────────────────────────────────────────────────

// Kurzer Hinweis unten rechts; was fehlt, steht im Data Contract oben.
var table volWarn = table.new(position.bottom_right, 1, 1)
if barstate.islast and not volumeIsReal
    table.cell(volWarn, 0, 0, "CFT · MFI not available",
         bgcolor = color.new(color.orange, 20), text_color = color.white, text_size = size.small)

// ─────────────────────────────────────────────────────────────────────────────
// ALERTS
// ─────────────────────────────────────────────────────────────────────────────

bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed

alertcondition(alertOk and (bullSignal),     "CFT Bull Signal",           "CFT · LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearSignal),     "CFT Bear Signal",           "CFT · SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (bullMidConfirm), "CFT Bull Midline Confirm",  "CFT · BULL MIDLINE CONFIRM · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearMidConfirm), "CFT Bear Midline Confirm",  "CFT · BEAR MIDLINE CONFIRM · {{ticker}} {{interval}}")

Overview

Commodity FlowTrend [WavesUnchained] calculates a manually smoothed Money Flow Index from price and real trade volume, overlays a CCI reading scaled to the same range, and colors a histogram plus an optional background by expansion, accumulation, distribution, or reversal regimes. Signals fire when MFI or CCI leads a cross, optionally only after an overbought or oversold visit, with CCI confirmation and a price EMA trend filter. Lengths, smoothing type, levels, and display toggles are adjustable. It is unavailable when volume is tick-based.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

Separate pane below the chart

Category

Oscillators & momentum, Volume

Markets

Oil & commodities

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Sourcesourcehlc3src
MFI Lengthint14mfiLen
CCI Lengthint20cciLen
Signal Lengthint5sigLen
TypestringEMAsmoothType
JMA Phaseint50jmaPhase
JMA Powerfloat2.0jmaPower
MFI Overboughtfloat80mfiOB
MFI Oversoldfloat20mfiOS
MFI Midlinefloat50mfiMid
Trigger ModestringMFI LeadtriggerMode
Require CCI ConfirmationbooltrueuseCCIConfirm
Show all 24 settings
SettingTypeDefaultVariable
Only Signal From OB/OS ZonebooltrueuseExtremeOnly
OB/OS Lookbackint8lookbackExtreme
Use Price Trend Filter (EMA)booltrueusePriceTrendFilter
Price Trend EMA Lengthint200priceTrendLen
Bullcolor#26a69acolBull
Bearcolor#ef5350colBear
Show Flow BackgroundboolfalseshowBg
Show MFI HistogrambooltrueshowHist
Show CCI NormalizedbooltrueshowCCI
Show SignalsbooltrueshowSignals
Show CCI Turn MarkersbooltrueshowCCITurn
Alerts only on bar close (confirmed)booltruealertsOnBarClose
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

Money Flow Index

by everget
948 stars

Calculates the standard Money Flow Index (MFI) oscillator alongside an exponential moving average signal line in a separate pane. The script visually highlights trend conditions through dynamic ribbon filling, optional difference histogram bars, and overbought

Indicator v4 Oscillators & momentumVolume
Updated 10 days ago · GPL-3.0 GitHub repository of Money Flow Index

Commitment Gauge

by harryguiacorn
219 stars

This indicator measures directional momentum by evaluating candlestick body proportions and volume over a user-defined lookback window. It computes the ratio of candle body to total range, weights the cumulative value by volume and absolute body size, and dete

Indicator v4 VolumeCandlestick patterns
Updated 1 year ago · None stated GitHub repository of Commitment Gauge

Aggregate Signal

by capissimo
114 stars

This indicator builds an aggregate oscillator in a separate pane. It scales open, high, low, close, and volume into a 0 to 1 range, then averages scaled readings from MACD, a MACD histogram, RSI, stochastic, CCI, momentum, OBV, a directional index, volume-weig

Indicator v4 Oscillators & momentumVolume Repaints
Updated 1 year ago · None stated GitHub repository of Aggregate Signal

This indicator calculates the Bill Williams Market Facilitation Index as the high-low range divided by volume, or zero when volume is zero. It compares the index and volume with the previous bar and assigns one of four zones: green for both rising, fade for bo

Indicator v6 VolumeOscillators & momentum
Updated 4 weeks ago · Apache-2.0 GitHub repository of Bill Williams Market Facilitation Index

PPO ATR Bands

by cryptorife
75 stars

PPO ATR Bands plots a percentage price oscillator in a separate pane. It compares a Hull average of typical price with a volume-weighted exponential average of Parabolic SAR, then scales three true-range bands around the slow average into matching oscillator l

Indicator v4 Oscillators & momentumMoving averages
Updated 3 years ago · GPL-3.0 GitHub repository of PPO ATR Bands

Chaikin Oscillator

by be-thomas
65 stars

Calculates the Chaikin Oscillator by taking the difference between a fast exponential moving average and a slow exponential moving average of the Accumulation/Distribution line. It plots the resulting line in a separate pane alongside a dashed zero reference l

Indicator v4 Oscillators & momentumVolume
Updated 4 days ago · GPL-3.0 GitHub repository of Chaikin Oscillator

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers