Foundations · Intermediate
Indicators and the 200-day moving average
On a century of daily US data, the 200-day moving average reliably lowers risk, while its return advantage depends on the decade and on the execution.
Educational material only. Not investment advice.
Contents
Abstract
Indicators fall into five families, and six checks separate a sound test of one from a misleading one. We apply them to a single rule on US daily data since 1927: hold the market while it is above its 200-day moving average, otherwise hold T-bills. The rule’s lower risk survives every check. Its higher return belongs to the years before 1990, and disappears with one day of execution delay and a cost of 10 bp per switch.
Key takeaways
- An indicator is a function of past prices; the rule, the execution lag and the costs decide whether it is worth anything.
- On US daily data since 1927, the 200-day rule raised the Sharpe ratio and cut the deepest drawdowns to between a quarter and three-fifths of the market’s, at the price of frequent whipsaw.
- The return advantage belongs to the years before 1990; since then the rule has returned less than the market, while keeping its risk advantage.
- Every lookback from 50 to 300 days behaves similarly on Sharpe ratio; 200 is not special.
- One day of execution delay and 10 bp a switch erase the return advantage; the Sharpe and drawdown advantages survive.
Before you start
- Market anomalies, for multiple testing and the deflated Sharpe ratio
- Daily returns, compounding and drawdowns
- Reading Python with pandas
A technical indicator turns a price history into a number, and a rule turns the number into a position. The idea is old and the evidence is mixed. Most of what goes wrong has little to do with the indicator itself: the close it reads, the moment the order can execute and the cost of each switch decide the result. The most widely quoted indicator, the 200-day moving average of the US stock market, shows all of this on 99 years of daily data: its lower risk is robust, and its higher return is not.
Indicators and rules
A backtest replays history through a rule, day by day, charging the trades it would have made, and reports what the rule would have earned; most of this note is about making that replay honest.
Write Pt for the closing price (or total-return index) on day t. An indicator is a function It = f(Pt, Pt−1, …) of prices up to and including the close of t. A rule maps the indicator to an exposure wt+1 held over the next day. Everything that goes wrong in practice happens in the space between those two sentences: which close the indicator uses, when the order can execute, and what the trade costs. Figure 1 shows the chain; the notation is the one used throughout the series.
The main families
Most indicators in use belong to five families. Each has a standard definition; the differences between implementations (a simple or exponential average, a close or a typical price, the smoothing of Wilder’s averages) matter less than the lookback and the execution around it.
| Family | Canonical indicator | Definition | Typical use |
|---|---|---|---|
| Trend | Simple / exponential moving average | SMAₙ = mean of the last n closes; EMA with α = 2/(n+1) | Price above average: hold; crossovers of a fast and a slow average |
| Momentum | Rate of change (ROC) | Pₜ / Pₜ₋ₙ − 1 | Sign of the past year’s return (time-series momentum) |
| Volatility | Average true range (ATR); realised volatility | Wilder’s average of max(high − low, |high − prev close|, |low − prev close|); standard deviation of returns | Position sizing and stops rather than direction |
| Mean reversion | Relative strength index (RSI) | 100 − 100 / (1 + avg gain / avg loss), Wilder smoothing | Fade short-term extremes (below 30, above 70) |
| Breadth | Share of constituents above their own 200-day average; advance–decline line | Counts across the index’s members | Confirms or contradicts the index’s own trend |
The ATR and RSI are due to Wilder (1978); breadth needs constituent-level data, which is why it is described here but not computed.
The trend and momentum families are two views of the same thing: a price above its n-day average has, roughly, risen over the last n/2 days or so, since the average is centred that far back. Time-series momentum, which Market anomalies reviews, is the academic name for the second view.1 Volatility indicators are the input to Volatility targeting. Wilder introduced the RSI and the ATR,2 and the RSI’s mean-reversion logic is a form of the short-term reversal described in Market anomalies. Figure 2 draws four of the families on the same days.
def sma(price: pd.Series, n: int) -> pd.Series:
"""Simple moving average of the last n closes."""
return price.rolling(n, min_periods=n).mean()
def ema(price: pd.Series, n: int) -> pd.Series:
"""Exponential moving average with span n (alpha = 2 / (n + 1))."""
return price.ewm(span=n, adjust=False, min_periods=n).mean()
def roc(price: pd.Series, n: int) -> pd.Series:
"""Rate of change over n periods (time-series momentum)."""
return price / price.shift(n) - 1.0
def rsi(price: pd.Series, n: int = 14) -> pd.Series:
"""Wilder's relative strength index: 100 - 100 / (1 + avg gain / avg loss)."""
d = price.diff()
gain = d.clip(lower=0.0).ewm(alpha=1.0 / n, adjust=False, min_periods=n).mean()
loss = (-d.clip(upper=0.0)).ewm(alpha=1.0 / n, adjust=False, min_periods=n).mean()
return 100.0 - 100.0 / (1.0 + gain / loss)
def realised_vol(ret: pd.Series, n: int = 21, periods=None) -> pd.Series:
"""Annualised standard deviation of the last n returns. `periods` defaults to the sessions
per year at each date (`sessions_per_year`), so pre-1952 data is annualised correctly."""
periods = sessions_per_year(ret.index) if periods is None else periods
return ret.rolling(n, min_periods=n).std(ddof=1) * np.sqrt(periods)How to evaluate an indicator
Indicators are easy to compute and easy to overrate. Six checks separate a result from an artefact; the analysis that follows applies each of them.
- Look-ahead. A signal computed from the close of day t cannot earn day t’s return. The error is common because in a vectorised backtest it is one missing
shift, and it is flattering: on the rule below it adds 8.7 percentage points a year. - Execution lag. Even correctly lagged, a rule that trades “at the close” assumes the signal is known in time to send a market-on-close order. Test one day later as well; the difference measures how much the result depends on precise timing.
- Costs. Charge every switch. A rule that trades a few times a year looks cheap until the switches cluster in volatile, wide-spread markets.
- Whipsaw. Count round trips that reverse within weeks, and measure what they cost. They are the price of a trend rule, and they are paid in exactly the range-bound markets where the rule adds nothing.
- Regime dependence. Report results by decade and by episode, not only in aggregate. A century-long average can be one decade’s result in disguise.
- Multiple testing. The 200-day lookback is famous because it has been examined for decades, which is itself a form of selection. Show the whole neighbourhood of lookbacks, and read Market anomalies on deflating the best of many trials.
The 200-day rule on a century of data
The rule is the simplest trend rule there is: at each close, hold the US stock market if its total-return index is above its 200-day simple moving average, otherwise hold one-month Treasury bills; trade at that close. The data are the daily CRSP value-weighted US market total return and the one-month T-bill return from the Kenneth R. French Data Library,3 5 May 1927 to 31 August 2026 after 252 sessions of warm-up (about ten months, since the market then also traded on Saturdays). Sharpe ratios are annualised with the number of sessions actually traded each year. That is the broad US market rather than the S&P 500 or the SPY exchange-traded fund, which has existed only since 1993: a long daily S&P 500 history is not freely licensed. As a check, the 200-day regime computed on the S&P 500 price index (FRED, 12 July 2017 to 31 August 2026)4 agreed with the regime on the CRSP series on 98.5% of 2,297 days.
The 200-day rule compounded at 11.5% a year against 10.2% for buy and hold, with a worst drawdown of −33.6% against −84.1%, invested 72% of the time and switching 5.7 times a year.
| 5 May 1927 to 31 August 2026 | Buy and hold | 200-day rule |
|---|---|---|
| Annual return | 10.2% | 11.5% |
| Annual volatility | 17.5% | 11.5% |
| Sharpe ratio (excess over T-bills) | 0.46 | 0.73 |
| Worst drawdown | −84.1% | −33.6% |
| Worst day | −17.4% | −9.2% |
| Time in the market | 100% | 72% |
| Switches a year | — | 5.7 |
US market total return (CRSP value-weighted, via the Kenneth R. French Data Library); the rule holds one-month T-bills when out. The moving average is computed on the total-return index. Cost is charged per unit of exposure switched.
The headline is real but needs reading carefully. The rule earned a higher return than the market with two-thirds of its volatility, so its Sharpe ratio was materially higher (0.73 against 0.46), and its worst drawdown was −34% against −84%. It achieved that by being out of the market 28% of the time and switching 5.7 times a year. Two cautions apply before any of these numbers is quoted. The return advantage comes from the years before 1990 (next section); and the 200-day drawdown is the best, not the typical, result among the lookbacks tested (Figure 4). Trading at the close that produced the signal is also the optimistic case; the next-close row below is the conservative reading.
import sys
from pathlib import Path
import pandas as pd
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from fieldnotes import backtest, data, indicators # noqa: E402
d = data.market_daily() # daily total return, T-bill return
price = (1 + d["mkt"]).cumprod() # total-return index
signal = indicators.sma_regime(price, 200) # 1 above the 200-day average, else 0
rule = backtest.run(d["mkt"], signal, d["rf"], lag=1) # lag=1: decided at the close of t, held over t + 1
hold = backtest.run(d["mkt"], pd.Series(1.0, index=d.index), d["rf"])
start = d.index[252] # 252 sessions of warm-up for every lookback
for name, res in (("buy and hold", hold), ("200-day rule", rule)):
r = res[res.index >= start]
print(name, {k: round(float(v), 4) for k, v in
backtest.stats(r["ret"], d["rf"][r.index], weight=r["weight"]).items()})def run(ret: pd.Series, weight: pd.Series, cash: pd.Series | None = None,
cost: float = 0.0, lag: int = 1) -> pd.DataFrame:
"""Daily P&L of holding `weight` in `ret`, the rest in `cash`.
lag=1: the weight decided at close t earns the return of t+1 (trade at the signal's close).
lag=2: trade one period later. lag=0: LOOK-AHEAD (the signal earns its own day's return).
cost: a fraction of capital charged on every unit of exposure traded (0.001 = 10 bp).
"""
w = weight.shift(lag).fillna(0.0)
cash = pd.Series(0.0, index=ret.index) if cash is None else cash.reindex(ret.index).fillna(0.0)
traded = w.diff().abs().fillna(w.abs())
pnl = w * ret + (1.0 - w) * cash - cost * traded
return pd.DataFrame({"ret": pnl, "weight": w, "traded": traded})def sma_regime(price: pd.Series, n: int = 200, band: float = 0.0) -> pd.Series:
"""1 when the close is above its n-day SMA, else 0, decided at each close.
band > 0 adds hysteresis: switch IN only above sma * (1 + band) and OUT only below
sma * (1 - band); in between, keep yesterday's state. band = 0 is the plain rule.
"""
m = sma(price, n)
up, down = price > m * (1 + band), price < m * (1 - band)
state = pd.Series(np.nan, index=price.index)
state[up] = 1.0
state[down] = 0.0
state[m.isna()] = 0.0
return state.ffill().fillna(0.0)Where the result comes from
By period
| Period | Market, annual return | Rule, annual return | Market, Sharpe | Rule, Sharpe | Market, worst drawdown | Rule, worst drawdown |
|---|---|---|---|---|---|---|
| 1927 to 1952 | 7.1% | 11.8% | 0.39 | 0.80 | −84% | −34% |
| 1953 to 1989 | 11.5% | 13.7% | 0.51 | 0.90 | −48% | −16% |
| 1990 to 2026 | 11.0% | 9.2% | 0.52 | 0.58 | −55% | −29% |
Each period compounded from one on its first day.
The century-long return advantage belongs to the years before 1990, and mostly to the first quarter-century: from 1927 to 1952 the rule compounded at 11.8% against 7.1%. From 1953 to 1989 it was ahead by 2.2 percentage points, and since 1990 it has returned less than the market (9.2% against 11.0%). What held in every period is the risk profile: a higher Sharpe ratio and a much smaller worst drawdown.
By decade
| Decade | Market, annual return | Rule, annual return | Market, worst drawdown | Rule, worst drawdown | Rule, switches |
|---|---|---|---|---|---|
| 1927–29 | 14.9% | 23.9% | −44% | −17% | 5 |
| 1930–39 | −0.4% | 8.4% | −80% | −34% | 67 |
| 1940–49 | 9.1% | 10.1% | −31% | −15% | 58 |
| 1950–59 | 18.2% | 16.7% | −21% | −12% | 58 |
| 1960–69 | 8.3% | 10.3% | −28% | −9% | 45 |
| 1970–79 | 6.1% | 10.7% | −48% | −16% | 63 |
| 1980–89 | 16.8% | 18.8% | −33% | −15% | 34 |
| 1990–99 | 17.9% | 15.8% | −22% | −14% | 42 |
| 2000–09 | −0.4% | 2.4% | −55% | −29% | 96 |
| 2010–19 | 13.6% | 8.0% | −20% | −22% | 58 |
| 2020–26 | 15.5% | 12.2% | −34% | −19% | 36 |
Each decade compounded from one on its first day; the first and last are partial.
The rule beat the market in the decades of long bear markets, the 1930s, the 1970s and the 2000s, and lagged it in strong, steady ones: the 1950s, the 1990s, so far the 2020s and, most of all, the 2010s, when it returned 8.0% a year against 13.6%. Its drawdowns were smaller in almost every decade. Anyone who adopted the rule in 2010 on the strength of the previous eighty years would have spent ten years underperforming and been right to ask whether it had stopped working.
In the deepest drawdowns
| Market peak | Trough | Recovered | Market | Rule, worst in the same span | Rule, peak to trough only |
|---|---|---|---|---|---|
| 1929-09-03 | 1932-07-08 | 1945-02-06 | −84% | −34% | −20% |
| 2007-10-09 | 2009-03-09 | 2012-02-28 | −55% | −22% | −11% |
| 2000-03-24 | 2002-10-09 | 2006-10-23 | −49% | −29% | −26% |
| 1973-01-11 | 1974-10-03 | 1976-12-28 | −48% | −14% | −8% |
| 1968-11-29 | 1970-05-26 | 1971-04-27 | −37% | −9% | −9% |
This is where the rule earns its reputation: in the five deepest market drawdowns its own worst loss over the same span was between 24% and 59% of the market’s, about a quarter to three-fifths. The mechanism is simple: a decline deep enough to matter lasts long enough to take the index below its average, and the rule steps aside for the rest of it. The price is paid later. In three of the five episodes (1929, 2007 and 1973) the rule’s worst loss began only after the market’s low: having gone back in, it rode a later fall (September 1932 to March 1933, April to August 2010 and July to September 1975). In the others it began before the low. Measured from each peak to the market’s trough only, the rule lost between 8% and 26%. The decline of 2007–09 shows the mechanism and its price, step by step.
- Step 1 of 4
Above the average
In the summer of 2007 the market’s total-return index stood above its 200-day average, so the rule held the market. The index reached its high on 9 October 2007.
- Step 2 of 4
The whipsaw
From 6 August 2007 the index hovered around its average, and every crossing was a switch at the close: 21 of them, in and out, before the rule stepped aside for the rest of the decline. Its first day in bills of that long exit was 9 June 2008, with the index already 11% below its high.
- Step 3 of 4
Out through the crash
The rule then held T-bills while the index fell a further 49% to its low of 9 March 2009. From the 2007 high to its exit the rule lost at most 11.4%, in those whipsaws. Over the whole episode, until the market regained its high in February 2012, the market’s worst drawdown was −54.6% and the rule’s −22.0%. The rule’s came after this chart ends, from April to August 2010: 12% as it stayed in through a fall until its exit, and 11% more over the nine switches that followed.
- Step 4 of 4
Back in, late
The index crossed back above its average at the end of May 2009, and the rule’s first day back in the market was 1 June 2009, when the index was already 37% above its low. Waiting for the trend to confirm costs the start of every rebound: that is the price of the protection in the previous step.
US market total return and one-month T-bills, Kenneth R. French Data Library, daily, June 2007 to December 2009: the rule of Figure 3 with its defaults (a 200-day average, trading at the signal’s close, no cost, no band). The ticks under the top panel mark switches: the lower row a first day in bills, the upper row a first day back in the market. Both panels start from 1 June 2007; the lower one measures drawdowns from the high of 9 October 2007. Without script, or with reduced motion, the figure shows its final state.
Whipsaw
The cost of that protection is whipsaw. Of 281 round trips (an exit and the next re-entry), 247 (88%) were out of the market for 60 trading days or less, and on average the market rose 1.2% more than T-bills while those quick round trips were out. The worst are listed below; the largest, from 18 August 1939 to 6 September 1939, missed 7.8% in 15 trading days.
| First day out | First day back in | Days out | Missed, net of bills |
|---|---|---|---|
| 1939-08-18 | 1939-09-06 | 15 | 7.8% |
| 2000-04-17 | 2000-04-19 | 2 | 6.9% |
| 1932-10-11 | 1932-10-13 | 1 | 6.9% |
| 1933-03-18 | 1933-04-20 | 27 | 6.0% |
| 2010-06-07 | 2010-06-16 | 7 | 4.8% |
| 1933-10-20 | 1933-10-24 | 3 | 4.4% |
Lookback, execution and costs
The lookback
Figure 4 repeats the analysis for every lookback from 50 to 300 days in steps of ten. The Sharpe ratio ranges from 0.68 to 0.84 and the annual return from 11.1% to 12.2%; every lookback beats buy and hold on Sharpe ratio, and 200 days is not special. That flatness is reassuring, with a caveat: every lookback shares the same few bear markets, so the 26 results are far from 26 independent confirmations. The worst drawdown, by contrast, jumps around, because it is decided by how a handful of crashes happened to line up with each average. At 200 days it was −34%, the best of the 26 (tied with another lookback); the median across lookbacks was −41%. Quote the median, not the famous number.
Execution and costs
| Variant | Annual return | Sharpe | Worst drawdown | Worst day | Switches a year |
|---|---|---|---|---|---|
| Buy and hold | 10.2% | 0.46 | −84% | −17.4% | — |
| Rule, trade at the signal’s close | 11.5% | 0.73 | −34% | −9.2% | 5.7 |
| Rule, trade at the next close | 10.6% | 0.66 | −39% | −17.4% | 5.7 |
| Rule, next close and 10 bp per switch | 10.0% | 0.61 | −41% | −17.4% | 5.7 |
| Rule with look-ahead (wrong) | 20.2% | 1.37 | −25% | −9.2% | 5.7 |
| Rule, 10 bp per switch | 10.9% | 0.68 | −34% | −9.2% | 5.7 |
| Rule, 25 bp per switch | 10.0% | 0.61 | −37% | −9.2% | 5.7 |
| Rule, ±1% band | 11.0% | 0.69 | −36% | −9.2% | 2.5 |
| Rule, ±2% band | 11.1% | 0.70 | −43% | −9.2% | 1.5 |
Costs are charged per unit of exposure switched. The band switches in only above the average by the band and out only below it by the band.
Four things in this table matter. First, look-ahead multiplies the return by 1.75: the error is not a rounding matter. Second, one day of delay costs 0.9 percentage points a year and changes the worst day from −9.2% to −17.4%. The worst-day change is a single date: the index closed below its average on Friday 16 October 1987, so selling at that close avoided Monday’s −17.4%, and selling at Monday’s close took all of it. That one day explains only part of the annual difference; the rest is spread across hundreds of switches, each a day late. Third, one day of delay and 10 bp a switch together remove the return advantage entirely (10.0% against 10.2%), while the Sharpe ratio and drawdown still favour the rule. Fourth, a band cuts the switches by more than half, but both bands cost a little return and deepen the worst drawdown, from −34% to −36% at 1% and to −43% at 2%, so a band is a cost decision with a price of its own.
What the literature says
The evidence on technical rules is long and divided. Fama and Blume found that simple filter rules on individual stocks did not beat buy and hold once costs were counted.5 Brock, Lakonishok and LeBaron tested moving-average and trading-range rules on the Dow Jones index from 1897 to 1986 and found returns after buy signals that standard time-series models could not explain.6 Sullivan, Timmermann and White then corrected for the full universe of rules that could have been tried: the best rule remained significant in the original sample but not in the decade that followed.7 Lo, Mamaysky and Wang gave chart patterns a statistical footing and found that some carried incremental information,8 and Park and Irwin’s survey concluded that the positive results cluster in older samples and weaken with better controls for data snooping and costs.9
For asset allocation, Faber popularised a monthly version of this note’s rule, a 10-month average applied to several asset classes, and reported equity-like returns with much smaller drawdowns;10 Zakamulin examined the real-life performance of moving-average and momentum timing rules and found the out-of-sample advantage smaller and concentrated in bear markets.11 The wider trend-following literature puts the same idea across many markets.1,12 This note’s result sits comfortably inside that literature: protection in long declines, a price paid in whipsaw and in strong bull markets, and returns that depend on the period.
Implications
- Treat a trend indicator as a risk overlay first: its robust property is avoiding the middle of long declines.
- Lag every signal, test one day later as well, and distrust any result that hinges on a single close.
- Charge realistic costs per switch, and consider a band or a monthly check if switches cluster.
- Report the whole neighbourhood of lookbacks and the per-decade table, not a single number.
- Decide in advance how long the rule may lag the market, remembering the 2010s, before concluding that it has stopped working.
References
- Moskowitz, T. J., Ooi, Y. H., & Pedersen, L. H. (2012). Time series momentum. Journal of Financial Economics, 104(2), 228–250. https://doi.org/10.1016/j.jfineco.2011.11.003
- Wilder, J. W. (1978). New concepts in technical trading systems. Trend Research, Greensboro, NC.
- French, K. R. (n.d.). Data Library: Fama/French factors (daily) and momentum factor (daily). Tuck School of Business, Dartmouth College. mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html
- Federal Reserve Bank of St. Louis (n.d.). S&P 500 (SP500). FRED, Federal Reserve Economic Data. fred.stlouisfed.org/series/SP500
- Fama, E. F., & Blume, M. E. (1966). Filter rules and stock-market trading. Journal of Business, 39(1), Part 2, 226–241. https://doi.org/10.1086/294849
- Brock, W., Lakonishok, J., & LeBaron, B. (1992). Simple technical trading rules and the stochastic properties of stock returns. Journal of Finance, 47(5), 1731–1764. https://doi.org/10.1111/j.1540-6261.1992.tb04681.x
- Sullivan, R., Timmermann, A., & White, H. (1999). Data-snooping, technical trading rule performance, and the bootstrap. Journal of Finance, 54(5), 1647–1691. https://doi.org/10.1111/0022-1082.00163
- Lo, A. W., Mamaysky, H., & Wang, J. (2000). Foundations of technical analysis: Computational algorithms, statistical inference, and empirical implementation. Journal of Finance, 55(4), 1705–1765. https://doi.org/10.1111/0022-1082.00265
- Park, C.-H., & Irwin, S. H. (2007). What do we know about the profitability of technical analysis? Journal of Economic Surveys, 21(4), 786–826. https://doi.org/10.1111/j.1467-6419.2007.00519.x
- Faber, M. T. (2007). A quantitative approach to tactical asset allocation. Journal of Wealth Management, 9(4), 69–79. https://doi.org/10.3905/jwm.2007.674809
- Zakamulin, V. (2014). The real-life performance of market timing with moving average and time-series momentum rules. Journal of Asset Management, 15(4), 261–278. https://doi.org/10.1057/jam.2014.25
- Hurst, B., Ooi, Y. H., & Pedersen, L. H. (2017). A century of evidence on trend-following investing. Journal of Portfolio Management, 44(1), 15–29. https://doi.org/10.3905/jpm.2017.44.1.015