I'm learning how to do backtesting in Python using Pandas. I'm learning how to use Moving Average Crossover. I've generated signals to buy or to sell already. But I'm not sure where to go from there? Let's say if I have the initial capital of $100,000 what would be my final % return

I realise that this is a very basic question but I can't seem to wrap my head around it yet.

This is what I have it so far.

import datetime
import pandas as pd
from pandas_datareader import data, wb
import numpy as np
import Quandl
import matplotlib.pylab as pylab
%matplotlib inline

start_date = datetime.datetime(2009,1,1)
end_date = datetime.datetime(2014,1,1)
amzn = data.DataReader("AMZN", "yahoo", start_date, end_date)

def generate_signals(self):
    # Create DataFrame and initialise signal series to zero
    signals = pd.DataFrame(index=amzn.index)
    signals['signal'] = 0

    # Create the short/long simple moving averages
    signals['short_mavg'] = pd.rolling_mean(amzn['Adj Close'], 40, min_periods=1)
    signals['long_mavg'] = pd.rolling_mean(amzn['Adj Close'], 100, min_periods=1)

    # When the short SMA exceeds the long SMA, set the ‘signals’ Series to 1 (else 0)
    signals['signal'][40:] = np.where(signals['short_mavg'][40:] >
        signals['long_mavg'][100:], 1, 0)

    # Take the difference of the signals in order to generate actual trading orders
    signals['positions'] = signals['signal'].diff()
    return signals

I've taken the code from https://s3.amazonaws.com/quantstart/media/powerpoint/an-introduction-to-backtesting.pdf

The Portfolio part doesn't run for me so I'm trying to figure out what's supposed to happen in the actual backtesting.


1 Answer 1


Note: Assuming you're a bit of a beginner trying to learn the ropes of how this whole process works at a high level, I can definitely make a couple recommendations (if I'm interpreting that wrong then I apologize if the explanation below isn't what you're after).

If you're trying to learn some basic backtesting fundamentals, while QuantStart is an amazing educational resource, I might recommend writing a similar signal generation function outside of a Portfolio class (so no self function argument), just to get your feet wet with how the trading logic works. The presentation you're citing takes a much more rigorous object-oriented approach to whole thing that might not click contextually for someone just learning the basics.

Next, I think it's worth mentioning you'll want to make sure you lag your signals. Failing to do so is a common backtesting pitfall that can yield artificially good results.

Once you have a given signal at each time step you can calculate some returns. Let's say your signals are 1 for being long, -1 for being short, and 0 for being out of the market. Then you can compute the differences in price and multiply by your signals, so that a price increase will be favorable if your signal is 1 and a fall in price will be favorable if your signal is -1. That multiplication step will yield a gross (ie dollar amount) P&L for any given day assuming each trade is only 1 share.

Once you have that vector of daily P&L's, you can take a cumulative summation (cumsum function) to yield a total running P&L vector, to which you can add your initial portfolio equity of $100,000. Once you've done all that you can start doing some returns calculations.

Here's a small example similar to the code that you have above that works outside of a Portfolio class for doing a very simply & straightforward backtest.

import pandas as pd
from pandas.io.data import DataReader
import numpy as np

ticker = 'amzn'
px = DataReader(ticker, 'yahoo')

def generate_signals(px):
    signals = pd.DataFrame(index=px.index)
    signals['signal'] = 0
    short_ma = pd.rolling_mean(px['Adj Close'], 40, min_periods=1)
    long_ma = pd.rolling_mean(px['Adj Close'], 100, min_periods=1)
    signals['signal'] = np.where(short_ma > long_ma, 1, 0)
    return signals['signal'].shift(1)  # remember to lag your signals :)

px['Signals'] = generate_signals(px)
px['Daily P&L'] = px['Adj Close'].diff() * px['Signals']
px['Total P&L'] = px['Daily P&L'].cumsum()
print px
  • 1
    $\begingroup$ Thanks for your response! Exactly what I was looking for. Can you explain a bit what does 'lag your signals` mean? $\endgroup$
    – toy
    Jan 27, 2016 at 2:36
  • 1
    $\begingroup$ Absolutely, great question. So when you calculate your moving averages, a crossover will generate a signal on the same day. But since you're using the closing prices, in realtime trading you can't know if the closing prices will cause an MA-cross until market close. So by "lagging the signals" (pushing them up a timestep) a signal on a given day will correspond with the how the strategy performed on that day assuming that you would have traded at the previous close. Without lagging, you're inadvertently simulating the strategy with information you wouldn't have had in realtime. Does that help? $\endgroup$
    – Jacob Amos
    Jan 27, 2016 at 2:43
  • $\begingroup$ Just want to confirm if I interpret the result correctly. I just run your code and I got this 2016-01-26 601.250000 1 4.719971 315.169983 at the end. I assume this is just investing one share at a time. The last column is Total T&L does it mean that in the end I get $315. And if the number was negative that means I lose money? $\endgroup$
    – toy
    Jan 27, 2016 at 23:58
  • 1
    $\begingroup$ Correct on all accounts :) $\endgroup$
    – Jacob Amos
    Jan 28, 2016 at 0:13
  • $\begingroup$ @JacobAmos Is there a way to use the running P&L as base for the next position, in a vectorized way? $\endgroup$
    – chris
    Mar 1, 2023 at 15:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.