How to implement a basic trading algorithm in QuantConnect using Python (for data scientists)

samcha
4 min readOct 18, 2018

Code adapted from https://www.quantopian.com/posts/simple-machine-learning-example-mk-ii.

Ever wondered how to apply your machine learning/data science skills to algorithmic trading?

If you already know Python and basic statistical principles of data science (like train-test-split, over-fitting, etc), you’re already way ahead of the curve.

Translating machine learning models into trading algorithms is pretty simple, once you know some of the quirks of how data is handled and executed in these environments.

Let’s look at a super-basic machine learning model (adapted to QuantConnect from the Quantopian platform).

Go here to make an account for QuantConnect.

See the code, backtest, and stats here.

1. Get imports

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

2. Understand the basic QuantConnect structure

Here’s a code skeleton of our algo:

class SimpleML(QCAlgorithm):    def Initialize(self):

# Set settings here.
# Instantiate variables here.
# Schedule events here.
def OnData(self, data): # Handle data live.

def scheduled_event(self):

# Scheduled event function.

In platforms like QuantConnect and Quantopian, algos are structured in classes. This is where you set your settings, instantiate variables, and schedule other functions you want to run.

3. Settings

Let’s fill out the def Initialize(self) part.

def Initialize(self):    self.SetStartDate(2018,1,1)
self.SetEndDate(2018,9,1)
self.SetCash(100000)
self.AddEquity('AMZN', Resolution.Daily)

self.model = GradientBoostingRegressor()

self.lookback = 30
self.history_range = 200

self.X = []
self.y = []

self.Schedule.On(self.DateRules.WeekStart(), self.TimeRules.BeforeMarketClose('AMZN', minutes=10), Action(self.create_model))
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('AMZN', minutes=1), Action(self.trade))
  • self.SetStartDate(): The start date of backtest (YYYY, MM, DD).
  • self.SetEndDate(): The end date of backtest (YYYY, MM, DD).
  • self.SetCash(100000): How much cash to trade.
  • self.AddEquity(‘AMZN’, Resolution.Daily): Add any instruments we want data on, as well as the resolution. More on adding instruments in QuantConnect’s documentation.
  • self.model: Instantiate our model.
  • self.lookback: Number of past samples to train on.
  • self.history_range: Range of history we’ll train on.
  • self.X: Empty list for our features (in this case, past returns).
  • self.y: Empty list for target (the next day’s return).
  • self.Schedule.On(): This is how we call functions we’ve defined.

Note we’re calling our create_model function on the first trading day of the week, 10 minutes before close. Then we predict and trade off the model each day 10 minutes after open.

4. OnData

OnData handles live data for the algo. This is only used if your algo is executing on a lower timeframe (like HTF algorithms). We won’t need this because we’re only fitting and trading once per day.

def OnData(self, data):

pass

5. Write modeling function

This part should look familiar. The important thing here is knowing how to read in past price data.

def create_model(self):

recent_prices = self.History(['AMZN'], self.history_range)['close'].values
price_changes = np.diff(recent_prices).tolist()

for i in range(self.history_range-self.lookback-1):
self.X.append(price_changes[i:i+self.lookback])
self.y.append(price_changes[i+self.lookback])

self.model.fit(self.X, self.y)
  • recent_prices = self.History(): This is how we call in past data for whatever instruments we added at the top of the algo. This will return a dataframe of open, high, low, and close for bid and ask, plus volume over the timeframe you specify. Selecting [‘close’] selects just the close of each day. (Remember, we specified Resolution.Daily at the top.)
  • price_changes = np.diff(recent_prices).to_list(): We’ll be predicting off the percentage price changes, from daily close to daily close. (If we wanted to be more statistically sound, we’d use the log returns, but let’s keep it simple for now.)
  • for loop: For each day in our history, our X will be the number of days in our look back. The y will be the return for that day. We loop through the number of days in history, less the lookback (so we don’t run out of room!).
  • Fit the model.

6. Write trade execution

Last part!

def trade(self):

if len(self.y) > self.lookback:

recent_prices = self.History(['AMZN'], self.lookback+1)['close'].values
price_changes = np.diff(recent_prices)

prediction = self.model.predict(price_changes.reshape(1, -1))

if prediction > 0:
self.SetHoldings('AMZN', 1.0)
else:
self.SetHoldings('AMZN', 0)
  • if len(self.y) > self.lookback: This is a check to make sure our y is at least the amount of our lookback.
  • prediction: Predict using our model, feeding in the most recent price changes.

If our prediction is positive, we go all-in! If not, we’re flat.

Results:

Here’s a look at our results on the QuantConnect platform. While it beats the market, you would have made +72% buy-and-hold AMZN during this time.

Complete code:

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

# Adapted from https://www.quantopian.com/posts/simple-machine-learning-example-mk-ii

class SimpleML(QCAlgorithm):

def Initialize(self):

self.SetStartDate(2018,1,1)
self.SetEndDate(2018,9,1)
self.SetCash(100000)
self.AddEquity('AMZN', Resolution.Daily)

self.model = GradientBoostingRegressor()

self.lookback = 30
self.history_range = 200

self.X = []
self.y = []

self.Schedule.On(self.DateRules.WeekStart(), self.TimeRules.BeforeMarketClose('AMZN', minutes=10), Action(self.create_model))
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('AMZN', minutes=1), Action(self.trade))

def OnData(self, data):

pass

def create_model(self):

recent_prices = self.History(['AMZN'], self.history_range)['close'].values
price_changes = np.diff(recent_prices).tolist()

for i in range(self.history_range-self.lookback-1):
self.X.append(price_changes[i:i+self.lookback])
self.y.append(price_changes[i+self.lookback])

self.model.fit(self.X, self.y)

def trade(self):

if len(self.y) > self.lookback:

recent_prices = self.History(['AMZN'], self.lookback+1)['close'].values
price_changes = np.diff(recent_prices)

prediction = self.model.predict(price_changes.reshape(1, -1))

if prediction > 0:
self.SetHoldings('AMZN', 1.0)
else:
self.SetHoldings('AMZN', 0)

Anything I can write about to help you find success in data science or trading? Tell me about it here: https://bit.ly/3mStNJG

--

--

samcha

Python, trading, data viz. Get access to samchaaa++ for ready-to-implement algorithms and quantitative studies: https://samchaaa.substack.com/