Applying Research

Key Concepts

Introduction

The ultimate goal of research is to produce a strategy that you can backtest and eventually trade live. Once you've developed a hypothesis that you're confident in, you can start working towards exporting your research into backtesting. To export the code, you need to replace QuantBook() with self and replace the QuantBook methods with their QCAlgorithm counterparts.

Workflow

Imagine that you've developed the following hypothesis: stocks that are below 1 standard deviation of their 30-day mean are due to revert and increase in value. The following Research Environment code picks out such stocks from a preselected basket of stocks:

import numpy as np
qb = QuantBook()

symbols = {}
assets = ["SHY", "TLT", "SHV", "TLH", "EDV", "BIL",
"SPTL", "TBT", "TMF", "TMV", "TBF", "VGSH", "VGIT",
"VGLT", "SCHO", "SCHR", "SPTS", "GOVT"]

for i in range(len(assets)):
    symbols[assets[i]] = qb.add_equity(assets[i],Resolution.MINUTE).symbol

# Fetch history on our universe
df = qb.history(qb.securities.keys, 30, Resolution.DAILY)

# Make all of them into a single time index.
df = df.close.unstack(level=0)

# Calculate the truth value of the most recent price being less than 1 std away from the mean
classifier = df.le(df.mean().subtract(df.std())).tail(1)

# Get indexes of the True values
classifier_indexes = np.where(classifier)[1]

# Get the Symbols for the True values
classifier = classifier.transpose().iloc[classifier_indexes].index.values

# Get the std values for the True values (used for magnitude)
magnitude = df.std().transpose()[classifier_indexes].values

# Zip together to iterate over later
selected = zip(classifier, magnitude)

Once you are confident in your hypothesis, you can export this code into the backtesting environment. The algorithm will ultimately go long on the stocks that pass the classifier logic. One way to accommodate this model into a backtest is to create a Scheduled Event that uses the model to pick stocks and place orders.

def initialize(self) -> None:
    self.set_start_date(2014, 1, 1)
    self.set_cash(1000000)
    self.set_benchmark("SPY")
    
    self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
    self.set_execution(ImmediateExecutionModel())
    
    self.assets = ["IEF", "SHY", "TLT", "IEI", "SHV", "TLH", "EDV", "BIL",
                   "SPTL", "TBT", "TMF", "TMV", "TBF", "VGSH", "VGIT",
                   "VGLT", "SCHO", "SCHR", "SPTS", "GOVT"]
    
    self.symbols = {}
    
    # Add Equity ------------------------------------------------ 
    for i in range(len(self.assets)):
        self.symbols[self.assets[i]] = self.add_equity(self.assets[i], Resolution.MINUTE).symbol 
        
    # Set the Scheduled Event method
    self.schedule.on(self.date_rules.every(DayOfWeek.monday), self.time_rules.after_market_open("IEF", 1), self.every_day_after_market_open)

Now that the Initialize method of the algorithm is set, export the model into the Scheduled Event method. You just need to switch qb with self and replace QuantBook methods with their QCAlgorithm counterparts. In this example, you don't need to switch any methods because the model only uses methods that exist in QCAlgorithm.

def every_day_after_market_open(self):
    qb = self
    # Fetch history on our universe
    df = qb.history(qb.securities.keys, 5, Resolution.DAILY)
    
    # Make all of them into a single time index.
    df = df.close.unstack(level=0)
    
    # Calculate the truth value of the most recent price being less than 1 std away from the mean
    classifier = df.le(df.mean().subtract(df.std())).tail(1)
    
    # Get indexes of the True values
    classifier_indexes = np.where(classifier)[1]
    
    # Get the Symbols for the True values
    classifier = classifier.transpose().iloc[classifier_indexes].index.values
    
    # Get the std values for the True values (used for magnitude)
    magnitude = df.std().transpose()[classifier_indexes].values
    
    # Zip together to iterate over later
    selected = zip(classifier, magnitude)
    
    # ==============================
    
    insights = []
    
    for symbol, magnitude in selected:
        insights.append(Insight.price(symbol, timedelta(days=5), InsightDirection.UP, magnitude))
    
    self.emit_insights(insights)

With the Research Environment model now in the backtesting environment, you can further analyze its performance with its backtesting metrics. If you are confident in the backtest, you can eventually live trade this strategy.

To view full examples of this Research to Production workflow, see the examples in the menu.

Contribute Tutorials

If you contribute Research to Production tutorials, you'll get the following benefits:

  • A QCC reward
  • You'll learn the Research to Production methodology to improve your own strategy research and development
  • Your contribution will be featured in the community forum

To view the topics the community wants Research to Production tutorials for, see the issues with the WishList tag in the Research GitHub repository. If you find a topic you want to create a tutorial for, make a pull request to the repository with your tutorial and we will review it.

To request new tutorial topics, contact us.

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: