This trend following algorithm is converted from Naoki Nagai combined with the improvement of Maxim Girbu about applying the weighted moving average to adjust position sizing.
First, choose the major ETFs in the index, fix income and commodity market. The linear regression over the open price of the last half year is used to decide the current trend. The trading signals are triggered by the following rules
1. Set a threshold to the slope of the regression line, trade if the slope exceeds the threshold
2. Uptrend ( slope > 0 and slope > threshold):
- Today's open cross the regression line upward and the equity is not invested - long
- The equity has already been invested(long) and today's open greater than 95% upper Bollinger band - liquidate
3. Downtrend (slope < 0 and slope < - threshold)
- Today's open cross below the regression line and the equity is not invested - short
- The equity has already been invested(short) and today's open lower than 95% lower Bollinger band - liquidate
4. If the equity has already been invested
- long but the slope turns down, liquidate
- short but the slope turns upward, liquidate
5. Trailing stop
- The stoploss percentage is the absolute value of slope over the lookback period, the price is the historical mean over the last three days
Michael Manus
nicely done,....thanks for posting.
Aaron Gilman
Jing,
This is awesome! If you were to modify the algorithm to consume output from CourseSelection/FineSelection as a starting universe, how would you handle the uptake of the Universe symbols instead? I tinkered around with it a bit this morning, but need an extra nudge! Thanks in advance.
Aaron
Rasheduzzaman hridoy
nicely done,....thanks for posting.
Jing Wu
Hi Aaron,
This is my attempt to add the universe selection for symbol picking. The universe selection is rebalanced every month and it picks the symbol based on PE ratio. It's a nice suggestion to improve the algorithm. If you have some great ideas about how to choose the universe, welcome to post it here.
Aaron Gilman
As always, Thanks Jing! I will post a new version if I find anything worthwhile.
DEVON
Hi Jing,
thanks you share the strategy !
what wrong of the following message that when I backtest the universe selection for symbol picking version strategy and backtest start date from 2015
38 | 09:44:30:
Runtime Error: QuantConnect.Scheduling.ScheduledEventException: Python.Runtime.PythonException: ValueError : arrays must all be same length
at QuantConnect.Scheduling.ScheduleManager+<>c__DisplayClass16_0.<On>b__0 (System.String name, System.DateTime time) [0x00000] in <e60e6336223b4153a44c7c1bc57e55f5>:0
at QuantConnect.Scheduling.ScheduledEvent.OnEventFired (System.DateTime triggerTime) [0x00036] in <e60e6336223b4153a44c7c1bc57e55f5>:0
at QuantConnect.Scheduling.ScheduledEvent.OnEventFired (System.DateTime triggerTime) [0x00086] in <e60e6336223b4153a44c7c1bc57e55f5>:0
at QuantConnect.Scheduling.ScheduledEvent.Scan (System.DateTime utcTime) [0x0010f] in <e60e6336223b4153a44c7c1bc57e55f5>:0
at QuantConnect.Lean.Engine.RealTime.BacktestingRealTimeHandler.SetTime (System.DateTime time) [0x00019] in <edb82c23e8dc4c13a9514cc4c313efae>:0
at QuantConnect.Lean.Engine.AlgorithmManager.Run (QuantConnect.Packets.AlgorithmNodePacket job, QuantConnect.Interfaces.IAlgorithm algorithm, QuantConnect.Lean.Engine.DataFeeds.IDataFeed feed, QuantConnect.Lean.Engine.TransactionHandlers.ITransactionHandler transactions, QuantConnect.Lean.Engine.Results.IResultHandler results, QuantConnect.Lean.Engine.RealTime.IRealTimeHandler realtime, QuantConnect.Lean.Engine.Server.ILeanManager leanManager, QuantConnect.Lean.Engine.Alpha.IAlphaHandler alphas, System.Threading.CancellationToken token) [0x007d5] in <edb82c23e8dc4c13a9514cc4c313efae>:0
at QuantConnect.Lean.Engine.Engine+<>c__DisplayClass8_1.<Run>b__3 () [0x000ac] in <edb82c23e8dc4c13a9514cc4c313efae>:0 (Open Stacktrace)
Jing Wu
Hi Devon, this error happens in calc_vol_scalar(self) function when trying to convert the dict into dataframe.
Converting the array value in dict into series will solve the problem.
HanByul P
Hi Jing Wu, Thanks for your work.You normalized the slope as below, Can you explain about this?
# Normalized slope
slope = a / b *252.0
Is your intention to make it 'annualized slope'? (or else?) If you want to get annualized slopes, should'nt it be 'sqrt(252)'? Or am I missing something here? Thanks.
HanByul P
Hi Jing Wu, I got same error as below.
Runtime Error: QuantConnect.Scheduling.ScheduledEventException: In Scheduled Event 'SPY: EveryDay: SPY: 28 min after MarketOpen-6909e3d5b3c747ee9a4e80648b2061fc', TypeError : No method matches given arguments for History ---> Python.Runtime.PythonException: TypeError : No method matches given arguments for Historyat QuantConnect.Scheduling.ScheduleManager+<>c__DisplayClass16_0.<On>b__0 (System.String name, System.DateTime time) [0x00000] in <669a48023294462e9e7fa428d3f38468>:0
at QuantConnect.Scheduling.ScheduledEvent.OnEventFired (System.DateTime triggerTime) [0x00036] in <669a48023294462e9e7fa428d3f38468>:0 (Open Stacktrace)140 | 18:27:32:Algorithm Id:(bfb08429eae765001044b2e6d847e9dc) completed in 13.08 seconds at 0k data points per
HanByul P
Hi Jing Wu, One more quick question. Is there way to get all the slopes of entire symbols that are filtered? You got candidates from filtering 'P/E ratio' in 'FineSelectionFunction' and then you got the slope of each stock by using linear regression. Instead, can we get the series (or in the form of dataframe) of all the slopes of entire stocks that we filetered with the P/E ratio as below?
# Slopes of each stock (I used just dummy numbers, not real slopes.)
AAPL 2.0
MSFT 1.5
AMZN 2.5
XOM 1.5
......
So that we can play with this data (e.g. ranking, etc.). Thank you.
Jing Wu
Hi Hanbyul, the normalized slope here is intended to show the percentage change at each bar. The formula should be
Slope = Change in Price
Normalized Slope = Change in Price / Bar
The bar here is 1/252. Thus Normalized Slope = a/b *252
This normalization would allow for comparison of the Regression Slope of different stocks trading in different price ranges. Thus here we divide the slope by the intercept b.
Jing Wu
QuantTrader, HanByul_p, the error message comes from the history request. The number of days parameter in self.History(symbols, number of days, resolution) must be int. In Python2, int/int will be automatically converted to int but in Python3, the result of int/int is a float.
# instead of self.lookback = 252/2
self.lookback = int(252/2)
The updated algorithm is attached.
HanByul P
Jing Wu, Thanks!
DEVON
How to avoid the backtest issue:
47 | 00:15:21:
Backtest Handled Error: The order quantity for SPY cannot be calculated: Reason: The portfolio does not have enough margin available..
48 | 00:15:21:
Backtest Handled Error: The order quantity for USO cannot be calculated: Reason: The portfolio does not hold enough cash including the order fees..
Tim De Lise
When running the backtest I see a bunch of order warnings. I am pretty sure this means that your algo was trying to enter orders and your portfolio didn't have enough available margin to enter them. So the backtest isn't accurately representing the algorithm.
In a related matter I think there may be some bug with how the Lean engine is calculating margin requirements on orders, especially when going far back in time.
39 | 04:17:37: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 140 | 04:17:38: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 141 | 04:17:40: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 142 | 04:17:41: Backtest Handled Error: The order quantity for USO cannot be calculated: Reason: The portfolio does not hold enough cash including the order fees.. 143 | 04:17:41: Your algorithm messaging has been rate limited to prevent browser flooding. 144 | 04:17:42: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 145 | 04:17:45: Backtest Handled Error: The order quantity for GLD cannot be calculated: Reason: The portfolio does not have enough margin available.. 146 | 04:17:47: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not hold enough cash including the order fees.. 147 | 04:17:47: Your algorithm messaging has been rate limited to prevent browser flooding. 148 | 04:17:48: Backtest Handled Error: The order quantity for RWX cannot be calculated: Reason: The portfolio does not have enough margin available.. 149 | 04:17:50: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 150 | 04:17:50: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 151 | 04:17:52: Backtest Handled Error: The order quantity for SPY cannot be calculated: Reason: The portfolio does not hold enough cash including the order fees.. 152 | 04:17:52: Your algorithm messaging has been rate limited to prevent browser flooding. 153 | 04:17:53: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 154 | 04:17:54: Backtest Handled Error: The order quantity for DIA cannot be calculated: Reason: The portfolio does not have enough margin available.. 155 | 04:17:55: Backtest Handled Error: The order quantity for SPY cannot be calculated: Reason: The portfolio does not have enough margin available.. 156 | 04:18:12: Backtest Handled Error: The order quantity for GLD cannot be calculated: Reason: The portfolio does not hold enough cash including the order fees.
Quant Trader
I believe there is an error in rule 4. although it is not hit if you change sign:
# Long but slope turns down, then exit if symbol.weight > 0 and slope < 0: symbol.weight = 0 # short but slope turns upward, then exit if symbol.weight < 0 and slope > 0: symbol.weight = 0
John Bailey
What would it take to get someone to explain this algorithm in plain language?
NOOB, and can't make any sense of it...
Nicolas Ferrari
Can someone explain how self.weigth is determined in this algo? As is initialized with 0, but I don't see any part in which is assigned a new value for self.weigth.
Thanks
Nicolas
Jing Wu
Hi Nicolas, the weight is assigned in regression() method.
# Trend is up
if slope > slope_min:
# price crosses the regression line
if delta[-1] > 0 and delta[-2] < 0 and symbol.weight == 0:
symbol.stopprice = None
symbol.weight = slope
# Trend is down
if slope < -slope_min:
# price crosses the regression line
if delta[-1] < 0 and delta[-2] > 0 and symbol.weight == 0:
symbol.stopprice = None
symbol.weight = slope
Zach Oakes
This is a really interesting algo -- but I'm having trouble getting it working, even with the int(), and even explicitly looking for the key values prior to running dict assignment (on self.price). I either get a version where it skips everything, or it throws a key error on hyg, or other symbols within regression function.
Any ideas?
Jing Wu
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!