Intro
Superior algo returns can be thought of as being the result of two components: a great strategy regarding ‘what stocks to buy’ (the stock selection component, SEL) and a ‘clever timing’ (the in & out component, I/O) regarding when we are ‘in’ the market and hold the stocks versus when we are ‘out’ of the market and hold alternative assets such as bonds. We often focus on optimizing SEL and tend to neglect I/O; thus, for an important discussion of recent I/O tactics, see here.
Focus of this thread: Optimal SEL + I/O combinations
It is worthwhile to separately optimize SEL and I/O. However, the ultimate total return will also be determined by a certain synergy or dissonance between the two components. So, it seems that we won’t get around the arduous task of individually testing (all possible) combinations to identify optimal SEL + I/O pairs, which is the eventual focus of this thread. I reckon a preparatory step can be to dig up all the hidden SEL and I/O treasures from this forum and beyond to see what inputs are available for the combinations.
Ultimate objective
Let's get rich together, why not?
Aalap Sharma
Just FYI… The trade got triggered today on my live algo today!
Peter Guenther
Definitely an interesting addition, Santa24. Thanks for sharing. It uses the percentile approach / extreme observation logic, which links in nicely with the existing algo. In line with what Jack Pizza has noted, it does remind of a ‘yield curve inversion’-tracker. So, in addition, it might be used to decide regarding in vs out. The equity market definitely tends to strongly react to when the yield curve inverts.
Also thanks for the live trading update, Aalap Sharma. Fingers crossed. I use one additional indicator—based on whether the FED recently has increased the interest rate–which I never got integrated into the algo version here on QuantConnect. Still out for a few days but should also be in soon. Looking at the QQQ and SPY charts, we might see a bit of a further decline in the next few days, hopefully not too steep.
Aalap Sharma
I have the IN_OUT_V8_FIXED version. No idea how many more revisions have had happened since. These are the last 3 trades I have.
+2022-01-26 11:30:00 TMF Buy Market
Fill: $24.74 USD
+2022-08-17 11:30:00 TMF Sell Market
Fill: $12.27 USD
+2022-08-17 11:30:00 TQQQ Buy Market
Fill: $37.03 USD
Jack Pizza
Aalap Sharma no idea what version you're using but thats brutal -50% on TMF alone, I would use one of the last versions posted and add BIL as on option instead of leveraged bonds… until rates stabilize.
Latest version hasn't entered a trade since January it went into cash in January and that's it still in cash.
Aalap Sharma
Is this the one you are talking about Jack Pizza ?
Peter Guenther
Maybe some of you have come across this as well: Billionaire investor Jeremy Grantham on super-bubbles. At least two of the criteria, if not all of them, could be modelled quantitatively. This might enrich the In & Out algo and/or be used to decide which assets to hold. Definitely, the In & Out follows a similar purpose, which is to catch early signals of the “Wild Rumpus” before it actually happens.
Peter Guenther
… or in the light of Jack Pizza‘s and Aalap Sharma’s points: to decide when to accelerate using leveraged ETFs such as TQQQ or decelerate using unleveraged ETFs such as QQQ.
Xeno core
Hi Peter Guenther,
This is great work. Thank you for sharing this. I am working on added a simple dynamic universe for the SEL. However, the algo also buys the list of Equities that are used for the signals based ETF's. I assume this is because this list is within the init method. How can I keep this list in the init method but not have the algo buy these equities ?
Xeno core
Or perhpas its due to the OnData method I have added?
Peter Guenther
Thanks for joining in, Patrick Kocsis, and looking forward to your modifications / ideas.
Yes, it must be the new bits that you have added. The original algo does not trade the signals. For example, see the version above that switches between TQQQ and TMF. It only trades these two ETFs and does not trade any of the signal ETFs.
Or, see the link below as an example in which we use the Coarse and Fine Universe filters to trade a certain stock selection (Qual-Up = Quality Companies in an Uptrend). Again, none of the signal ETFs are traded in the algo.
Hope this helps.
Xeno core
Hi Peter, Thank you for the reply. I will take a look at the link, thank you. I am just feeling my way around at the moment. I have adapted the simple SEL from one of Louis's videos. My goal is to understand how to integrate this SEL into your In/Out strategy. It seems that with his code, the signals in the init method get added into the portfolio at the beginning. This also happens when I just cut and paste these into the init method of Louis's code. When i set these signals =None in the init method, they are not added into the portfolio. Can you tell me why this is ? I will attach the code I have worked out below.
Peter Guenther
I think that you are on the right track, Patrick Kocsis. My suspicion: The problem occurs in the OnSecuritiesChanged function and especially in the use of changes.AddedSecurities to determine self.activeStocks which then feed into self.portfolioTargets. As a result of the code, all the active (subscribed) securities are added to the portfolio, including the signal ETFs, i.e. the behavior that you have observed. A solution is to determine self.portfolioTargets in the FineFilter function. You basically want to hold stocks according to the logic: [x.Symbol for x in sortedByPE if x.MarketCap > 0][:5]. Therefore, build directly on this list to determine self.portfolioTargets instead of using changes.AddedSecurities in the OnSecuritiesChanged function.
If you want to see a concrete example, you could follow the link that I have posted above and look for the use of self.symbols in the code, which we use to save the stocks that we want to hold according to the Qual-Up stock selection logic. Note that self.symbols does not yet specify the weights, i.e. you would need to add the line from you code in which self.symbols (or in your case: self.activeStocks) are used to specify self.portfolioTargets.
Xeno core
Hi Peter,
Thank you for the reply and the solution. I am looking at the link you added. Its all very great work and I hope to add to it once i get oriented. Have a great day.
Xeno core
Hello all,
I am just running backtests on these algos. They all look great. However, I get errors for many of these algorithms.
For example, Zicai Feng's backtest clone, Peters QualUP IN/OUT and Peters VRockets IN/OUT clones. Is this something I am doing or are others getting errors with these algorithms ?
Xeno core
Hello all,
I am just running backtests on these algos. They all look great. However, I get errors for many of these algorithms.
For example, Zicai Feng's backtest clone, Peters QualUP IN/OUT and Peters VRockets IN/OUT clones. Is this something I am doing or are others getting errors with these algorithms ?
Peter Guenther
Thanks for sharing these issues, Patrick Kocsis. You can try the following modifications, hopefully these will solve the issues:
1. add the following line (… history.empty …) in line 198, directly after the function begins, as shown below
def rebalance_when_out_of_the_market(self):
if self.history.empty: return
2. Remove certain double brackets (around self.DEBT) in line 216 like so:
extreme_b.loc[self.DEBT] = np.where((extreme_b.loc[self.DEBT].any()) & (abovemedian[[self.METL, self.NRES]].any()), False, extreme_b.loc[self.DEBT])
Xeno core
Hi Peter,
Thank you I will give it a try.
Here is the error I get in your: clone of: qualup_inout_dbear_v2
Runtime Error: 'QualUp_inout' object has no attribute 'history_shift'
at signalcheck_inout
returns_sample = (self.history / self.history_shift - 1)
in main.py: line 238
at rebalance_when_out_of_the_market
if self.go_inout_vs_dbear==1: out_signal = self.signalcheck_inout()
at Python.Runtime.PythonException.ThrowLastAsClrException()
at Python.Runtime.PyObject.Invoke(PyObject[] args)
at QuantConnect.Scheduling.ScheduleManager.<>c__DisplayClass15_0.<On>b__0(String name in main.py: line 300 (Open Stack Trace)
Xeno core
Hi Peter,
Here is the error i get in your: clone of: vrockets_inout_dbear_v2
Runtime Error: 'ValuationRockets_inout' object has no attribute 'history_shift'
at signalcheck_inout
returns_sample = (self.history / self.history_shift - 1)
in main.py: line 218
at rebalance_when_out_of_the_market
if self.go_inout_vs_dbear==1: out_signal = self.signalcheck_inout()
at Python.Runtime.PythonException.ThrowLastAsClrException()
at Python.Runtime.PyObject.Invoke(PyObject[] args)
at QuantConnect.Scheduling.ScheduleManager.<>c__DisplayClass15_0.<On>b__0(String name in main.py: line 280 (Open Stack Trace)
Xeno core
Hi Peter, I have added your solution to the code and here is the error i get for your: clone of: qualup_inout_v5
float division by zero at rebalance weight = 0.99/len(symbols) in main.py: line 263 at rebalance_when_out_of_the_market self.rebalance() at Python.Runtime.PythonException.ThrowLastAsClrException() at Python.Runtime.PyObject.Invoke(PyObject[] args) at QuantConnect.Scheduling.ScheduleManager.<>c__DisplayClass15_0.<On>b__0(String name in main.py: line 244
your solution:
def rebalance_when_out_of_the_market(self):
if self.history.empty:
return
# Returns sample to detect extreme observations
returns_sample = (self.history / self.history_shift - 1)
# Reverse code USDX: sort largest changes to bottom
returns_sample[self.USDX] = returns_sample[self.USDX] * (-1)
# For pairs, take returns differential, reverse coded
returns_sample['G_S'] = -(returns_sample[self.GOLD] - returns_sample[self.SLVA])
returns_sample['U_I'] = -(returns_sample[self.UTIL] - returns_sample[self.INDU])
returns_sample['C_A'] = -(returns_sample[self.SHCU] - returns_sample[self.RICU])
# Extreme observations; statist. significance = 1%
pctl_b = np.nanpercentile(returns_sample, 1, axis=0)
extreme_b = returns_sample.iloc[-1] < pctl_b
# Re-assess/disambiguate double-edged signals
median = np.nanmedian(returns_sample, axis=0)
abovemedian = returns_sample.iloc[-1] > median
### Interest rate expectations (cost of debt) may increase because the economic outlook improves (showing in rising input prices) = actually not a negative signal
extreme_b.loc[self.DEBT] = np.where((extreme_b.loc[self.DEBT].any()) & (abovemedian[[self.METL, self.NRES]].any()), False, extreme_b.loc[self.DEBT])
### GOLD/SLVA differential may increase due to inflation expectations which actually suggest an economic improvement = actually not a negative signal
Peter Guenther
Thanks for sharing, Patrick Kocsis. The first two posts, where it says that the attribute history_shift is missing, try the following:
In the Initialize section of the code, there is a comment-heading “Warm up history”. Make sure that the following line is included as the final line:
self.update_history_shift()
This line should follow the line which I reckon is already there:
self.history = self.history['close'].unstack(level=0).dropna()
Regarding the error reported in your final post: I could not replicate the error, so it seems to be one of the non-systematic ones. Not sure whether you changed any of the code, but it seems that symbols is empty and, therefore, its length is zero, resulting in a division by zero. You could prevent the error by adding the line:
if len(symbols)==0: return
before the existing line:
weight = 0.99/len(symbols)
But this might not address the underlying issue when you have changed the code so that symbols always does not contain elements. Then your equity curve will basically be a flat line since nothing will be traded. If you should observe this scenario then you'll need to double-check any of the additional changes that you might have undertaken, especially where these affect self.symbols.
Peter Guenther
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!