Backtesting in Python

A minimal moving-average crossover backtest

6 min

Let us turn the SMA crossover idea into a small, self-contained vectorized backtest using just pandas and NumPy. This shows the logic; a real framework adds proper order handling and reporting.

The strategy

Go long when the fast moving average is above the slow one; be flat otherwise. A long-only, always-or-never position.

import numpy as np
import pandas as pd

def backtest_sma(df, fast=20, slow=50, cost_bps=2):
    px = df["close"]
    sma_fast = px.rolling(fast).mean()
    sma_slow = px.rolling(slow).mean()

    # Signal: 1 when fast above slow, else 0
    signal = (sma_fast > sma_slow).astype(int)

    # CRITICAL: trade on the NEXT bar to avoid look-ahead
    position = signal.shift(1).fillna(0)

    # Costs charged when the position changes
    trades = position.diff().abs().fillna(0)
    cost = trades * (cost_bps / 10000.0)

    ret = px.pct_change().fillna(0)
    strat_ret = position * ret - cost
    equity = (1 + strat_ret).cumprod()
    return equity, strat_ret

The single most important line

position = signal.shift(1).fillna(0)

The shift(1) is everything. The signal is computed from a bar's close, but you cannot act on it until the next bar opens. Without that shift you are trading on information you did not yet have — instant look-ahead bias and a backtest that looks magical and is worthless.

Reading the result

equity, r = backtest_sma(df)
sharpe = np.sqrt(252) * r.mean() / r.std()
print("final equity:", round(equity.iloc[-1], 3))
print("approx Sharpe:", round(sharpe, 2))

Honest caveats

This omits slippage beyond the flat cost, assumes you always fill at the close, ignores position sizing and shorting, and uses a single instrument and period. It is a teaching skeleton. Any equity curve it produces is hypothetical — treat it as a starting point for the rigorous testing in the rest of this track, not a result to trust.

Finished reading?
Risk disclaimer

This content is for educational and informational purposes only and is not investment, financial, tax or legal advice. Trading and investing carry risk, including the possible loss of capital. Any performance shown by third-party tools is hypothetical and not a promise of future results. Do your own research and consider professional advice before making any decision.