cTrader

AlgoWay Blog

Guides, manuals and platform references.

Core
JSON & Alerts
MT5 / Expert Advisors
Errors
Integrations
Other Publications
AlgoWay Strategy Lab

pandas EMA adjust: Why Early Trading Signals Differ

Published 2026-11-06 · Author: AlgoWay

View Markdown

Two exponential averages diverging during their starting history

An EMA length is not a complete calculation specification. Two Python scripts can both request a span of three, receive the same closing prices, and disagree near the beginning of the series because one uses adjust=True and the other uses adjust=False.

That matters before a Python trading bot sends an order. In an AlgoWay Python webhook workflow, compare the upstream indicator values and signal decisions before investigating execution. If two calculations disagree about whether an entry condition exists, a connector cannot make their strategies equivalent.

Same decay, different starting weights

For a span of three, pandas uses a smoothing factor alpha = 2 / (3 + 1) = 0.5. The default adjust=True calculates a weighted average over the observations available so far and divides by their total weight. With adjust=False, the first observation initializes the average, followed by recursive updates. These are documented alternatives in the pandas ewm reference.

For a complete series with no missing values, the recursive update is:

new average = 0.5 × previous average + 0.5 × new close

The adjusted calculation instead gives the latest close weight 1, the previous close weight 0.5, the one before that weight 0.25, and divides their weighted sum by the sum of those weights. The distinction is especially visible while there are only a few observations.

A three-price example that changes a decision

Consider the invented closing-price sequence 100, 104, 102.4. This is an arithmetic illustration, not a trading result.

Observation Close adjust=True adjust=False
First 100 100 100
Second 104 102.666667 102
Third 102.4 102.514286 102.2

At the second observation, the adjusted average is (104 + 0.5 × 100) / 1.5 = 102.666667. The recursive average is 0.5 × 100 + 0.5 × 104 = 102.

At the third observation, the adjusted numerator is 102.4 + 0.5 × 104 + 0.25 × 100 = 179.4. Divide by 1.75 to obtain approximately 102.514286. The recursive calculation gives 0.5 × 102 + 0.5 × 102.4 = 102.2.

Now apply a precise rule: the current close must be below its EMA, while the previous close was at or above its EMA. At the second observation, 104 is above both averages. At the third, 102.4 is below the adjusted average but above the recursive average. Only the adjusted version produces that downward-crossing event. Neither calculation has malfunctioned; the rule was fed different numbers.

You can reproduce the example without connecting to a trading account:

import pandas as pd

close = pd.Series([100.0, 104.0, 102.4])
adjusted = close.ewm(span=3, adjust=True).mean()
recursive = close.ewm(span=3, adjust=False).mean()

result = pd.DataFrame({
    "close": close,
    "adjusted": adjusted,
    "recursive": recursive,
})
print(result.round(6))

Warm-up is a separate decision

Setting min_periods delays the first reported value until enough observations exist. It does not select a different initialization method. With min_periods=3, the first two rows of this example are hidden, but the third values still differ. A crossing detector also needs an eligible previous value, so it cannot treat a hidden previous EMA as an established comparison.

Do not assume that a three-period span means older observations disappear after three rows. The pandas windowing guide explains the exponentially decaying weights and the special role of the initial observation in the recursive calculation. Missing values introduce another choice through ignore_na; keep them out of this simple example and specify their treatment separately in real data.

For a running bot, compare a fixed recent signal window using several longer starting histories. Record both the numerical difference and whether the actual entry or exit Boolean changes. The indicator startup-history guide explains that broader diagnostic. It does not choose the pandas weighting convention for you.

Make a reproducible indicator specification

Record the input price field, chronological ordering, span or alpha, adjust, missing-value policy, minimum observations and history start. When matching another platform, obtain its initialization rule instead of assuming that every indicator named EMA shares one.

Keep those choices identical in research and production. If the live process restarts with a shorter history, compare its decisions with the established calculation before allowing new orders. A useful audit row contains the timestamp, close, current and previous EMA, and the resulting signal Boolean. That is considerably more revealing than a log entry saying only “EMA 3.”

Last updated: 2026-11-06