This guest post was written by Troy Bombardia. He shares free market analysis, data, and trading algorithms at Bull Markets.
This is Part 3 in this Python for Trading Series. Parts 1 and 2 can be found here and here. In Part 3 I will teach you how to code a simple yet effective trading algorithm using Pandas. This strategy trades 3 ETFs:
- QQQ (NASDAQ ETF)
- SPY (S&P 500 ETF)
- TLT (Treasury Bond ETF)
You ready? Let’s start!
Import the libraries we need
First, we need to import 2 libraries: Pandas and Numpy. As I said in Part 2 of this series, Python “libraries” are collections of pre-built functions that you can use in your trading algorithms. Pandas and Numpy are very powerful and widely used in finance.
We also need to import matplotlib, which is used for graphing in Python.
Once you run these 3 lines of code, your text editor (e.g. Jupyter) will automatically import these libraries. You’re done!
Read your data
We need to “read” the data from the csv, and turn this data into a Pandas Dataframe. A dataframe is similar to an Excel table: it has rows and columns. We will use the data in this dataframe for our algorithm.
“df” is the variable we use for this Dataframe.
pd.read_csv() is the function we use to “read” the data from our CSV file. Pass in the path where your CSV file is located on your computer. On my computer, it’s ‘C:\\Users\\vaish\\Desktop\\pythoncode\\youtube_new\\python_part_three.csv’. The CSV file’s location on your computer will be different depending on where you downloaded the file.
Calling “df” prints the dataframe in Jupyter. As you can see, this Dataframe has 4 columns and 4640 rows. By default Jupyter will display the first and last 5 rows. This doesn’t mean that the other rows disappeared – it’s just Jupyter’s way of making sure the Output doesn’t look too long.
Calculate daily percent change
The next block of code calculates each ETF’s daily % change. To do this we use the .pct_change() function
- We create a new column called df[‘QQQ Change’]. We set this new column equal to the percent change of df[‘QQQ’]
- We create a new column called df[‘SPY Change’]. We set this new column equal to the percent change of df[‘SPY’]
- We create a new column called df[‘TLT Change’]. We set this new column equal to the percent change of df[‘TLT’]
Trading algorithm’s criteria
This trading algorithm’s strategy is as follows:
- If QQQ is above its 50 day moving average, go long QQQ for tomorrow.
- If QQQ is not above its 50 dma, AND SPY’s 20 dma is above its 100 dma, go long SPY for tomorrow.
- If neither of the above conditions are TRUE (i.e. you don’t go long QQQ or SPY), go long TLT for tomorrow.
- The 1st line of code creates a new column called df[‘QQQ Trend’]. This column is set to equal the question “Is QQQ above its 50 dma”. This question returns a TRUE or FALSE and inserts this TRUE/FALSE into df[‘QQQ Trend’]
- The 2nd line of code creates a new column called df[‘SPY Trend’]. This column is set to equal the question “Is SPY’s 20 dma above its 200 dma”. This returns and inserts a TRUE or FALSE into df[‘SPY Trend’]
- The 3rd line of code creates a column df[‘QQQ Long’]. This column is filled with “TRUE” and “FALSE”. It returns a TRUE when “QQQ is above its 50 dma”. Otherwise it returns a FALSE.
- The 4th line of code creates a column df[‘SPY Long’]. This column returns a TRUE when “QQQ is not above its 50 dma AND SPY’s 20 dma is above its 100 dma”. Otherwise it returns a FALSE.
- The 5th line of code creates a column df[‘TLT Long’]. This column returns a TRUE when df[‘QQQ Long’] and df[‘SPY Long’] are FALSE.
Remove the rows you don’t need
Remember that one of our algorithm’s criteria is “is SPY’s 20 dma above its 100 dma”.
This means that the first 99 rows are useless because a “100 day moving average” can only begin on the 100th row. Thus, we need to eliminate the first 99 rows from our dataframe.
- Create a list called “lst”. Set this list to equal every number from 0 to 99.
- Use the .drop() function to drop rows 0 to 98.
Calculate the trading algorithm’s performance over time
It’s time to calculate this trading strategy’s performance over time. We also need to compare it against buy and hold SPY.
*The above screenshot cuts out some code. The code that was cutout is as follows:
df[‘Strategy’] = 100*(1+df[‘QQQ Long’].shift(1)*df[‘QQQ Change’]+df[‘SPY Long’].shift(1)*df[‘SPY Change’]+df[‘TLT Long’].shift(1)*df[‘TLT Change’]).cumprod()
- The 1st line of code essentially calculates buy and hold for SPY, setting an initial portfolio value of $100. We take the daily compounded returns for SPY and multiply it by $100.
- The 2nd line of code calculates the trading algorithm’s performance, setting an initial portfolio value of $100. Every single day we look at whether yesterday we were supposed to buy QQQ, SPY, or TLT. Then we take the daily compounded returns for this strategy and multiply it by $100.
Graph the performance
Let’s chart the performance for this trading strategy against buy and hold SPY.
*The above screenshot cuts out some code. The code that was cutout is as follows:
graph = df[[‘Strategy’, ‘Buy and hold SPY’]].plot(grid=True, kind=’line’, title =”Testing trading algorithm”, logy=True, figsize=(11, 6))
- The 1st line of code uses the .set_index() function to set the x-axis as “Date”.
- The 2nd line of code uses the .plot() function to graph these 2 strategies.
As you can see, this strategy beats buy and hold with less volatility.
Calculate average return
Finally, let’s calculate this trading algorithm’s average annual return.
- The 1st line of code uses the .iloc function to find the starting value of the df[‘Strategy’] column.
- The 2nd line of code uses the .iloc function to find the ending value of the df[‘Strategy’] column.
- “years” is a variable that stores how many years this strategy has been backtested. In this case, years = 18
- The average_return variable returns the average annual return for this trading algorithm.
The average annual return is 15.35%
Thank you for reading this tutorial. I will share more simple and complicated trading algorithms in future parts of this Python for Trading Series. Happy New Year!
This post was by Troy Bombardia of Bull Markets of can follow him on Twitter at @BullMarketsco.:
Previous posts:
Python for Trading Series – Part 1