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.
| Parameter | Default | Description |
|---|---|---|
| Core lengths | SMA 50; EMA 21 / 55 | Lengths count chart observations. Equal fast/slow lengths produce identical EMA lines and no crosses; reversed lengths reverse their usual labels. |
| Source | Close | All lines use the selected source. Synthetic candles or an external indicator source change the underlying measurement. |
| Ribbon | Start 8; step 8; count 5 | The default lines are 8,16,24,32,40. Count may be 2–6, with the sixth at 48 for these settings. |
| Display controls | Ribbon and markers on; background off | These 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.
//@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.
| Feature | Manual Pine Script | Pineify Visual Editor |
|---|---|---|
| Average settings | Call 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 table | Use 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. |
| Validation | Match 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
Configure the MA module
Choose Source, Length and MA Type. Add separate MA instances for different averages.
Specify the crossover
Define fast-over-slow and whether the event must wait for bar close.
Request custom presentation
For the ribbon, table or other behavior not exposed by the chosen module, request a custom script and review it.
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
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.