Skip to content
Level 1 · Chart ReaderLessonPart 06 · page 2 of 1128 min
28Minutes
6AFL functions
8Sources
StandardRequires
AFL functions taught here6

Moving Averages: SMA and EMA

Two lines, one question: when you put a simple and an exponential moving average of the same length on the same chart, what exactly are you looking at when they differ? This lesson answers that, gives you the AmiBroker syntax that catches most beginners out, and shows the one experiment that makes the difference between the two visible rather than theoretical.

A simple moving average over n bars is the arithmetic mean of the last n values. Nothing more. At every bar it adds the newest value, drops the value that fell out of the window, and divides by n. Each of the n bars carries exactly the same weight, 1/n, and every bar older than the window carries a weight of zero.

An exponential moving average never drops anything. It is recursive: each new value is a blend of the current bar and the previous output. AmiBroker documents this shape explicitly on the AMA page, where the adaptive average is defined as output[i] = factor[i] * input[i] + ( 1 - factor[i] ) * output[i-1], and its own example uses 2 / ( n + 1 ) as the factor — the standard exponential smoothing constant. Because the previous output already contains a fraction of the output before it, every bar the average has ever seen still contributes something. The contribution just decays geometrically.

The weight each past bar carries, for a 5-bar window

Weights for a 5-period window. The simple average is flat and stops dead at 5 bars. The weighted average, documented as running from n down to 1, is a straight ramp. The exponential average with factor 2/(n+1) decays without ever reaching zero.
Barnow1 back2 back3 back4 back5 back10 back
Simple average0.200.200.200.200.200.000.00
Weighted average0.330.270.200.130.070.000.00
Exponential average0.330.220.150.100.070.040.01
Weights for a 5-period window. The simple average is flat and stops dead at 5 bars. The weighted average, documented as running from n down to 1, is a straight ramp. The exponential average with factor 2/(n+1) decays without ever reaching zero.

Three things follow directly from that table, and they are the whole of the practical difference.

The simple average has a hard edge. A single extreme bar affects the line identically for n bars and then vanishes from it completely, which produces a visible jolt on the day the extreme bar leaves the window — a movement in the line caused by something that happened n bars ago, not by today.

The exponential average has no edge. Old data fades rather than falling off, so there is no drop-out jolt, but there is also no point at which you can say “this line no longer knows about that crash”.

And for the same nominal period, the exponential average puts more weight on the newest bars, so it responds sooner. Sooner is not the same as better. It responds sooner to noise as well.

The three standard readings, stated as what people say rather than as findings:

  • Direction. A rising average is read as an uptrend, a falling one as a downtrend.
  • Position. Price above the average is read as strength, below as weakness.
  • Crossings. Price crossing the average, or a fast average crossing a slow one, is read as a change of state. That is the subject of the next lesson.

Notice that the first two are close to being definitions rather than claims. If “uptrend” means “the 200-bar average is rising”, then saying “the trend is up because the 200-bar average is rising” tells you nothing you did not already say. It becomes a claim only when you attach something to it — that returns after such bars differ from returns after other bars, for instance — and then it becomes testable.

Switching to EMA because it is “faster”. It is faster in exactly the sense that a shorter simple average is faster, and it pays the same price: more turns, more of which reverse. If you want a faster line you have two dials — length and weighting — and both cost the same currency.

Comparing average values between instruments. A moving average is in the units of its input. “The average is 148” is meaningless across symbols. The distance from price to the average, divided by price or by ATR, is comparable. The raw level is not.

Judging an average on the left edge of the chart. More on this in a moment; it is important enough to have its own section.

Reading a crossover on a chart of an instrument you chose because the crossover worked. This is the most common one and the hardest to see in yourself. Part 30 gives it its proper name, selection bias.

A moving average cannot distinguish a genuine directional move from a slow drift with noise on top, because both produce a sloping line. It cannot tell you anything about the quality of a move — participation, volatility, whether the move is happening in one gap or over thirty bars — because it only ever sees one number per bar.

And it produces a line during a sideways range that looks exactly like a trend line during a trend, only shorter. The eye reads the shape; the arithmetic makes no such distinction.

One average, two regimes

Drift removed from bar 41 on
  • Close
  • MA(Close, 20)
The same MA(Close, 20) over both halves. On the left it slopes, and price stays on one side of it for long runs. On the right it crosses price repeatedly, because there is no longer a direction for it to lag behind - and the arithmetic did not change at bar 41, only the market did. The data in this chart is invented for the illustration. It is not market data and nothing should be inferred from it.

Notice what the average does not do at bar 41. It does not signal the change. It cannot: it is an average of the last twenty closes, and twenty bars after the regime changed it is still partly made of the old one. The flattening you can see is a description of a change that finished happening some time ago.

AmiBroker documents MA()’s warm-up precisely, and the official tutorial shows it: for MA( Close, 3 ) the first two bars are Null, and the first number appears on the third bar. In general a periods-bar simple average is Null for the first periods - 1 bars, and those Nulls propagate — the same tutorial shows Buy = Cond1 AND Cond2 inheriting them.

The exponential average is trickier, and this is where numbers stop matching between platforms. AmiBroker’s author states on the EMA page that EMA is initialised from a simple moving average of the same length, deliberately, to match another package’s output. So the recursion does not start from the first close; it starts from an SMA value at bar n, and every value afterwards carries a fading memory of that seed.

The DEMA page states the practical consequence in a form you can measure: a hand-built 2 * EMA( C, len ) - EMA( EMA( C, len ), len ) and the built-in DEMA( C, len ) do not converge until roughly 2 * len bars after the EMA-based curve starts — about 6 * len bars from the beginning of the data.

There are two routes, and it is worth doing both once.

Without writing code. Open Window -> Charts, find the moving average entry in the tree, and double-click it. AmiBroker inserts a new pane and opens the Parameters dialog for the indicator you just added. To put the average on top of the price instead, drag its name from the Charts tree onto the price pane rather than double-clicking.

The Parameters dialog is where you set the period, the price field, the colour and the style. You reach it later with a right-click on the pane, or Ctrl+R. AmiBroker stores those values per chart pane, which is why the same formula can look different in two panes.

By applying a formula. Open Analysis -> Formula Editor, paste the code, type a name into the Formula Name field, and press Apply Indicator. The toolbar button is Check syntax; the same action is Tools -> Verify syntax on the menu, which is worth knowing because searching the menus for “Check syntax” finds nothing.

Both functions take the array and the period, and neither has a documented default:

Fragment — not a complete formula

Fast = MA( Close, 20 ); // simple average of the last 20 closes
Slow = EMA( Close, 50 ); // exponential average, period 50
VolAvg = MA( Volume, 50 ); // any array, not just price

Two more documented details worth carrying:

MA accepts a time-variant period — the second argument may itself be an array, so the window can change bar by bar. The EMA page makes no such statement, so do not assume it; for a smoothing factor that varies per bar, AMA is the documented tool.

ma, rsi and cci are function names, and AmiBroker’s language reference states that user identifiers cannot duplicate them. ma = MA( Close, 20 ); will not compile. Name your variables after what they mean instead — Trend, FastAvg, VolumeBaseline.

A formula that makes the difference visible

Section titled “A formula that makes the difference visible”

Reading about weighting schemes is not the same as watching one line pull away from another. This formula puts a simple and an exponential average of the same length on the chart, plus the gap between them as a percentage of price.

Complete runnable AFL

ma-response-comparison.afl
// ma-response-comparison.afl
// Part 6 - Moving Averages: SMA and EMA
//
// Draws price with a simple and an exponential moving average of the SAME
// length, plus the gap between them, so that the only thing you are looking at
// is the effect of the weighting scheme.
//
// Assumptions declared up front:
// - Daily bars on a liquid instrument with several years of history.
// - MA() returns Null for the first MaPeriod-1 bars, and EMA() is seeded from
// a simple average at bar MaPeriod, so the far left of the chart is
// warm-up and tells you nothing. Scroll away from it before judging.
// - Both averages are taken on Close. Changing the input changes the meaning
// of everything below.
_SECTION_BEGIN( "SMA vs EMA" );
// One period drives BOTH averages on purpose. Comparing a 20-bar SMA against a
// 50-bar EMA would only tell you about length, which is not the question here.
MaPeriod = Param( "Averaging period", 20, 2, 200, 1 );
// MA() and EMA() each require the array AND the period. Neither has a
// documented default, so MA( 20 ) is an error rather than a 20-bar average.
SimpleAvg = MA( Close, MaPeriod );
ExpAvg = EMA( Close, MaPeriod );
// The gap between the two averages, expressed as a percentage of price so it
// stays readable when the price level changes over the years. The gap widens
// only when the newest bars in the window differ from the older ones.
GapPercent = 100 * ( ExpAvg - SimpleAvg ) / Close;
Plot( Close, "Close", colorDefault, styleCandle );
Plot( SimpleAvg, "SMA(" + MaPeriod + ")", colorBlue, styleLine | styleThick );
Plot( ExpAvg, "EMA(" + MaPeriod + ")", colorRed, styleLine | styleThick );
// Drawn on its own scale so it cannot squash the price axis. The documented
// minvalue / maxvalue arguments apply to styleOwnScale plots only, which is
// exactly what this is.
Plot( GapPercent, "EMA - SMA (% of close)", colorGrey40,
styleLine | styleOwnScale | styleNoLabel, -10, 10 );
_SECTION_END();

Download ma-response-comparison.afl42 lines

How it works. One Param drives both averages, which is the entire design decision: if the two averages had different lengths, any difference you saw would be a difference of length and would tell you nothing about weighting. The two averages are computed with MA and EMA on Close. The gap is expressed as a percentage of the closing price so that it stays readable when the price level changes by a factor of ten over a decade. The gap is drawn with styleOwnScale, whose documented minvalue and maxvalue arguments — the fifth and sixth arguments of Plot — are the only place those two arguments do anything.

Expected result. A candlestick chart with a blue simple average, a red exponential average, and a thin grey line near the bottom. In a quiet, directionless stretch the two averages sit almost on top of each other and the grey line hugs zero. When a sustained move begins, the red line separates from the blue one in the direction of the move, and the grey line pushes away from zero. When the move ends, the red line crosses back through the blue one before the blue one has finished turning.

Test it. Set the period to 2. Both averages should now sit almost on the price bars, and the gap should be tiny — with a two-bar window there is barely any weighting to differ about. Then set it to 200 and scroll to the start of the symbol’s history: the blue line should be absent for the first 199 bars while the red line, seeded from a simple average, appears around bar 200 and then drifts as the seed washes out. Both behaviours are what the documentation says should happen, which is a reasonable check that you are reading the chart correctly.

Common errors. If you get “Error 5: too few arguments”, you have written MA( 20 ) somewhere. If both lines look identical at every period, check that you passed the same MaPeriod variable to both and did not hard-code a number in one of them. If the grey line flattens the price bars into a stripe, the styleOwnScale flag has been lost from the last Plot call.

Extension. Add a third average using WMA( Close, MaPeriod ), which AmiBroker documents as giving weight n to the newest bar, n-1 to the one before, down to 1. It should sit between the other two most of the time. Predict where it will sit before you plot it, then check whether you were right.

“Price above the 200-day average means the trend is up” is a definition. Here is a version that could be false:

Over the last fifteen years, on the symbols in my watch list, the distribution of 20-day forward returns measured on bars where the close was above its 200-day simple average differs materially from the distribution measured on all other bars.

That statement names a universe, a period, a condition, a measurement and a horizon. It can come out either way. It still has serious problems — the watch list is probably made of survivors, fifteen years is one sample of market history, and “materially” needs a number — and those problems are exactly what the final lesson of this part is about. Do not build the test yet. Notice instead how much had to be specified before the sentence became checkable at all.

A simple moving average weights the last n bars equally and everything else at zero; an exponential average weights the newest bar most and never quite forgets anything. That single difference produces everything else: the drop-out jolt in one, the seed sensitivity in the other, the earlier response of the exponential line and the extra turns that come with it. In AmiBroker both functions require the array and the period, MA is Null for the first periods - 1 bars, and EMA is seeded from a simple average, so the early part of any recursive line is contaminated for far longer than its nominal period.

Check your understanding

Question 1. Which of these compiles and does what the author intended?
Show the answer and why

Answer: Trend = MA( Close, 20 );

MA and EMA take the array first and the period second, and both are required. "ma" cannot be used as a variable name because it duplicates a function name, which AmiBroker’s language reference forbids.

Question 2. A 50-bar simple moving average jumps noticeably one day, but the day’s price barely moved. What is the most likely explanation?
Show the answer and why

Answer: An extreme bar from 50 bars ago dropped out of the window

A simple average has a hard edge: the bar leaving the window changes the mean as much as the bar entering it. This drop-out effect is the price you pay for equal weighting, and it is invisible on the chart because the cause is 50 bars to the left.

Question 3. Why is the very start of an EMA line on a freshly imported symbol untrustworthy?
Show the answer and why

Answer: EMA is seeded from a simple average and the recursion carries that seed forward for many bars

AmiBroker documents that EMA is initialised from a simple moving average of the same length. Because the recursion blends each output into the next, that seed keeps influencing values well past the first non-Null bar — the DEMA page puts convergence at roughly six times the period from the start of data.

Question 4. Which of these statements about moving averages are supported by AmiBroker’s documentation? Select all that apply.
Show the answer and why

Answer: MA accepts a time-variant period passed as an array, MA( Close, 3 ) returns Null on the first two bars, WMA gives the most recent bar the largest weight

The MA and WMA pages both state that the period may be time-variant and WMA documents its n, n-1, ... 1 weighting; the official tutorial shows the MA warm-up Nulls. The EMA page makes no time-variant claim, so do not assume it — use AMA when the smoothing factor must vary.

Sources for this lesson

8 verified · checked 2026-08-31

  1. 01AFL Function Reference — MAamibroker.com/guide/afl/ma.html2026-08-31
  2. 02AFL Function Reference — EMA§ Author comment on initialisationamibroker.com/guide/afl/ema.html2026-08-31
  3. 03AFL Function Reference — WMAamibroker.com/guide/afl/wma.html2026-08-31
  4. 04AFL Function Reference — DEMA§ Convergence with an EMA-composed equivalentamibroker.com/guide/afl/dema.html2026-08-31
  5. 05AFL Function Reference — AMAamibroker.com/guide/afl/ama.html2026-08-31
  6. 06AFL Function Reference — Plotamibroker.com/guide/afl/plot.html2026-08-31
  7. 07AmiBroker User's Guide — Understanding how AFL language worksamibroker.com/guide/h_understandafl.html2026-08-31
  8. 08AmiBroker User's Guide — Creating indicators by drag-and-dropamibroker.com/guide/h_dragdrop.html2026-08-31

Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.