TradingView indicator guides

Moving Average Ribbon: 32 Lines and Pine Script | Pineify

This Moving Average Ribbon plots 32 averages with default SMA lengths 5 through 36. Each of the first 31 lines compares its value with the next line; the last compares with close. The result visualizes several overlapping lookbacks. It does not independently measure trader conviction, predict a breakout, or create 32 independent confirmations.

Inspect the Pine Script

Calculate the periods from the actual arguments

For zero-based line index c, this helper computes count = length × start, then period[c] = count + length × c = length × (start + c). Its parameter named length is the increment between periods, not the period of the first line.

With length 1 and start 5, the 32 periods are 5, 6, 7, …, 36. With length 2 and start 5, they are 10, 12, 14, …, 72; they do not begin at 5. Use positive integer arguments and inspect the complete period list before changing the call.

The fixed code uses source close, type SMA, and ma_number 32. The helper includes eleven average types: SMA, EMA, RMA, HMA, WMA, TMA, VAR, WWMA, ZLEMA, TSF, and VWMA. Definitions for the four custom branches are included so the displayed script is complete.

The custom VAR and WWMA branches have initialization behavior that differs from some similarly named averages. VAR can remain zero on all-flat positive input because of its zero seed and zero adaptive coefficient. Check the MA helper contracts before substituting a branch; passing syntax does not establish equivalence.

Colors: the last line uses a different comparison

For lines 1–31, teal means current MA is less than or equal to the next MA; purple means it is greater when both are valid. Line 32 is teal when its value is less than or equal to close, and purple otherwise. This last comparison is not another adjacent-average comparison.

Consequently, a strictly descending sequence of MA values from short to long produces purple on the first 31 lines. A steady rising-price example also gives teal on the final line. “Every line teal means an uptrend” does not describe this source.

Equality selects teal. Missing comparisons can fall through to purple before the next average is ready, so an early color is not a confirmed directional observation. The code uses 25% transparency, approximately 75% opacity. There are 32 line plots and no fill() calls.

The theme and colour_from arguments are accepted but unused in this helper. Changing them alone does not alter these fixed plot colors. There are no settings inputs, alerts, orders, or close-confirmation gates in the copy.

Worked configurations: width is not acceleration

For a complete linear price history rising by one unit every bar, with current close 200, SMA N = 200 − (N−1)/2. SMA 5 is 198, SMA 6 is 197.5, and SMA 36 is 182.5. Each adjacent gap is 0.5 and the outer gap is 15.5.

The first 31 lines are purple because each shorter average exceeds the next. The last is teal because 182.5 is below 200. All averages rise at the same one-unit rate, so the constant 15.5 spread does not demonstrate acceleration.

For a complete constant history of 100, all averages equal 100, all comparisons select teal, and the spread is zero. Teal alone therefore cannot be interpreted as buying pressure. Scaling every price by ten scales the raw spread by ten without creating ten times the predictive confidence.

Outside an ordered ribbon, the shortest and longest averages need not be the maximum and minimum among all lines. For a concrete counterexample, let current and preceding four closes all equal 100, the next older close equal 160, and the remaining 30 older closes all equal 98. Then SMA 5 = 100, SMA 6 = 110, and SMA 36 = 100. The two endpoint averages match while the full ribbon has nonzero range.

A width rule should therefore specify endpoint gap or maximum-minus-minimum across all active averages. A signed endpoint difference and an absolute range are not interchangeable.

History, line count, and runtime limits

With complete valid closes, default SMA 5 appears after five observations and SMA 36 after 36. The longest window contains the current bar through 35 bars ago. On a continuous five-minute chart it uses 36 bars, nominally 180 minutes of bar intervals; sessions and gaps can change elapsed clock time.

Reducing ma_number makes later outputs missing but does not rewrite the 32 plot calls. The last active line before line 32 still compares with the next, now missing, output and falls through to purple. If a shorter ribbon should compare its final active line with price, implement that rule explicitly rather than assuming it happens automatically.

The 32 plots use series-dependent colors. Under TradingView’s plot-count rules, each uses two plot counts, reaching the 64-count budget. Hiding some outputs with na does not remove their plot calls. Adding alertcondition(), more plots, or other counted visuals can exceed the limit; redesign the plot set before extending this full version.

The current close updates all averages while a bar is open. If an event should require a completed bar, implement that observation rule separately and avoid treating an intrabar color as a final historical signal.

Compare multiple-average systems without inventing evidence

Guppy MMA uses two particular groups of EMA periods; this ribbon uses one arithmetic sequence of lengths. Both transform overlapping price histories. Group labels do not identify actual short-term or long-term market participants.

Ichimoku uses different high-low calculations and display offsets. A forward-drawn span is not a forecast of future price. Different formulas, sources, and windows must be compared directly; no universal lead or ranking follows from the number of lines.

For an adaptation, first define line ordering, width, equality, initialization, and confirmation. Then specify an actual entry, exit, sizing, and cost model if testing a strategy. Compression can persist without a breakout. This page establishes no measured win rate, false-signal percentage, or preferred market.

References: Daryl Guppy’s GMMA explanation, Pine color semantics, and repainting and confirmation.

Pine Script v6: implementation and limits

The example includes all referenced average helpers and retains the default 32 SMA calculations and original comparison colors. It is a plotting indicator. Unused theme arguments are not implemented features; alternative custom MA branches retain their documented seed limitations.

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="Moving Average Ribbon", overlay=true, max_labels_count=500)

p_ta_Var_Func(src, length) =>
    valpha = 2 / (length + 1)
    vud1 = src > src[1] ? src - src[1] : 0
    vdd1 = src < src[1] ? src[1] - src : 0
    vUD = math.sum(vud1, 9)
    vDD = math.sum(vdd1, 9)
    vCMO = nz((vUD - vDD) / (vUD + vDD))
    VAR = 0.0
    VAR := nz(valpha * math.abs(vCMO) * src) + (1 - valpha * math.abs(vCMO)) * nz(VAR[1])
    VAR

p_ta_Wwma_Func(src, length) =>
    wwalpha = 1 / length
    WWMA = 0.0
    WWMA := wwalpha * src + (1 - wwalpha) * nz(WWMA[1])
    WWMA

p_ta_Zlema_Func(src, length) =>
    zxLag = int(math.floor(length / 2))
    zxEMAData = src + src - src[zxLag]
    ZLEMA = ta.ema(zxEMAData, length)
    ZLEMA

p_ta_Tsf_Func(src, length) =>
    lrc = ta.linreg(src, length, 0)
    lrc1 = ta.linreg(src, length, 1)
    lrs = lrc - lrc1
    TSF = ta.linreg(src, length, 0) + lrs
    TSF

// Main MA function — supports 11 moving average types
p_ta_ma(series float source, simple int length, simple string ma_type) =>
    switch ma_type
        "SMA" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        "RMA" => ta.rma(source, length)
        "HMA" => ta.hma(source, length)
        "WMA" => ta.wma(source, length)
        "TMA" => ta.sma(ta.sma(source, math.ceil(length / 2)), math.floor(length / 2) + 1)
        "VAR" => p_ta_Var_Func(source, length)
        "WWMA" => p_ta_Wwma_Func(source, length)
        "ZLEMA" => p_ta_Zlema_Func(source, length)
        "TSF" => p_ta_Tsf_Func(source, length)
        "VWMA" => ta.vwma(source, length)

p_getMa(series float source, string ma_type, int c, int count, int length) =>
    l = count + (length * c)
    p_ta_ma(source, l, ma_type)

p_ta_moving_average_ribbon(series float source, simple string ma_type, simple int theme, simple int length, simple int start, simple int ma_number, simple bool colour_from) =>
    count = length * start
    ma1 = ma_number >= 1 ? p_getMa(source, ma_type, 0, count, length) : na
    ma2 = ma_number >= 2 ? p_getMa(source, ma_type, 1, count, length) : na
    ma3 = ma_number >= 3 ? p_getMa(source, ma_type, 2, count, length) : na
    ma4 = ma_number >= 4 ? p_getMa(source, ma_type, 3, count, length) : na
    ma5 = ma_number >= 5 ? p_getMa(source, ma_type, 4, count, length) : na
    ma6 = ma_number >= 6 ? p_getMa(source, ma_type, 5, count, length) : na
    ma7 = ma_number >= 7 ? p_getMa(source, ma_type, 6, count, length) : na
    ma8 = ma_number >= 8 ? p_getMa(source, ma_type, 7, count, length) : na
    ma9 = ma_number >= 9 ? p_getMa(source, ma_type, 8, count, length) : na
    ma10 = ma_number >= 10 ? p_getMa(source, ma_type, 9, count, length) : na
    ma11 = ma_number >= 11 ? p_getMa(source, ma_type, 10, count, length) : na
    ma12 = ma_number >= 12 ? p_getMa(source, ma_type, 11, count, length) : na
    ma13 = ma_number >= 13 ? p_getMa(source, ma_type, 12, count, length) : na
    ma14 = ma_number >= 14 ? p_getMa(source, ma_type, 13, count, length) : na
    ma15 = ma_number >= 15 ? p_getMa(source, ma_type, 14, count, length) : na
    ma16 = ma_number >= 16 ? p_getMa(source, ma_type, 15, count, length) : na
    ma17 = ma_number >= 17 ? p_getMa(source, ma_type, 16, count, length) : na
    ma18 = ma_number >= 18 ? p_getMa(source, ma_type, 17, count, length) : na
    ma19 = ma_number >= 19 ? p_getMa(source, ma_type, 18, count, length) : na
    ma20 = ma_number >= 20 ? p_getMa(source, ma_type, 19, count, length) : na
    ma21 = ma_number >= 21 ? p_getMa(source, ma_type, 20, count, length) : na
    ma22 = ma_number >= 22 ? p_getMa(source, ma_type, 21, count, length) : na
    ma23 = ma_number >= 23 ? p_getMa(source, ma_type, 22, count, length) : na
    ma24 = ma_number >= 24 ? p_getMa(source, ma_type, 23, count, length) : na
    ma25 = ma_number >= 25 ? p_getMa(source, ma_type, 24, count, length) : na
    ma26 = ma_number >= 26 ? p_getMa(source, ma_type, 25, count, length) : na
    ma27 = ma_number >= 27 ? p_getMa(source, ma_type, 26, count, length) : na
    ma28 = ma_number >= 28 ? p_getMa(source, ma_type, 27, count, length) : na
    ma29 = ma_number >= 29 ? p_getMa(source, ma_type, 28, count, length) : na
    ma30 = ma_number >= 30 ? p_getMa(source, ma_type, 29, count, length) : na
    ma31 = ma_number >= 31 ? p_getMa(source, ma_type, 30, count, length) : na
    ma32 = ma_number >= 32 ? p_getMa(source, ma_type, 31, count, length) : na
    [ma1, ma2, ma3, ma4, ma5, ma6, ma7, ma8, ma9, ma10, ma11, ma12, ma13, ma14, ma15, ma16, ma17, ma18, ma19, ma20, ma21, ma22, ma23, ma24, ma25, ma26, ma27, ma28, ma29, ma30, ma31, ma32]

[p_ind_1_ma1, p_ind_1_ma2, p_ind_1_ma3, p_ind_1_ma4, p_ind_1_ma5, p_ind_1_ma6, p_ind_1_ma7, p_ind_1_ma8, p_ind_1_ma9, p_ind_1_ma10, p_ind_1_ma11, p_ind_1_ma12, p_ind_1_ma13, p_ind_1_ma14, p_ind_1_ma15, p_ind_1_ma16, p_ind_1_ma17, p_ind_1_ma18, p_ind_1_ma19, p_ind_1_ma20, p_ind_1_ma21, p_ind_1_ma22, p_ind_1_ma23, p_ind_1_ma24, p_ind_1_ma25, p_ind_1_ma26, p_ind_1_ma27, p_ind_1_ma28, p_ind_1_ma29, p_ind_1_ma30, p_ind_1_ma31, p_ind_1_ma32] = p_ta_moving_average_ribbon(close, "SMA", 2, 1, 5, 32, false)

plot(p_ind_1_ma1, "MA 1", p_ind_1_ma1 <= p_ind_1_ma2 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma2, "MA 2", p_ind_1_ma2 <= p_ind_1_ma3 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma3, "MA 3", p_ind_1_ma3 <= p_ind_1_ma4 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma4, "MA 4", p_ind_1_ma4 <= p_ind_1_ma5 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma5, "MA 5", p_ind_1_ma5 <= p_ind_1_ma6 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma6, "MA 6", p_ind_1_ma6 <= p_ind_1_ma7 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma7, "MA 7", p_ind_1_ma7 <= p_ind_1_ma8 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma8, "MA 8", p_ind_1_ma8 <= p_ind_1_ma9 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma9, "MA 9", p_ind_1_ma9 <= p_ind_1_ma10 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma10, "MA 10", p_ind_1_ma10 <= p_ind_1_ma11 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma11, "MA 11", p_ind_1_ma11 <= p_ind_1_ma12 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma12, "MA 12", p_ind_1_ma12 <= p_ind_1_ma13 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma13, "MA 13", p_ind_1_ma13 <= p_ind_1_ma14 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma14, "MA 14", p_ind_1_ma14 <= p_ind_1_ma15 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma15, "MA 15", p_ind_1_ma15 <= p_ind_1_ma16 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma16, "MA 16", p_ind_1_ma16 <= p_ind_1_ma17 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma17, "MA 17", p_ind_1_ma17 <= p_ind_1_ma18 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma18, "MA 18", p_ind_1_ma18 <= p_ind_1_ma19 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma19, "MA 19", p_ind_1_ma19 <= p_ind_1_ma20 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma20, "MA 20", p_ind_1_ma20 <= p_ind_1_ma21 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma21, "MA 21", p_ind_1_ma21 <= p_ind_1_ma22 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma22, "MA 22", p_ind_1_ma22 <= p_ind_1_ma23 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma23, "MA 23", p_ind_1_ma23 <= p_ind_1_ma24 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma24, "MA 24", p_ind_1_ma24 <= p_ind_1_ma25 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma25, "MA 25", p_ind_1_ma25 <= p_ind_1_ma26 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma26, "MA 26", p_ind_1_ma26 <= p_ind_1_ma27 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma27, "MA 27", p_ind_1_ma27 <= p_ind_1_ma28 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma28, "MA 28", p_ind_1_ma28 <= p_ind_1_ma29 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma29, "MA 29", p_ind_1_ma29 <= p_ind_1_ma30 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma30, "MA 30", p_ind_1_ma30 <= p_ind_1_ma31 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma31, "MA 31", p_ind_1_ma31 <= p_ind_1_ma32 ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
plot(p_ind_1_ma32, "MA 32", p_ind_1_ma32 <= close ? color.rgb(0, 128, 128, 25) : color.rgb(128, 0, 128, 25), 1)
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 the complete period sequence, desired average type, active line count, final-line comparison, equality policy, and confirmation timing. Ask the Pine Script AI Coding Agent to account for the plot-count budget before adding alerts. Compare numerical values with the linear and nonordered examples, then validate the revised script.

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 fixed call creates 32 SMAs from length 5 through 36. In general the helper uses period = length × (start + zero-based index), so changing the increment also changes the starting period.

Not in this code. Lines 1–31 use teal for MA less than or equal to the next MA. Line 32 instead compares with close. Constant equal values are also teal, and a rising linear example has 31 purple lines plus a teal final line.

No. This source contains 32 individual plot calls and no fill calls. The dense lines can look like a ribbon, but that is not a separately filled area.

There are no settings inputs. The theme and colour_from helper arguments are unused, and the plot calls fix the comparison logic and palette.

Only when those endpoints are the extrema. Intermediate averages can be above or below both. The worked example has SMA 5 and SMA 36 equal to 100 while SMA 6 is 110.

The 32 series-colored plots use the 64 plot-count budget. Additional counted features can exceed it. Reduce or redesign the plots before adding features, then validate the final script.