Edgoro
Indicator · Markers, drawings and colors

Pine Script moving average ribbon colored by slope

A Pine Script v6 moving average ribbon: six EMAs, each colored by its own slope, a fill when the ribbon is stacked in order, and why a loop can't build it.

Short answer

Calculate each average on its own line with ta.ema(), then give plot() a color that depends on the slope, such as ma > ma[3] ? color.teal : color.red, since plot() accepts a color that changes on every bar. A fill between the fastest and the slowest line shows when the whole ribbon is stacked in order.

The script

Six EMAs with lengths you can change, each teal while it rises and red while it falls, and a fill that turns green or red when all six are in order. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("EMA ribbon colored by slope", overlay = true)

src      = input.source(close, "Source")
slopeLen = input.int(3, "Slope lookback (bars)", minval = 1)
upCol    = input.color(color.teal, "Rising / falling colors", inline = "c")
downCol  = input.color(color.red, "", inline = "c")
len1 = input.int(10, "Lengths 1 to 3", minval = 1, inline = "a")
len2 = input.int(20, "", minval = 1, inline = "a")
len3 = input.int(30, "", minval = 1, inline = "a")
len4 = input.int(40, "Lengths 4 to 6", minval = 1, inline = "b")
len5 = input.int(50, "", minval = 1, inline = "b")
len6 = input.int(60, "", minval = 1, inline = "b")

// Rising, falling or flat compared with lookback bars ago
slopeColor(float ma, int lookback, color up, color down) =>
    ma > ma[lookback] ? up : ma < ma[lookback] ? down : color.gray

ma1 = ta.ema(src, len1)
ma2 = ta.ema(src, len2)
ma3 = ta.ema(src, len3)
ma4 = ta.ema(src, len4)
ma5 = ta.ema(src, len5)
ma6 = ta.ema(src, len6)

p1 = plot(ma1, "EMA 1", slopeColor(ma1, slopeLen, upCol, downCol), 2)
plot(ma2, "EMA 2", slopeColor(ma2, slopeLen, upCol, downCol))
plot(ma3, "EMA 3", slopeColor(ma3, slopeLen, upCol, downCol))
plot(ma4, "EMA 4", slopeColor(ma4, slopeLen, upCol, downCol))
plot(ma5, "EMA 5", slopeColor(ma5, slopeLen, upCol, downCol))
p6 = plot(ma6, "EMA 6", slopeColor(ma6, slopeLen, upCol, downCol), 2)

// Stacked: every average above the next slower one (or below, for a downtrend)
bool stackedUp   = ma1 > ma2 and ma2 > ma3 and ma3 > ma4 and ma4 > ma5 and ma5 > ma6
bool stackedDown = ma1 < ma2 and ma2 < ma3 and ma3 < ma4 and ma4 < ma5 and ma5 < ma6
fill(p1, p6, stackedUp ? color.new(upCol, 85) : stackedDown ? color.new(downCol, 85) : color.new(color.gray, 94), "Ribbon")

alertcondition(stackedUp and not stackedUp[1], "Ribbon stacked up", "EMA ribbon stacked bullish on {{ticker}}")
alertcondition(stackedDown and not stackedDown[1], "Ribbon stacked down", "EMA ribbon stacked bearish on {{ticker}}")

How it works

  • Six separate ta.ema() lines. Each average has its own call, run on every bar, with a length that comes from an input. That is what ta.ema() needs (see the pitfalls).
  • slopeColor() compares the average with its value lookback bars ago. A lookback of 1 flips color on every small wiggle; 3 to 5 bars gives steadier colors.
  • plot(ma1, "EMA 1", slopeColor(...), 2). The third argument of plot() is a series color, so each line changes color bar by bar from a single call. The fourth is the line width.
  • Inputs on shared rows. Lengths 1–3 and 4–6 sit on two rows (inline = "a" and "b"), with a title only on the first input of each row so they line up in the settings.
  • The fill uses the IDs of the fastest and slowest plots. stackedUp[1] in the alerts makes them fire on the first stacked bar only, not on every bar of the trend.

Pitfalls

  • Building the ribbon in a loop. for i = 1 to 6 with ta.ema(src, i * 10) doesn't work: the length changes inside the loop, so it isn't the fixed length ta.ema() needs (series int used but simple int expected), and a ta.* call in a loop doesn't run once per bar (should be called on each calculation). One line per average is the way.
  • plot() in an if block. Changing color with if rising then plot(ma, color = color.teal) gives Cannot use plot in local scope. Compute the color and pass it to a single plot().
  • Colors on the live bar. ma > ma[3] uses the live close, so a line can change color several times before the bar closes. Alerts from alertcondition() should use "Once per bar close" in the alert dialog.
  • Plot count. Each plot() counts toward the script's limit of 64 plots. Six lines and a fill are far from it, but a ribbon of dozens of averages with one plot per color would not be.

Variations

Color by slope strength, not just direction. Divide the slope by ATR so the scale doesn't depend on the price, then let color.from_gradient() fade from red to teal. Tune the -0.5 to 0.5 range to your market and timeframe.

//@version=6
indicator("EMA colored by slope strength", overlay = true)
len = input.int(50, "EMA length", minval = 1)
ma  = ta.ema(close, len)
atr = ta.atr(14)
// Slope over 5 bars, in ATRs
float slope = (ma - ma[5]) / atr
color c = color.from_gradient(slope, -0.5, 0.5, color.red, color.teal)
plot(ma, "EMA", c, 3)

Questions

How do I change a plot's color based on a condition in Pine Script?

Pass a color expression as the color argument: plot(ma, color = ma > ma[1] ? color.teal : color.red). plot() accepts a color that changes on every bar, so one call is enough.

Can I make a moving average ribbon with a for loop?

Not with ta.ema(): it needs a fixed length and must run on every bar, and a loop gives it a different length on each pass. Write one ta.ema() line and one plot() per average.

What does a stacked ribbon mean?

All averages are in order of length: fastest on top in an uptrend, slowest on top in a downtrend. It describes the trend so far; it lags, and it doesn't say how long the trend will last.

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“Plot an EMA ribbon of 8, 13, 21, 34, 55 and 89, each line green while rising and red while falling, and alert me when the ribbon becomes fully stacked.”