The Simple Moving Average is the “hello world” of indicators, and Python makes it almost too easy. But easy is the point — as the explainer showed, there’s genuinely nothing to it but summing and dividing a sliding window. Let’s build it, then turn it into the crossover signal from the chart above.
What you’ll need
- Python 3.9+,
pandas(pip install pandas) - A
closeprice series.
The one-liner
pandas has a rolling window built in, so the honest answer is:
import pandas as pd
df["sma20"] = df["close"].rolling(window=20).mean()
df["sma50"] = df["close"].rolling(window=50).mean()
rolling(20).mean() computes exactly (P₁ + … + P₂₀) / 20 at every bar, leaving
NaN for the first 19 bars where the window isn’t full yet. That’s correct
behaviour — don’t backfill it.
From scratch (so you know there’s no magic)
If you want to see the sum-and-divide explicitly:
def sma(values, period):
out = [float("nan")] * len(values)
running = 0.0
for i, v in enumerate(values):
running += v
if i >= period:
running -= values[i - period] # drop the price leaving the window
if i >= period - 1:
out[i] = running / period
return out
This is the same running-sum trick every fast implementation uses: instead of
re-adding N prices each bar, you add the newcomer and subtract the departer. O(1)
per bar. It matches rolling().mean() to the last decimal.
The crossover signal
The chart above marks golden and death crosses. Here’s how to detect them:
fast, slow = df["close"].rolling(20).mean(), df["close"].rolling(50).mean()
above = fast > slow
df["golden_cross"] = above & ~above.shift(1, fill_value=False) # crossed up
df["death_cross"] = ~above & above.shift(1, fill_value=False) # crossed down
golden_cross is True on the exact bar the fast SMA crosses above the slow one;
death_cross on the bar it crosses back below. That boolean is the seed of a
mechanical strategy — which you should backtest in AlgoGen
before believing.
Gotchas
min_periods. By defaultrolling(20)needs a full 20 values. Setmin_periods=1only if you deliberately want a partial average early on — most of the time you don’t.- Simple vs exponential. This weights every price in the window equally. If you want recent prices to matter more, that’s the EMA, not the SMA.
Same indicator elsewhere: MQL5, Pine Script, EasyLanguage, NinjaScript.
This post is educational, not financial advice. Indicators describe the past; they don’t predict the future. Backtest anything before you risk real money on it.
Sources and further reading
- Gartley and the early use of moving averages (CMT Association)
- Windowing operations (pandas documentation)
