Indicator TutorialPine Script v6

How to Create Moving Average Indicators on TradingView

Compare SMA and EMA calculations, closed-bar crosses and a configurable EMA ribbon.

This guide separates a moving average value from a crossover event. The example plots SMA 50, EMA 21 and EMA 55 with an optional ribbon. These are illustrative settings, not optimized parameters, and the indicator does not place orders or report trading returns.

What is a moving average?

SMA averages a finite set of equally weighted observations. EMA recursively combines the current source with its previous value; its length controls the smoothing factor rather than a hard cutoff after n bars. Neither predicts the next price.

For the same nominal length, EMA gives the newest observation more weight than SMA, but it is not always closer to price or faster at every crossing. The path and initialization matter. See TradingView’s moving-average guide.

Why Traders Use This Indicator

  • Compare current price with an explicitly defined historical average.
  • Distinguish relative MA ordering from a new crossover event.
  • Inspect how several smoothing lengths respond to the same price path.
ParameterDefaultDescription
Core lengthsSMA 50; EMA 21 / 55Lengths count chart observations. Equal fast/slow lengths produce identical EMA lines and no crosses; reversed lengths reverse their usual labels.
SourceCloseAll lines use the selected source. Synthetic candles or an external indicator source change the underlying measurement.
RibbonStart 8; step 8; count 5The default lines are 8,16,24,32,40. Count may be 2–6, with the sixth at 48 for these settings.
Display controlsRibbon and markers on; background offThese switches affect visibility only. Hiding markers does not disable the underlying alert conditions.

How moving averages are computed

For n available consecutive observations, SMA = (x[0] + … + x[n−1]) / n. With hypothetical 98,100,102 the three-value SMA is 100; replacing 98 with 106 makes it 102.6667.

After initialization, EMA = α × current source + (1−α) × previous EMA, with α = 2/(n+1). For n=3, a previous EMA of 100 and new source of 104 gives 102. This assumed previous value is an arithmetic illustration, not a claim about the built-in seed.

Use ta.sma and ta.ema rather than rewriting the formula merely to switch types. Their warm-up, missing-data and initialization behavior should be checked against the exact source and history; there is no universal rule that every average becomes fully stabilized after n bars.

Signal Interpretation

A fast-over-slow cross means the current fast value is greater and the previous fast value was at or below slow. Merely remaining above is a state, not another cross. Lines, table and background update on open bars; markers and named alerts in this example wait for bar close.

Ribbon ordering is a relationship among averages of the same source. It does not measure independent trader conviction. Equal values produce neither the bullish nor bearish background. A closing confirmation can discard an intrabar cross; it does not eliminate losing events.

Create the desired alert in TradingView and recreate it after changing code or inputs, because running alerts use a saved configuration. See the alert documentation.

Combining with Other Indicators

An RSI, ATR or higher-timeframe condition creates an additional rule to evaluate. It can remove profitable as well as losing observations. This example contains no RSI/ATR filter, session-specific calculation, higher-timeframe request or executable order.

Writing Pine Script Manually

Challenges of Manual Coding

Distinguish a finite SMA window from recursive EMA history.

Calculate crosses every bar before applying closing confirmation.

Keep session filtering of observations separate from hiding a plotted line.

Pine Script v6
//@version=6
indicator("MA Suite: SMA, EMA, Crossovers & Ribbon", overlay=true)

// --- Inputs ---
grpCore = "Core MAs"
maSource = input.source(close, "Source", group=grpCore)
smaLen = input.int(50, "SMA length", minval=1, group=grpCore)
emaFastLen = input.int(21, "Fast EMA length", minval=1, group=grpCore)
emaSlowLen = input.int(55, "Slow EMA length", minval=1, group=grpCore)

grpRibbon = "Ribbon"
showRibbon = input.bool(true, "Show EMA ribbon", group=grpRibbon)
ribbonStart = input.int(8, "Ribbon start length", minval=2, group=grpRibbon)
ribbonStep = input.int(8, "Ribbon step", minval=1, group=grpRibbon)
ribbonCount = input.int(5, "Ribbon lines (max 6)", minval=2, maxval=6, group=grpRibbon)

grpSig = "Signals"
showCross = input.bool(true, "Show crossover shapes", group=grpSig)
showBg = input.bool(false, "Highlight trend background", group=grpSig)

// --- Core calculations ---
smaVal = ta.sma(maSource, smaLen)
emaFast = ta.ema(maSource, emaFastLen)
emaSlow = ta.ema(maSource, emaSlowLen)

rawBullCross = ta.crossover(emaFast, emaSlow)
rawBearCross = ta.crossunder(emaFast, emaSlow)
bullCross = barstate.isconfirmed and rawBullCross
bearCross = barstate.isconfirmed and rawBearCross

// --- Ribbon: explicit plots (Pine requires plot() outside loops) ---
rLen0 = ribbonStart + 0 * ribbonStep
rLen1 = ribbonStart + 1 * ribbonStep
rLen2 = ribbonStart + 2 * ribbonStep
rLen3 = ribbonStart + 3 * ribbonStep
rLen4 = ribbonStart + 4 * ribbonStep
rLen5 = ribbonStart + 5 * ribbonStep
r0 = ta.ema(maSource, rLen0)
r1 = ta.ema(maSource, rLen1)
r2 = ta.ema(maSource, rLen2)
r3 = ta.ema(maSource, rLen3)
r4 = ta.ema(maSource, rLen4)
r5 = ta.ema(maSource, rLen5)

// --- Plots: primary lines ---
plot(smaVal, title="SMA", color=color.new(color.teal, 0), linewidth=2)
plot(emaFast, title="Fast EMA", color=color.new(color.blue, 0), linewidth=2)
plot(emaSlow, title="Slow EMA", color=color.new(color.orange, 0), linewidth=2)

// --- Ribbon plots ---
plot(showRibbon and ribbonCount > 0 ? r0 : na, title="Ribbon 0", color=color.new(color.rgb(90, 200, 230), 45), linewidth=1)
plot(showRibbon and ribbonCount > 1 ? r1 : na, title="Ribbon 1", color=color.new(color.rgb(80, 185, 225), 42), linewidth=1)
plot(showRibbon and ribbonCount > 2 ? r2 : na, title="Ribbon 2", color=color.new(color.rgb(70, 170, 220), 40), linewidth=1)
plot(showRibbon and ribbonCount > 3 ? r3 : na, title="Ribbon 3", color=color.new(color.rgb(60, 155, 215), 38), linewidth=1)
plot(showRibbon and ribbonCount > 4 ? r4 : na, title="Ribbon 4", color=color.new(color.rgb(50, 140, 210), 36), linewidth=1)
plot(showRibbon and ribbonCount > 5 ? r5 : na, title="Ribbon 5", color=color.new(color.rgb(40, 125, 205), 34), linewidth=1)

// --- Crossover markers ---
plotshape(showCross and bullCross, title="Bull cross", style=shape.triangleup, location=location.belowbar, size=size.small, color=color.new(color.green, 0))
plotshape(showCross and bearCross, title="Bear cross", style=shape.triangledown, location=location.abovebar, size=size.small, color=color.new(color.red, 0))

// --- Trend background ---
trendBull = emaFast > emaSlow
trendBear = emaFast < emaSlow
bgcolor(showBg and trendBull ? color.new(color.green, 92) : na)
bgcolor(showBg and trendBear ? color.new(color.red, 92) : na)

// --- Alerts ---
alertcondition(bullCross, title="Bullish MA cross", message="Fast EMA crossed above slow EMA at bar close")
alertcondition(bearCross, title="Bearish MA cross", message="Fast EMA crossed below slow EMA at bar close")

// --- Summary table ---
var table info = table.new(position.top_right, 1, 4, border_width=1)
if barstate.islast
    table.cell(info, 0, 0, "SMA(" + str.tostring(smaLen) + ")", text_color=color.white, bgcolor=color.new(color.gray, 30))
    table.cell(info, 0, 1, str.tostring(smaVal, format.mintick), text_color=color.white)
    table.cell(info, 0, 2, "Fast - Slow", text_color=color.white, bgcolor=color.new(color.gray, 30))
    table.cell(info, 0, 3, str.tostring(emaFast - emaSlow, format.mintick), text_color=color.white)

Maintenance Note: This is a chart-timeframe indicator with no session filter or orders. Lines, table and background remain live; markers and alert conditions are confirmed at close. Display switches do not disable alerts. The ribbon uses six fixed global plot calls, with 2–6 displayed.

Build with Pineify Visual Editor

Pineify lets you configure supported indicators and export Pine Script. Available settings and plan requirements vary by indicator. Compare the generated rules with the example before using its signals.

FeatureManual Pine ScriptPineify Visual Editor
Average settingsCall ta.sma or ta.ema with source and length.The MA module exposes Source, Length and MA Type; its defaults are Close, 20 and EMA.
Custom ribbon and tableUse explicit plots and table updates.Specify custom output to AI Coding Agent and inspect the returned source; the base MA module exposes one MA output.
ValidationMatch symbol, timeframe, source and chart session.Perform the same comparisons on generated Pine.

Configure a supported MA source, length and type.

Inspect generated output before defining trading conditions.

Request custom display and confirmation behavior, then review it.

Step-by-Step Tutorial

1

Configure the MA module

Choose Source, Length and MA Type. Add separate MA instances for different averages.

2

Specify the crossover

Define fast-over-slow and whether the event must wait for bar close.

3

Request custom presentation

For the ribbon, table or other behavior not exposed by the chosen module, request a custom script and review it.

4

Compare in TradingView

Match source, timeframe and session, then compare actual values before creating alerts.

Trading Strategies & Pro Tips

Crossover research design

Predeclare entries, exits, position size, costs and an evaluation period. Compare a raw crossover rule with one additional filter using the same data; this indicator supplies no backtest.

Ribbon sensitivity comparison

Change one length or spacing at a time and inspect which confirmed events change. A visually ordered ribbon does not establish profitability.

Common Mistakes to Avoid

  • Treating EMA length as a finite history cutoff.
  • Assuming that hiding out-of-session plots excludes those observations from the calculation.
  • Treating all MAs as fully stabilized after exactly length bars.
  • Interpreting multiple averages of the same source as independent confirmations.
  • Reading synthetic-candle averages as executable market prices.

Frequently Asked Questions

No. EMA gives more weight to the newest observation for a matching length, while SMA uses a finite equal-weight window. The appropriate rule depends on the intended measurement, not a guaranteed performance advantage.

Check source, adjusted prices, timeframe, session, available history, initialization and missing-data conventions. Matching the nominal length alone is insufficient.

No. A visibility mask only hides output. To calculate from regular-session observations, use an appropriate regular-session data context or explicitly define a session-aware input and calculation. This example has no such filter.

No optimum is established here. The example uses EMA 21 and 55; these are not the traditional daily 50/200 SMA golden-cross pair. Evaluate any chosen rule separately with defined costs and later data.

It can calculate using the selected source, but the default Close then comes from synthetic candles. That is a different measurement from standard-candle closing prices.

No guarantee follows from adding a filter. It changes the set of events and may exclude useful observations as well as losing ones. The example itself contains no filter beyond closing confirmation for crosses.

Build and inspect a moving-average rule

  • Set the source, length and MA type.
  • Specify confirmation and session assumptions explicitly.
  • Compare generated values before creating alerts.

Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.