Hello, I am implementing intra-day trading algorithm and facing a slow backtest.
I wonder if you guys have a better idea than I currently have now.
The basic strategy is that we track all equities (no ETF), filter them based on cumulative intra-day dolllar volume for each one at 3pm, and buy 20 among them based on some algorithm that I have.
Here are two ways that I came up with, which both are really slow on backtesting.
<First Way>
Universe Resolution: Hour (Minute would be idea, but it is too slow)
Basic idea: Within OnData, calculate dollar volume (volume * close) and add it to dictionary.
import pandas as pd
class MyAlgo(QCAlgorithm):
def Initialize(self):
# Set start date and end date
self.SetStartDate(2020, 1, 1)
# Set strategy cash
self.SetCash(10000)
# Set benchmark
self.benchmark = Symbol.Create('SPY', SecurityType.Equity, Market.USA)
# Parameters
self.dollar_volume_limit = 1000000
self.price_limit = 20
# Set requested data resolution
self.UniverseSettings.Resolution = Resolution.Hour
self.SetUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction))
self.AddEquity("SPY", Resolution.Minute)
# Exit position
self.Schedule.On(
self.DateRules.EveryDay("SPY"),
self.TimeRules.At(9, 30),
self.ExitPosition
)
# Enter position
self.Schedule.On(
self.DateRules.EveryDay("SPY"),
self.TimeRules.At(15, 00),
self.EnterPosition
)
self.my_universe = []
self.dollar_volume_by_symbol = {}
def CoarseSelectionFunction(self, coarse):
# Exclude ETF and Price filter
filtered = [c for c in coarse if c.HasFundamentalData and c.Volume > 0 and c.Price > 1 and c.Price < self.price_limit]
self.dollar_volume_by_symbol = {c: 0 for c in filtered}
self.my_universe = filtered
return self.my_universe
def OnData(self, data):
for symbol in self.dollar_volume_by_symbol.keys():
if symbol in data.Bars:
value = data.Bars[symbol]
self.dollar_volume_by_symbol[symbol] += (value.Volume * value.Price)
def EnterPosition(self):
# dollar volume threshold
temp = []
for s in self.my_universe:
if s in self.dollar_volume_by_symbol.keys():
if self.dollar_volume_by_symbol[s] > self.dollar_volume_limit:
temp.append(s)
self.dollar_volume_by_symbol.clear()
self.my_universe = temp
########################
# Buy some number of stocks from filtered self.my_universe
########################
def ExitPosition(self):
self.Liquidate()
<Second Way>
Within EnterPosition function, use History for every my_universe equities, and use dataframe to calculate dollar volume.
----------------------------------------------- Code is similar to the First one-------------------------------------------
First way is a bit faster on backtest than the second one, but they are both extremely slow.
Is there a way to increase backtest speed for such strategies that require intra-day cumulative data to order, but you can't filter out many securities within universe selection?
Thanks!
Taeyoung Kong
import pandas as pd class MyAlgo(QCAlgorithm): def Initialize(self): # Set start date and end date self.SetStartDate(2020, 1, 1) # Set strategy cash self.SetCash(10000) # Set benchmark self.benchmark = Symbol.Create('SPY', SecurityType.Equity, Market.USA) # Parameters self.dollar_volume_limit = 1000000 self.price_limit = 20 self.UniverseSettings.Resolution = Resolution.Hour self.SetUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction)) self.AddEquity("SPY", Resolution.Minute) # Exit position self.Schedule.On( self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 30), self.ExitPosition ) # Enter position self.Schedule.On( self.DateRules.EveryDay("SPY"), self.TimeRules.At(15, 00), self.EnterPosition ) self.my_universe = [] self.dollar_volume_by_symbol = {} def CoarseSelectionFunction(self, coarse): # Exclude ETF and Price filter filtered = [c for c in coarse if c.HasFundamentalData and c.Volume > 0 and c.Price > 1 and c.Price < self.price_limit] self.dollar_volume_by_symbol = {c: 0 for c in filtered} self.my_universe = filtered return self.my_universe def OnData(self, data): for symbol in self.dollar_volume_by_symbol.keys(): if symbol in data.Bars: value = data.Bars[symbol] self.dollar_volume_by_symbol[symbol] += (value.Volume * value.Price) def EnterPosition(self): # dollar volume threshold temp = [] for s in self.my_universe: if s in self.dollar_volume_by_symbol.keys(): if self.dollar_volume_by_symbol[s] > self.dollar_volume_limit: temp.append(s) self.dollar_volume_by_symbol.clear() self.my_universe = temp ######################## # Buy some number of stocks from filtered self.my_universe ######################## def ExitPosition(self): self.Liquidate()
For some reason, all indentations are messed up and I cannot edit it.
Here is a code.
Derek Melchin
Hi Taeyoung,
The first method is our recommended approach.
To speed up the backtest, we can:
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Taeyoung Kong
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!