Hi,
I finished implementing and backtesting Andreas Clenow strategy with the Algo Framework from Stocks on the Move book. Hope it can be useful for other people from the community.
Quite happy with the results but I am sure it could be optimized.
I would really enjoy some feedback about any aspect to improve with the strategy or the code as the framework is still pretty new for me.
Thanks !
Derek Melchin
Hi Etienne,
Great start! Here's some things to consider:
- We can replace the two history calls in OnSecuritiesChanged with a single one
history = algorithm.History(addedSymbols, max(self.SMAperiod, self.ATRperiod), self.resolution)
- Instead of creating technical indicators in a dynamic universe with the shortcut methods (SMA, ATR), we should use the full class names (SimpleMovingAverage, AverageTrueRange), setup a consolidator to have it updated automatically, and unsubscribe the consolidator when the security is removed from the universe. This pattern is demonstrated in the Momentum in Mutual Fund Returns tutorial.
- The algorithm above makes a several History calls inside the Update method. Replacing these calls with a consolidator increases the efficiency of the backtest.
- The AddEquity method should only be called during the initialization of the algorithm, not inside the alpha model's Update method. We can move the logic to create and warm-up the SMA of the `referenceTicker` to the OnSecuritiesChanged method.
- The process of tracking the `AvailablePorfolioPercent` to select securities is not necessary. The InsightWeightingPortfolioConstructionModel normalizes the weights when their sum exceed 1. See the source code here.
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.
Etienne RAINAUT
Hi Derek,
Thanks a lot for those inputs. I updated the code to minimize the number of history calls. See backtest attached.
I also implemented a condition to remove both history calls for refTicker and ClenowMomentum from the alpha Update().
Is this possible for someone with advanced knowledge of the framework to tell me if this way of coding the algo is the most efficient ?
Feedback about more error handling would be appreciated to learn and eventually go live.
As for the AvailablePorfolioPercent, I prefer to manage in this fashion because the alpha needs to know how much money is left to buy more stocks in the top selection ranking (please see strategy trading rules below, rebalancing point). Then the alpha can send "the whole picture" to the PortfolioConstructionModel.
Thanks
The Strategy Trading Rules from Stocks on the Move by Andreas Clenow
What we’re dealing with here is a long term method of beating the stockmarket. Part of such a strategy is to avoid acting too fast. To reduce both workload and trading frequency, we’ll only check for trade signals once per week. It doesn’t matter if a stock plunges 20% in a day, unless that’s the day we’re supposed to trade we don’t do a thing. Note that this doesn’t mean that we work with weekly data. All calculations are done on daily data. We just don’t trade unless it’s a Wednesday. Why Wednesday of all days? Because Wednesdays happen to have a 20% probability of being the best possible weekday to trade. Yes, it’s absolutely arbitrary. Pick a day. It doesn’t matter.
Rank all stocks in the S&P 500 Index based on momentum. We’ll use annualized exponential regression slope,
calculated on the past 90 days, and then multiply it with the coefficient of determination (R2) for the same period. This gives us a volatility adjusted momentum measurement. Remember that if a stock is trading below its 100 day moving average or has a recent gap in excess of 15%, it’s disqualified.
Calculate position size, using a simple ATR based formula, targeting a daily move of 10 basis points. The formula to calculate number of shares is AccountValue * 0.001 / ATR20.
You’re only allowed to open new positions if the S&P 500 Index is above its 200 day moving average. If it’s below, no new buys are allowed.
Start from the top of your ranking list. If the first stock is not disqualified by being below its 100 day moving average or having a 15%+ gap, then buy it and move to the next. Buy from the top until you run out of cash.
Once a weekwe checkif any stockneeds to be sold. If a stockis no longer in the top 20% of the S&P 500 stocks, based on the ranking, we sell it. If it’s trading below its 100 day moving average, we sell it. If it had a gap over 15%, we sell it. If it left the index, we sell it.
If we have available cash, we look for stocks to buy. If any stock is being sold, there is of course cash available. Buying replacement stocks follow the same logic as above. Only buy if the index is in a positive trend. Buy from the top of the ranking list, if it’s in the top 20%, has positive trend and doesn’t have a large gap. As long as the index is in a positive trend, we just buy new stocks from the top of the list until we run out of cash again.
Twice per month we reset position sizes. As explained earlier, a long term strategy needs to incorporate position size rebalancing to avoid ending up with a completely random risk. Go over each position in your portfolio, compare your current position size with the target size. Calculate target size based on the exact same formula as you used to begin with, but of course with the updated portfolio size and ATR.
If the difference is minor, there’s no need to rebalance just for the sake of it. This procedure is here to make sure position risk doesn’t spin out of control. If there’s any significant deviation, reset position size to target size.
Well, that’s about it. Wait, let’s look at that again.
Ok, you only need to check the markets once a week. I picked Wednesday completely at random, so please don’t send me emails about whether some moon cycle pattern makes Wednesday the best day. Pick whatever day you prefer.
So we only look at the market on Wednesdays. Every week we check first if we should sell any positions. If a position no longer qualifies, it’s sold. Then if we have available cash, and the index is in a positive trend, we buy stocks. Start at the top of the rankings list and buy until you have no more cash.
Every second Wednesday, we have a have an additional task. Compare position target sizes with actual sizes, and rebalance as needed.
Vladimir
Etienne RAINAUT,
When I set self.SetStartDate (2018, 1, 1) in the latest version, I got:
Runtime Error: AttributeError : 'NoneType' object has no attribute 'Symbol'
at Update in ClenowMomentumAlphaModel.py:line 82
:: if data[refSecurity.Symbol].Close > refSecurity.referenceSMA.Current.Value:
AttributeError : 'NoneType' object has no attribute 'Symbol' (Open Stacktrace)
Etienne RAINAUT
Thanks for catching that error Vladimir
I fixed it with more error handling
if data.ContainsKey(self.referenceSymbolData.Symbol): if data[self.referenceSymbolData.Symbol] is not None: if data[self.referenceSymbolData.Symbol].Close > self.referenceSymbolData.referenceSMA.Current.Value:
And also by moving the referenceSymbolData setup away from Update()
Mic Grauer
Hi!
Great algorithm! I cloned the code 7 days ago (2021-05-24) and did a backtest over a longer period - all fine.
Yesterday I tried to run the code again and now I geht an error right at the start:
During the algorithm initialization, the following exception has occurred: TypeError : __init__() takes from 2 to 3 positional arguments but 4 were given at __init__ super().__init__(filterFineData in QC500UniverseSelectionModel.py:line 55 TypeError : __init__() takes from 2 to 3 positional arguments but 4 were given
Even cloning the backtest of 7 days ago doesn't work!
Has something in the framework changed? Does anyone experience the same problem or are you still able to run the code?
best regards
Michael
Martin Molinero
Hi!
This is due to PR, we removed some obsolete code, the SecurityInitializer pass to the universe wasn't being used for the last two years, sorry to hear it affected you.
Solving the issue just requires removing the SecurityInitializer argument being passed, attaching backtest
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.
Mic Grauer
Hi Martin,
thanks a lot!
best regards Michael
Etienne RAINAUT
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!