Because I love programming since my first experiences at the age of six I wanted to explain you how you can code your own backtests.
Did you find a signal you would like to know how it worked in the past 20 years?
We do that know together!
First the candlesticks …
At first we need the candlestick data with date, open, high, low, close and volume of each day.
You can get the data from your broker, for example through the API of Interactive Brokers or the csv-file export of TC2000, or you use one of the free or cheap APIs (IEX, Quandl, …).
For our example you can download the needed data here.
… and the import in your program
Now you’re thinking: Hey, I didn’t code anything yet! No problem, it’s not that difficult.
I know many coding languages like JavaScript or PHP but after programming my tools for stocks and trading around 5 years I can recommend to you:
The best language for programming with financial data is Python!
Let’s start our example with the SPY, daily candles and a simple moving average crossing strategy.
To read the data of our source file (SPY.csv
, located in the same folder), we just need write this line into a text file named “backtest.py
”:
data = pd.read_csv("SPY.csv")
The “pd” stands for pandas, a library for data calculations and comparison which we have to integrate in our code before the line above:
import pandas as pd
To check the data by printing the last 5 rows/days on the screen we can add
print(data.tail())
The strategy
The simple moving average over 200 days (200 SMA) for all rows can be calculated by
data['SMA200'] = data.Close.rolling(200).mean()
and to check if the close is above the SMA 200 just type
data['above'] = data.Close > data.SMA200
Now we have 2 new columns with ‘SMA200
‘ and ‘above
‘ in our dataset. We will add three more for the “not above” and for the buy and sell signal when crossing (-
stands for not
and here we have to use &
instead of and
):
data['notabove'] = -data.above
data['buy'] = data.above & data.notabove.shift(1)
data['sell'] = data.notabove & data.above.shift(1)
Checking each day with one loop
The ‘shift
‘ moves a column one row up (shift(-1)
) or down (shift(1)
), so here we can compare with the day before.
But how to code the backtest for every row of data? While iterating through the rows, we invest at the buy signal and close the position at the sell signal while setting the variable invest
true or false:
total_return = 0.0
invested = False
for index, row in data.iterrows():
if row.buy and not invested:
entry = row.Close
invested = True
if row.sell and invested:
exit = row.Close
trade_return = exit/entry - 1
total_return += trade_return
invested = False
Show our result
And finally we just have to output the total return:
print("Total return from", data.index[0], "to", data.index[-1], "is", round(total_return*100, 2), "%.")
Now you start our program with this command – and hopefully the same output if you used the source file of the above link:
>> python3 backtest.py
Total return from 1993-01-29 to 2019-10-08 is 172.33 %.
Therefore you had profits of 172.33% if you invested the same amount with our buy signal since 1993. It’s not difficult to change it to the cumulative return or to compare with a buy & hold strategy.
If there are any questions, experiences or wishes about how to code a backtest I would like to read your comment below. I wish you good results with your code, fun with many combinations and a strategy that fits best to you!
2 Comments
Alessandro
(10. November 2019 - 19:27)dear Alexander,
could you explain, why (and “if”) it is important to forward test a strategy which showed positive results in the backtest? And if yes, for how long should the forward test last?
And could you explain the concept of “curve fitting” in simple words?
Alexander
(13. November 2019 - 12:42)Because I did many backtests but I am not an expert for backtesting and working strategies yet, I can only write my opinion:
1. Why forward test?
All kinds of strategy tests can only cover an area and not the whole scope of a strategy. Therefore I recommend to use all possibilities to test a strategy before trading it. And therefore after backtesting a collection of contracts in a period of time you should trade it in a paper account for some months. Why? Then you can compare your statistics of the paper trades with those of your backtests. If the two kinds of tests will give you the same conclusions and results there’s a higher probability of being successful with your strategy.
2. How long to forward test?
How long did you backtest? 10 years? 20 years? If you tested daily candles you should have tested about 500 or more rows of data and maybe 300 to 1000 trades. How many trades should you trade to compare the forward test with your backtest? Several hundreds.
But of course, you will recognize after 50-100 trades when the forward test is running in another direction than you expected. So, you can just start with your forward test and decide when you’re confident that results in live trading will be as expected.
3. Curve fitting
Simple words? Uh, I will try: If you change your conditions for a backtest to get perfect results you have to pay attention on not changing too many details because your past trades do not cover the whole scope of possible trades. But when it’s the improvement of a strategy and when it’s curve fitting? Nobody knows. But you can take several areas maybe stocks of different sectors or some stocks and compare the results of this section with the whole backtest. It’s a good sign when everything follows the same rules and results. Or you forward test (see (2)) your strategy and compare those results with your backtest.
I hope now you understand the details of backtests, forward tests and curve fitting much better?