Indicator · Pine Script v6 · On the chart

Momentum Profile [WavesUnchained]

by casoon

This overlay indicator builds a horizontal momentum profile over a chosen lookback.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code251
Settings15inputs
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

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

momentum_profile.pine
//@version=6
// ============================================================================
// Momentum Profile [WavesUnchained]
// Version: 1.0.3
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// Analog to Money Flow Delta Profile but for momentum: maps average WaveTrend
// and MFI values per price zone to identify dominant-momentum levels (strong moves
// backed by oscillator conviction) vs mixed levels (fragile zones likely to be
// revisited).
//
// Features:
//   ✓ WaveTrend average per price zone — bars extend right from anchor, colored by direction
//   ✓ MFI average per zone as colored reference band (green = volume-backed, red = selling)
//   ✓ Momentum POC — zone with highest absolute average WaveTrend
//   ✓ Configurable lookback, rows, profile width and offset
//   ✓ Dashboard with overall bias, average WT, average MFI, mPOC
//
// Data Contract:
//   Price:     REQUIRED   chart symbol
//   Volume:    OPTIONAL   real trade volume only — MFI band degrades to neutral
//   OI:        NO
//   Reference: NO
//   Verdict:   CFD-degraded
//
// ============================================================================

indicator("Momentum Profile [WavesUnchained]", "Mom Profile [WU]", overlay=true,
     max_bars_back=1500, max_boxes_count=500, max_lines_count=100)

// ─── Inputs ───────────────────────────────────────────────────────────────────

grP  = "Profile"
rpLN = input.int(200, "Lookback",          minval=10, maxval=1500, step=10, group=grP)
rpNR = input.int(25,  "Rows",              minval=10, maxval=100,  step=5,  group=grP)

grWT    = "WaveTrend"
wtChLen = input.int(10, "Channel Length",  minval=1, group=grWT)
wtAvLen = input.int(21, "Average Length",  minval=1, group=grWT)

grMfi   = "MFI"
showMfi = input.bool(true, "Show MFI Reference Band", group=grMfi,
     tooltip="Colors the reference bar by average MFI at that zone. Green = net buying pressure, red = net selling, gray = neutral/mixed. Needs real trade volume; without it the band stays neutral gray.")
mfiLen  = input.int(14, "MFI Length", minval=1, group=grMfi)

grV          = "Visualization"
showPOC      = input.bool(true,  "Momentum POC",        group=grV)
showDash     = input.bool(true,  "Dashboard",           group=grV)
showRange    = input.bool(false, "Show Lookback Range", group=grV)
profileWInp  = input.int(60,    "Profile Width (bars)", minval=10, maxval=200, group=grV)
vpHO         = input.int(40,    "Bar Offset",           group=grV)

bullC = input.color(color.new(#26a69a, 20), "Bull",       inline="c1", group=grV)
bearC = input.color(color.new(#ef5350, 20), "Bear",       inline="c1", group=grV)
refN  = input.color(color.new(color.gray, 80), "Ref/POC", inline="c2", group=grV)
pocC  = input.color(#ffd600,               "",            inline="c2", group=grV)

// ─── State ────────────────────────────────────────────────────────────────────

var array<box>   drwBoxes  = array.new<box>()
var array<line>  drwLines  = array.new<line>()
var array<label> drwLabels = array.new<label>()
var table        dash      = na

// ─── Series computed every bar for history access ─────────────────────────────

float pLo = ta.lowest(low,   rpLN)
float pHi = ta.highest(high, rpLN)

_wtEsa = ta.ema(hlc3, wtChLen)
_wtD   = ta.ema(math.abs(hlc3 - _wtEsa), wtChLen)
_wtCi  = (hlc3 - _wtEsa) / (0.015 * math.max(_wtD, syminfo.mintick))
wtOsc  = ta.ema(_wtCi, wtAvLen)

_mfiTyp = (high + low + close) / 3
_mfiRaw = _mfiTyp * math.max(nz(volume), 1.0)
_mfiPos = math.sum(_mfiTyp > _mfiTyp[1] ? _mfiRaw : 0.0, mfiLen)
_mfiNeg = math.sum(_mfiTyp < _mfiTyp[1] ? _mfiRaw : 0.0, mfiLen)
_mfiVal = _mfiNeg == 0 ? 100.0 : 100.0 - 100.0 / (1.0 + _mfiPos / _mfiNeg)
// MFI needs real trade volume; without it the series is na and the profile treats it as neutral (50)
bool volumeIsReal = (ta.cum(nz(volume)) > 0 and syminfo.volumetype != "tick") and not na(volume)
mfiSer  = volumeIsReal ? _mfiVal : na

// ─── Compute and draw (last bar only) ─────────────────────────────────────────

if barstate.islast

    rpLN_adj = math.min(rpLN - 1, last_bar_index)

    if not na(pLo) and pHi > pLo

        for b in drwBoxes
            box.delete(b)
        for l in drwLines
            line.delete(l)
        for lb in drwLabels
            label.delete(lb)
        drwBoxes.clear()
        drwLines.clear()
        drwLabels.clear()

        pSTP = (pHi - pLo) / rpNR

        // Per-row accumulators (weighted by overlap fraction)
        wtSum    = array.new_float(rpNR, 0.0)
        mfiSum   = array.new_float(rpNR, 0.0)
        rowCount = array.new_float(rpNR, 0.0)  // summed overlap fractions (bar-equivalents)

        for bI = rpLN_adj to 0
            h_   = high[bI]
            l_   = low[bI]
            wt_  = nz(wtOsc[bI])
            mfi_ = nz(mfiSer[bI], 50.0)

            if h_ > l_
                for r = 0 to rpNR - 1
                    rowLo = pLo + r * pSTP
                    rowHi = rowLo + pSTP
                    if h_ >= rowLo and l_ < rowHi
                        ovLo = math.max(l_, rowLo)
                        ovHi = math.min(h_, rowHi)
                        frac = (ovHi - ovLo) / (h_ - l_)
                        wtSum.set(r,    wtSum.get(r)  + wt_ * frac)
                        mfiSum.set(r,   mfiSum.get(r) + mfi_ * frac)
                        rowCount.set(r, rowCount.get(r) + frac)

        // Per-row averages and max|avgWT|
        wtAvg    = array.new_float(rpNR, 0.0)
        mfiAvg   = array.new_float(rpNR, 50.0)
        maxAbsWT = 0.0
        pocIdx   = 0

        for r = 0 to rpNR - 1
            cnt = rowCount.get(r)
            if cnt > 0
                av = wtSum.get(r) / cnt
                wtAvg.set(r,  av)
                mfiAvg.set(r, mfiSum.get(r) / cnt)
                if math.abs(av) > maxAbsWT
                    maxAbsWT := math.abs(av)
                    pocIdx   := r

        if maxAbsWT == 0.0
            maxAbsWT := 1.0

        pocPrice = pLo + (pocIdx + 0.5) * pSTP

        profileW = profileWInp
        maxOH    = math.max(int(profileW / 2), 1)
        anchorBI = bar_index + vpHO + 1
        rightBI  = anchorBI + profileW

        if showRange
            drwBoxes.push(box.new(bar_index - rpLN_adj, pHi, bar_index, pLo,
                 border_color=color.new(color.orange, 40),
                 bgcolor=color.new(color.orange, 92)))

        // Profile rows
        for r = 0 to rpNR - 1
            yBot = pLo + r * pSTP
            yTop = pLo + (r + 1) * pSTP
            if yTop <= yBot + syminfo.mintick
                yTop := yBot + syminfo.mintick
            gap  = pSTP * 0.08
            yLo  = yBot + gap
            yHi  = yTop - gap
            if yHi <= yLo + syminfo.mintick
                yLo := yBot
                yHi := yTop

            avgWT  = wtAvg.get(r)
            avgMfi = mfiAvg.get(r)
            cnt    = rowCount.get(r)

            // MFI reference band
            color refColor = refN
            if showMfi and cnt > 0
                if avgMfi > 60
                    refColor := color.new(#00c853, 85)
                else if avgMfi < 40
                    refColor := color.new(#ef5350, 85)
            drwBoxes.push(box.new(anchorBI, yHi, rightBI, yLo,
                 border_color=color.new(color.gray, 70),
                 bgcolor=refColor))

            // WaveTrend momentum bar (center-out)
            wtRatio = math.abs(avgWT) / maxAbsWT
            wtW     = math.max(int(wtRatio * maxOH), 0)
            if wtW > 0
                barColor = avgWT >= 0 ? bullC : bearC
                split    = anchorBI + math.max(wtW * 2 / 3, 1)
                tip      = anchorBI + wtW
                drwBoxes.push(box.new(anchorBI, yHi, split, yLo, color(na), bgcolor=color.new(barColor, 10)))
                if split < tip
                    drwBoxes.push(box.new(split, yHi, tip, yLo, color(na), bgcolor=color.new(barColor, 55)))

        // Anchor line
        drwLines.push(line.new(anchorBI, pHi, anchorBI, pLo,
             xloc=xloc.bar_index, color=color.new(color.gray, 40),
             style=line.style_solid, width=2))

        // Momentum POC line
        if showPOC
            drwLines.push(line.new(anchorBI, pocPrice, rightBI, pocPrice,
                 xloc=xloc.bar_index, color=pocC,
                 style=line.style_dashed, width=2))
            drwLabels.push(label.new(rightBI + 1, pocPrice, "mPOC",
                 xloc=xloc.bar_index, yloc=yloc.price,
                 color=color(na), style=label.style_none,
                 textcolor=pocC, size=size.small,
                 textalign=text.align_left))

        // Dashboard
        if showDash
            wtTotal   = wtSum.sum()
            cntTotal  = rowCount.sum()
            avgAllWT  = cntTotal > 0 ? wtTotal / cntTotal : 0.0
            avgAllMfi = cntTotal > 0 ? mfiSum.sum() / cntTotal : 50.0
            biasC     = avgAllWT > 5 ? color.new(#00c853, 0) : avgAllWT < -5 ? color.new(#d50000, 0) : color.new(color.gray, 20)
            color tc  = color.new(color.gray, 20)
            color hd  = color.new(color.gray, 90)

            if na(dash)
                dash := table.new(position.top_right, 2, 5,
                     bgcolor=color.new(color.white, 5),
                     border_color=color.new(color.gray, 60), border_width=1,
                     frame_color=color.new(color.gray, 40), frame_width=1)

            table.cell(dash, 0, 0, "Momentum Profile",
                 text_color=tc, bgcolor=hd, text_size=size.small)
            table.cell(dash, 1, 0, str.tostring(rpLN_adj + 1) + "b",
                 text_color=tc, bgcolor=hd, text_size=size.small)
            table.cell(dash, 0, 1, "mPOC",
                 text_color=tc, text_size=size.small)
            table.cell(dash, 1, 1, str.tostring(pocPrice, format.mintick),
                 text_color=tc, text_size=size.small)
            table.cell(dash, 0, 2, "Avg WT",
                 text_color=tc, text_size=size.small)
            table.cell(dash, 1, 2, str.tostring(avgAllWT, "#.#"),
                 text_color=biasC, text_size=size.small)
            table.cell(dash, 0, 3, "Avg MFI",
                 text_color=tc, text_size=size.small)
            table.cell(dash, 1, 3, volumeIsReal ? str.tostring(avgAllMfi, "#.#") : "n/a",
                 text_color=tc, text_size=size.small)
            table.cell(dash, 0, 4, "mPOC WT",
                 text_color=tc, text_size=size.small)
            pocWT = wtAvg.get(pocIdx)
            pocWTC = pocWT > 0 ? color.new(#00c853, 0) : color.new(#d50000, 0)
            table.cell(dash, 1, 4, str.tostring(pocWT, "#.#"),
                 text_color=pocWTC, text_size=size.small)

Overview

Momentum Profile [WavesUnchained] splits price into rows and plots the average WaveTrend of each zone as a bar colored by direction, plus an optional MFI reference band that stays gray when real trade volume is missing. It marks the momentum point of control as the zone with the largest absolute WaveTrend average and can show a dashboard of overall bias, average WaveTrend, average MFI, and that point of control. Lookback, row count, oscillator lengths, profile width, and bar offset control the display.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Oscillators & momentum, Volume, Support & resistance, Screeners & dashboards

Markets

Any symbol

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
Lookbackint200rpLN
Rowsint25rpNR
Channel Lengthint10wtChLen
Average Lengthint21wtAvLen
Show MFI Reference BandbooltrueshowMfi
MFI Lengthint14mfiLen
Momentum POCbooltrueshowPOC
DashboardbooltrueshowDash
Show Lookback RangeboolfalseshowRange
Profile Width (bars)int60profileWInp
Bar Offsetint40vpHO
Bullcolorcolor.new(#26a69a, 20)bullC
Show all 15 settings
SettingTypeDefaultVariable
Bearcolorcolor.new(#ef5350, 20)bearC
Ref/POCcolorcolor.new(color.gray, 80)refN
pocCcolor#ffd600pocC
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

4 stars

This indicator overlays an order flow intelligence suite directly on the price chart. It renders a liquidity heatmap across configured price zones, alongside an on-chart information panel displaying volume delta, cumulative volume delta, a momentum compass bas

Indicator v6 VolumeScreeners & dashboards
Updated 2 weeks ago · MIT GitHub repository of GHOST — OrderFlow Intelligence
4 stars

An overlay indicator that calculates multi-timeframe market conditions and displays them in an information panel on the price chart. It evaluates timeframe metrics such as 15-minute and 1-hour periods to monitor RSI values, support and resistance levels, and r

Indicator v5 Screeners & dashboardsOscillators & momentum Repaints
Updated 4 years ago · None stated GitHub repository of SnagProfit Logic 15M 1H
3 stars

This indicator calculates multi-source analysis signals based on The Trading Parrot methodology, combining SOPR proxy calculations, custom volume-weighted RSI, liquidity support and resistance levels, volume spike detection, and pattern recognition. It outputs

Indicator v6 Oscillators & momentumVolume
Updated 1 year ago · Custom licence GitHub repository of Trading Parrot Multi-Source Analysis

This overlay indicator screens a pasted list of up to forty symbols and draws a table on the price chart. It classifies bullish or bearish setups from the percent change between the current candle and the candle 250 bars back, then shows RSI with a simple movi

Indicator v5 Oscillators & momentumVolume
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup - Ankush Bajaj Momentum Investing Setup

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