How to build a quantitative(quant) trading strategy in Python

With the ever increasing popularity of using quantitative trading strategies to perform systematic trading, in this post I’ll be showing you how to build simple quant trading strategy using Python. A quant trading strategy is one where decisions for buying and selling securities is based upon conditions that are created from analyzing large amounts of data and finding profitable strategy. These conditions could be anything like “buy S&P 500 when the price goes above it’s 200 day simple moving average and sell when the price falls below the 200dma”. This in turn helps eliminate human emotions and personal biases from trading decisions.

Python is a programming language which helps with automation and AI which has become an integral part of the finance realm changing the traditional ways of the finance industry. Traders are being replaced by trading bots that execute trades more accurately, larger and larger amounts of financial data are being processed to detect correlations and mobile banking has become a reality among a lot more changes. Python is one of the top 3 programming languages used in the finance industry. Python’s popularity can be attributed to the following:

  • Python’s easy to read and write syntax which helps increase the speed of building minimum viable products
  • availability of large amounts of libraries, tools and packages instead of building from scratch, and
  • a large support community that contribute to open source projects, build and improve tools

Now that we have looked at what python can do, let’s apply it to build a quant trading strategy.

To build a quant trading strategy, we need to first define the conditions for buy and sell. For this post I will show 2 simple trading strategies applied to the S&P 500 that beats buy and hold.

For strategy 1:

  • Buy if the price is above the 200 day simple moving average
  • Sell if the price falls below the 200 day simple moving average

For strategy 2:

  • Buy when the 50 day simple moving average crosses above the 200 day simple moving average or when the price is above the 200 day simple moving average
  • Sell when the 50 day moving average crosses below the 200 day moving and the price falls below the 200 day moving average

Now that the strategy is defined, let’s move on to coding and backtesting the quant strategy in python.

There are seven parts to this:

  1. Importing python libraries and modules
  2. Getting the S&P 500 data
  3. Calculating the quant strategy inputs like the 200 day simple moving average
  4. Quant strategy’s buy and sell conditions
  5. Calculating the trading strategy’s changes over time
  6. Calculating the quant trading strategy’s returns and
  7. Plotting out the strategies’ returns

1) Importing python libraries and modules


import pandas as pd

%matplotlib inline

Here we import pandas library that we will be using to manipulate the data. We import it as pd as we can call it as ‘pd.command’ instead of ‘pandas.command’ each time.

The second line of code helps with plotting the graph within the same notebook environment as where this code is being written. For this tutorial, I’m using Jupyter notebook to write my code in.

2) Getting the S&P 500 data


df = pd.read_csv('/Users/vaish/Desktop/Coding/Python for Finance/firstmodel.csv')

df.columns = df.columns.str.strip()

There are many ways to pull in the securities’ data into your code. For this tutorial, I have downloaded the S&P 500 data from Yahoo Finance (ticker: GSPC) as a csv file.

The first line here reads the csv file into a dataframe called ‘df’. This is done using pandas. A dataframe basically looks like an excel spreadsheet with columns and rows making working with data easier. The data in the ‘( )’ brackets is the user path of the csv file in your computer.

The second line simply removes the blank space in the column names of the dataframe to avoid any errors while running the code.

3) Calculation of quant trading strategy inputs


df['% change'] = df['Close'].pct_change()
df['200 sma'] = df['Close'].rolling(window=200).mean().round(5)
df['50 sma'] = df['Close'].rolling(window=50).mean().round(5)

The first line here creates a column called ‘% change‘  where it calculates the daily percent change of the S&P 500. This calculation is done on the ‘Close’ column data.

The second line calculates the 200 day simple moving average on the close data and stores it in the new ‘200 sma’ column of the dataframe ‘df’.

The third line calculates the 50 day simple moving average on the close data and stores it in the new ’50 sma’ column of the dataframe ‘df’.

4) Quantitative trading model’s criteria


df['Criteria 1'] = df['Close'] >= df['200 sma']
df['Criteria 2'] = (df['50 sma'] >= df['200 sma']) | df['Criteria 1'] == True

The first line here lays out the condition for the strategy 1 as described in the beginning of the post. It creates a new ‘Criteria 1’ column that basically checks if the price is above the 200 day simple moving average. The ‘Criteria 1’ column will contain boolean type of data (True or False).

Similarly, the second line lays out the condition for the strategy 2 as described in the beginning of the post. It creates a new ‘Criteria 2’ column that checks if the 50 day simple moving average is equal to or above the 200 day simple moving average ‘OR’ if the price is above the 200 day simple moving average (Criteria 1). The ‘Criteria 2’ column also contains boolean type of data (True or False).

5) Calculation of Model’s changes over time


df['Buy and hold'] = 100*(1+df['% change']).cumprod()
df['200 sma model'] = 100*(1+df['Criteria 1'].shift(1)*df['% change']).cumprod()
df['200 sma + crossover model'] = 100*(1+df['Criteria 2'].shift(1)*df['% change']).cumprod()

The first line here creates a column ‘Buy and hold’ that shows the changes for buy and hold over time with a starting value of 100.

The ‘200 sma model’ in line 2 is just the strategy 1 we have looked at. A new column is created to show the changes of strategy 1’s value over time keeping 100 as the beginning value.

And the ‘200 sma + crossover model’ in line 3 is basically the second strategy and the column stores the changes of strategy 2’s value over time keeping 100 as the beginning value.

6) Calculating the trading strategy’s returns


#buy and hold's returns
start_spx = df['Close'].iloc[200]
end_spx = df['Close'].iloc[-1]
years = (df['200 sma model'].count()+1-200)/252
spx_average_return = (end_spx/start_spx)**(1/years)-1
print('Buy and hold yields an average of', spx_average_return, '% per year')

# 200 sma model's returns
start_model1 = df['200 sma model'].iloc[200]
end_model1 = df['200 sma model'].iloc[-1]
model1_average_return = (end_model1/start_model1)**(1/years)-1
print('200 sma model yields an average of', model1_average_return*100, '% per year')

#200 sma + crossover model's returns
start_model2 = df['200 sma + crossover model'].iloc[200]
end_model2 = df['200 sma + crossover model'].iloc[-1]
model2_average_return = (end_model2/start_model2)**(1/years)-1
print('200 sma + crossover model yields an average of', model2_average_return*100, '% per year')

There’s 3 parts to this part of the code. We calculate the average returns of ‘Buy and hold’, ‘200 sma model’ and the ‘200sma + crossover model’.

For each, we calculate the start date that we use in the calculation, the end date and the number of years and store it in different variables with readable names. Using this we then calculate the average annual return for each of these strategies.

7) Plotting out the strategies’ results


df.set_index('Date')

df[['Buy and hold', '200 sma model', '200 sma + crossover model']].plot(grid=True, kind='line', title='Different models', logy=True, figsize = (20,10))

And finally this line of code plots out the 2 strategies along with the ‘Buy and hold’ on a log graph. The size of the figure can be adjusted by changing the value for the ‘figsize’  parameter.

As you can see, both the strategies outperform Buy and Hold by quite a bit.

Leave a Comment