TradingView indicator guides

Momentum (MOM): Price Difference and Pine Script | Pineify

Momentum (MOM) is the difference between a source value now and the value N bars ago. The example plots close minus close[10]. Its sign describes that endpoint comparison; its slope describes how the comparison changes as both endpoints move. Neither quantity directly measures buying pressure or guarantees a future price move.

Inspect the Pine Script

Formula and units

MOM(t, N) = source(t) − source(t − N). In Pine Script, ta.mom(source, N) performs this difference. With close as the source, the result has price units, not percentage units and not a bounded 0–100 scale.

Length 10 needs a current observation and the observation ten bars back: the first possible result occurs on the eleventh chart bar when both values exist. Missing endpoints produce a missing result. Interior observations do not directly enter this two-endpoint subtraction, although their existence determines bar indexing.

Ten chart bars are not always ten calendar periods. Sessions, missing bars, and nonstandard chart types affect what the source and bar spacing mean. Keep those settings consistent when comparing results.

A bullish zero crossing can occur while price falls

Consider this hypothetical sequence, using length 3 for a short worked example:

Two moving endpoints
BarCloseClose three bars agoMOM
0100UnavailableUnavailable
190UnavailableUnavailable
2100UnavailableUnavailable
399100-1
498908

At bar 4, close falls from 99 to 98, but MOM rises from −1 to +8. The reference endpoint falls by 10, outweighing the current one-point decline. The positive zero crossing does not mean the current bar rose or that buyers were identified.

Algebraically, change in MOM = current one-bar source change − the one-bar source change N bars ago. For N greater than 1, this is not the difference between the latest two consecutive one-bar changes. Calling every rising MOM “current acceleration” hides the moving-reference effect.

For a source that rises exactly two units on every bar, MOM(3) stays at 6 once available. A flat MOM can coexist with a persistent price increase. Negative MOM can also rise toward zero without becoming positive.

MOM, percentage ROC, and ratio momentum

Percentage ROC = 100 × (current − past) / past, when the past value is nonzero. Equivalently it is 100 × (current/past − 1). Omitting the minus one creates a ratio centered on 100, not percentage ROC centered on zero.

Synthetic endpoint comparisons
Past → currentMOMROC100 × current/past
500 → 515153%103
50 → 651530%130
100 → 90-10−10%90

Dividing MOM by the current price is a different normalization. For 500 → 515, it gives about 2.9126%, not the 3% return measured from the past price. Specify the denominator rather than treating normalized variants as interchangeable.

MOM is still defined by subtraction when the past source is zero; percentage ROC is not. For positive past prices and nonnegative current prices, ROC cannot fall below −100%, though it has no fixed positive ceiling. Raw MOM has no universal fixed bounds across instruments.

Official formula: TradingView Rate of Change. RSI instead normalizes smoothed gains and losses; it answers a different question and is not made invalid merely by staying near an extreme.

What this script does and does not show

The code has a fixed call to ta.mom(close, 10) and a single blue plot in a separate pane. It contains no settings inputs, horizontal zero-line drawing, crossover markers, divergence detector, alerts, or orders. Zero remains the numerical reference, but a dedicated visible zero line requires an explicit plotting addition.

The current close can update while its bar is open, so MOM can move across zero and back. This example has no bar-close gate. If an adaptation uses a confirmed event, define the event on the closing update and check the first price at which an order could actually execute.

A zero crossover typically means current MOM is strictly positive and previous MOM was nonpositive; a move from exactly zero can qualify. This is different from merely staying positive. A pivot-based divergence rule additionally needs its right-side confirmation bars before the pivot is known.

Reference: TradingView repainting and historical placement. A pivot drawn back on its original bar must not be counted as a signal already known at that time.

Lookback choices are hypotheses, not performance rankings

Length changes which old endpoint is compared with the current one. It is not a smoothing average, so increasing length does not guarantee fewer zero crossings. On a repeating 100, 101, 100, 101 sequence, MOM(2) is zero after initialization, while MOM(3) alternates between −1 and +1.

Use a defined event, symbol, timeframe, session, cost model, and evaluation period to compare choices. A longer confirmation rule changes observation time; it does not come with a fixed percentage reduction in false signals. Combining MOM with EMA, volume, or RSI adds assumptions that require separate evaluation.

If adding risk rules, keep reference prices explicit. “Entry minus 1.5 ATR” and “entry-bar low minus 1.5 ATR” are different stops unless entry equals the low. A chart indicator alone supplies neither a position-size rule nor an execution model.

Pine Script v6: implementation and limits

The full example plots ta.mom(close, 10) only. Unused generated time helpers do not add session filtering or other-timeframe behavior. The numerical zero reference is not separately drawn; any requested inputs, reference lines, or events must be added explicitly.

Pine Script v6
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify

//======================================================================//
//                    ____  _            _  __                          //
//                   |  _ (_)_ __   ___(_)/ _|_   _                    //
//                   | |_) | | '_ \ / _ \ | |_| | | |                   //
//                   |  __/| | | | |  __/ |  _| |_| |                   //
//                   |_|   |_|_| |_|\___|_|_|  \__, |                   //
//                                             |___/                    //
//======================================================================//

//@version=6
indicator(title="Momentum", overlay=false, max_labels_count=500)

//#region —————————————————————————————————————————————————— Custom Code

//#endregion ————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Common Dependence

p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_unix_time = na
    int end_unix_time = na
    int start_time_hour = na
    int start_time_minute = na
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        // Format: hh:mm-hh:mm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        // Format: hhmm-hhmm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
    end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
    [start_unix_time, end_unix_time]

p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_time_hour = na
    int start_time_minute = na
    if str.length(time_range) == 11
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
    else if str.length(time_range) == 9
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)

p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)

p_comm_timeframe_to_seconds(simple string tf) =>
    float seconds = 0
    tf_lower = str.lower(tf)
    value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
    if str.endswith(tf_lower, 's')
        seconds := value
    else if str.endswith(tf_lower, 'd')
        seconds := value * 86400
    else if str.endswith(tf_lower, 'w')
        seconds := value * 604800
    else if str.endswith(tf_lower, 'm')
        seconds := value * 2592000
    else
        seconds := str.tonumber(tf_lower) * 60
    seconds

p_custom_sources() =>
    [open, high, low, close, volume]

//#endregion —————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Ta Dependence


//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Constants

// Input Groups
string P_GP_1      =      ""

//#endregion —————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Inputs

//#endregion ———————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Price Data



//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicators

p_ind_1      =      ta.mom(close, 10) // MOM


//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Conditions

//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicator Plots

// MOM
plot(p_ind_1, "MOM", color.rgb(41, 98, 255, 0), 1)

//#endregion ————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Custom Plots

//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Alert

//#endregion ——————————————————————————————————————————————————————
Existing indicator chart preview
Existing visual preview. Verify exact values against the source; the image is not a performance result.

Specify a checkable adaptation in Pineify

Specify raw MOM or percentage ROC, the source, lookback, visible reference line, and the exact event to detect. Ask the Pine Script AI Coding Agent for those requirements and an explicit confirmation policy. Check the synthetic endpoint examples and validate revised code before using it in a strategy.

Open Pine Script AI Coding Agent

Educational indicator analysis. No measured trading performance is established here. Syntax validation does not prove formula equivalence, execution assumptions or profitability.

Implementation questions

The current source is higher than the source N bars ago. It does not necessarily mean the latest bar rose, the MOM line is rising, or a future return is positive.

Yes. Both comparison endpoints change each bar. The worked sequence has close falling from 99 to 98 while its three-bar reference falls from 100 to 90, so MOM rises from −1 to +8.

MOM is the raw difference. Percentage ROC divides that difference by the past value and multiplies by 100. A current/past ratio times 100 is centered on 100 and is a different formula.

No. MOM compares two points rather than averaging a window. A repeating two-bar sequence can have constant zero MOM at length 2 and alternating signs at length 3.

The displayed script fixes close and length 10 and has no inputs. Edit the call or ask for explicit settings inputs in an adaptation, then check the generated formula and behavior.

No. It plots one live blue line. Signals, pivot confirmation, alerts, order execution, and performance calculations require additional explicit logic and validation.