Calculating Strategy Returns from Lagged Moving Average Signals
Summary
The document explains how to turn moving average crossover signals into a basic backtest return series. It recommends generating a position signal, shifting it by one time step to avoid using information before it would have been available, and multiplying the signal by the change in adjusted price. With positions representing one share, this produces daily dollar profit and loss; cumulatively summing those values gives running profit and loss, which can be added to starting capital to track equity.
The example uses Amazon prices and short and long simple moving averages, but it does not report a final return or demonstrate a full portfolio accounting model. Its calculation assumes a fixed one-share exposure and omits transaction costs, slippage, position sizing, and reinvestment. The discussion therefore illustrates return bookkeeping rather than a complete or investment-ready backtest. It also notes that a more elaborate portfolio-class approach may be difficult for a beginner to follow.
Key ideas
- Lag a trading signal so the backtest does not use information before it becomes available.
- Multiplying a position signal by price changes gives period profit and loss for the assumed position size.
- Cumulative profit and loss can be added to initial equity to show a running portfolio value.
- A one-share example does not account for costs, changing exposure, or full portfolio mechanics.
Tags
Full text
# How to get the the final % return in backtesting?
# How to get the the final % return in backtesting?
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.
## Answer by Jacob Amos (score 4, accepted)
https://quant.stackexchange.com/a/22937
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
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.