I have recently gotten into trading and, though I went through the BootCamp and read through the documentation but I still feel like I have no idea what I'm doing. I guess I just don't have a great grasp of what all of the objects in LEAN are, how to use them properly, and how they all relate. Plus I am a self-taught programmer with little practical experience. In any case, I tried to implement an algorithm which selects equities from the US Market and trades them as follows:
Each day, invest equally in all equities which have reached or surpassed their 52-week high. Every minute for the remainder of the day, check for any other securities which have reached or surpassed the 52-week high to buy them as well. Also, liquidate any of these investments which have dropped below the 52-week high, as well as any investments which have reached above x% of the 52-week high (to secure profits). I know this last part can be done with sell stops/limits but I couldn't figure out how to implement this with everything else I have going on.
It wasn't working out too well (I didn't even finish any backtests because even for just a 1-month backtest, I was already realizing like -25% halfway through) and I'm wondering if maybe I didn't implement it properly. Moreover, the backtests were taking forever and kept giving me errors in the console that I didn't have enough margin. Here's the code if anyone has suggestions.
class VerticalOptimizedThrustAssembly(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 10, 12) # Set Start Date
self.SetEndDate(2019, 11, 19)
self.SetCash(100000) # Set Strategy Cash
self.numCoarse = 500
self.percentToTop = 0.0 # Percent within 52-week high to trigger a buy
self.percentToSecure = 0.05 # Percent above 52-week high to sell to secure profit
self.symbolHighs = {} # Universe security symbols : their 52-week highs
self.investments = {} # Currently held investments : the price at entry
self.toLiquidate = False
self.UniverseSettings.Resolution = Resolution.Minute
self.SetUniverseSelection(CoarseFundamentalUniverseSelectionModel(self.CoarseSelectionFunction))
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
# Liquidate any held securities removed from the universe
if self.toLiquidate:
for security in self.changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
self.toLiquidate = False
total = 0 # Total number of securities comprising new portfolio
toInvest = []
for symbol in self.symbolHighs.keys():
if not data.ContainsKey(symbol):
continue
if data[symbol] is None:
continue
if data[symbol].Price >= (1-self.percentToTop)*self.symbolHighs[symbol]:
if symbol in self.investments.keys():
if self.investments[symbol] == 0: # Make sure we haven't sold the position today already
continue
elif data[symbol].Price >= (1+self.percentToSecure)*self.investments[symbol]: # Secure profit
self.Liquidate(symbol)
self.investments[symbol] = 0
continue
total += 1
toInvest.append(symbol)
elif self.Portfolio[symbol].Invested:
self.Liquidate(symbol)
if total > 0:
weight = 1/total
for symbol in toInvest:
self.investments[symbol] = data[symbol].Price
self.SetHoldings(symbol, weight)
def CoarseSelectionFunction(self, coarse):
self.investments = {}
positiveUsa = [x for x in coarse if x.Price * x.Volume > 0
and x.Market == Market.USA]
securities = sorted(positiveUsa, key=lambda x: x.DollarVolume)[:500]
symbols = [c.Symbol for c in securities]
historical = self.History(symbols, 365, Resolution.Daily)
highs = historical.high.unstack(level=0)
for c in symbols:
self.symbolHighs[c] = max(highs[c])
return symbols
def OnSecuritiesChanged(self, changes):
self.changes = changes
if len(changes.RemovedSecurities) > 0:
self.toLiquidate = True
for security in changes.RemovedSecurities:
if security.Symbol in self.symbolHighs.keys():
del self.symbolHighs[security.Symbol]
Note: I did initially try to use rolling windows to get the 52-week high, but it seemed like I was making more Historical Data requests that way than the way it is currently set up, and the code was a lot messier since I had to delete the stored rolling windows of the securities which were removed from the universe, and make sure I was updating the rolling windows with the previous day's High, etc.