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.
Taylor Robertson
Hi Logan - I'm no expert coder, but from what I can see, it looks like you're selling as soon as the equity moves below its 52-week high. If that's the case, you would be well served to put a little buffer under your buy price before liquidating (I think your current stop is effectively equal to your buy price). A 52-week high system is a momentum system. Any momentum system requires some amount of breathing room or else you're assuming the stock will go straight up, which is unrealistic. Also, I'd consider using scheduled events to reduce your trade frequency as 52-week high strategies aren't usually intended for intraday trading.
Missing position sizing.
Rahul Chowdhury
Hi Logan,
Let's use a Maximum indicator, which returns the highest value over a lookback period, to keep track of the 52-week high. We can define a Maximum indicator which has a period of 252 days, the approximate number trading days in a year. We can also use a SymbolData class to organize the data for our symbols. We can keep all the relevant data for each symbol, like any indicators or rolling windows, in that class.
In our universe selection, let's take a look at the top 500 most liquid symbols for 52-week breakthroughs. We can use a history request to warm up our maximum indicators for our symbols, so that the indicators are immediately ready to use. Then we can pick out the symbols which have surpassed their previous high and choose those symbols to be in our universe. From here you can decide how you wish to act on those chosen securities. In this example, we go long on the symbols which are chosen and liquidate symbols which haven't been rechosen.
Best
Rahul
Logan Bell
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!