Skip to main content

← Back to the demo

Example.Written by the StrikeLab team to show what a finished capstone looks like; not a student's work.

ExampleBacktest a strategy honestly

Does a 50/200-day moving-average crossover beat buying and holding?

Question

The "golden cross" rule says buy when the 50-day average rises above the 200-day and sell when it falls below. Does it beat simply holding the index, after I remove the ways a backtest can fool me?

Code

import numpy as np

def backtest(prices):
    fast = np.convolve(prices, np.ones(50) / 50, mode="valid")[150:]
    slow = np.convolve(prices, np.ones(200) / 200, mode="valid")
    px = prices[199:]
    # Trade on TOMORROW's return: no look-ahead
    signal = (fast > slow)[:-1]
    daily = np.diff(px) / px[:-1]
    strat = daily * signal
    sharpe = lambda r: r.mean() / r.std() * np.sqrt(252)
    return sharpe(strat), sharpe(daily)

Results

Illustrative numbers for this example: over 20 years of an S&P 500 index fund, suppose the crossover's Sharpe ratio comes out at 0.52 versus 0.61 for buying and holding. A typical pattern: it sidesteps part of a crash like 2008 but misses the sharp rebounds after it, and those rebounds are where much of the return comes from.

Reflection

Testing on an index fund avoids survivorship bias (the index already includes companies that later failed), and trading on the next day's return avoids look-ahead. But I only tested one pair of window lengths. Trying many and reporting the best would be data snooping, so I'd pick windows on 2000–2012 and test once on 2013–2024.