This thread is meant to continue the development of the In & Out strategy started on Quantopian. The first challenge for us will probalbly be to translate our ideas to QC code.
I'll start by attaching the version Bob Bob kindly translated on Vladimir's request.
Vladimir:
About your key error, did you also initialize UUP like this?
self.UUP = self.AddEquity('UUP', res).Symbol
Vladimir
Goldie Yalamanchi,
The main difference in the luc prieur code is that he applies the momentum filter first and the fundamental filter second.
To solve the leverage problem you should calculate stocks and bond weights once per bar in the same place.
But let's talk about that in the "Amazing returns = superior stock selection strategy + superior in & out strategy".
Peter Guenther
Very much enjoying the discussion here; we are now officially at 200+ posts (plus the 100+ from Quantopian). A big thank you again to QuantConnect for hosting this discussion and allowing us to backtest strategies/ideas, plus the helpful comments and suggestions from the team (Jared Broad, Derek Melchin, Shile Wen)! To the thread's discussion participants: amazing work, keep it up! Very nice work on multiple in & out algos and looking beyond (stock selection, alternative assets selection, out-of-sample testing - possibly all warranting individual threads at some stage). Very interesting ideas, all!
Thunder Chicken
Hello,
Would anyone be able to in some detail tell me what these 5 snippets are trying to accomplish?
Any help would be great!
1. def rebalance_when_out_of_the_market(self):
# Returns sample to detect extreme observations
hist = self.History(
self.SIGNALS + [self.MRKT] + self.FORPAIRS, 252, Resolution.Daily)['close'].unstack(level=0).dropna()
hist_shift = hist.apply(lambda x: (x.shift(65) + x.shift(64) + x.shift(63) + x.shift(62) + x.shift(
61) + x.shift(60) + x.shift(59) + x.shift(58) + x.shift(57) + x.shift(56) + x.shift(55)) / 11)
returns_sample = (hist / hist_shift - 1)
2. # Reverse code USDX: sort largest changes to bottom
returns_sample[self.USDX] = returns_sample[self.USDX] * (-1)
3. # 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])
self.pairlist = ['G_S', 'U_I', 'C_A']
4. # Extreme observations; statist. significance = 1%
pctl_b = np.nanpercentile(returns_sample, 1, axis=0)
extreme_b = returns_sample.iloc[-1] < pctl_b
5. # Determine waitdays empirically via safe haven excess returns, 50% decay
self.WDadjvar = int(
max(0.50 * self.WDadjvar,
self.INI_WAIT_DAYS * max(1,
np.where((returns_sample[self.GOLD].iloc[-1]>0) & (returns_sample[self.SLVA].iloc[-1]<0) & (returns_sample[self.SLVA].iloc[-2]>0), self.INI_WAIT_DAYS, 1),
np.where((returns_sample[self.UTIL].iloc[-1]>0) & (returns_sample[self.INDU].iloc[-1]<0) & (returns_sample[self.INDU].iloc[-2]>0), self.INI_WAIT_DAYS, 1),
np.where((returns_sample[self.SHCU].iloc[-1]>0) & (returns_sample[self.RICU].iloc[-1]<0) & (returns_sample[self.RICU].iloc[-2]>0), self.INI_WAIT_DAYS, 1)
))
)
adjwaitdays = min(60, self.WDadjvar)
Vladimir
Peter Guenther,
I performed out sample testing of 4 "In-Out" algorithms.
All of them using 12 input sources, complex methods to calculate momentum and outdays.
Exit signal generated as union of 8 individual signals.
if (extreme_b[self.SIGNALS + self.pairlist]).any():
In_out Tentor Testivis October 2020,
In_out_flex_v4 2020-11
In & Out ‒ parsimonious trades edition 2020-11
In & Out ‒ parsimonious trades edition Update (2nd) 2020-11
Was everything as intended in November?
What do you think?
@Vladimir Very interesting, but I should note, that to be able to draw statistically significant conclusions, you need many years of out-of-sample data. This is why you should preferably optimize the strategy on about 67% of the available data, and then validate it on the remaining 33%, which in this case would imply the period 2016-2020. Even in that case the data available and the market conditions therein pretty limited. In any case the current algorithms have been optimized up till this year. This and the above reasoning is why a cross-validation method is the only reasonable method to perform out-of-sample testing in this case. The downside of course is, that you need some objective parameter optimization algorithm to prevent bias in the selection of the parameters.
Peter Guenther
Vladimir: Thanks for running these tests and sharing the results!
"Was everything as intended?" -- It may sound funny but I would almost say "partly yes". I think the algo does capture "jitter" in the market, see some of the recent business headlines (surging COVID cases, new lockdown measures in multiple countries, concerning US employment numbers, questions re whether the S&P500 surged too high too quickly, questions re tech overpricing etc.). However, did the algo outperform the market? There the answer is clearly "no"; SPY and QQQ made about 10% in Nov. The algo was completely in bonds for most of the month, with an initial uptick at the beginning but, overall, a zero return (see Nathan Swenson's recent insights in Amazing returns = superior stock selection strategy + superior in & out strategy).
Goldie Yalamanchi was crunching the signals (see recent post in Amazing returns = superior stock selection strategy + superior in & out strategy) and found that the SHY signal does not add much in terms of total returns over the backtesting period from 1/1/2008. Yet, the SHY signal was the main reason for that the algo was out in Nov. Based on Goldie's finding, attached is the algo without the SHY signal and with a daily (instead of weekly) 'in' reschuffling (some people may be running the algo with this setting, i.e. removing the 'lazy equity trader approach). I still have to test this setting over the full backtesting period. For Nov, the attached backtest suggests that this setting does a better job (i.e. setting = remove SHY signal in lines 41 and 51, schedule 'in' reschuffling to daily instead of weekly in lines 71-72).
Vladimir
Peter Guenther,
Here is my opinion sent to you 6 weeks ago.
And now you're using 8 factors.
Think about it.
Vladimir
Peter Guenther,
Every strategy goes in and out, but this strategy goes in and out according to the waiting day rule.
Asking "Was everything as intended?" I mean, the strategy must wait at least 15 days after the exit signal before entering.
if self.dcount >= self.outday + adjwaitdays:
self.be_in = True
But instead it made 10 trades in November.
Vladimir
Peter Guenther,
Here are some "Rules of the algo" from your post on Quantopian forum
Are they still active in current version of the algorithm?
Goldie Yalamanchi
Vladimir,
I will wait for Peter Guenther to answer. But from my debugging the "adjwaitdays" isn't always 15 days. I have seen it be 20 days or 24 days, etc.
If we wanted to always be 15 days... then this below calculation isn't needed...
self.WDadjvar = int( max(0.50 * self.WDadjvar, self.INI_WAIT_DAYS * max(1, np.where((returns_sample[self.GOLD].iloc[-1]>0) & (returns_sample[self.SLVA].iloc[-1]<0) & (returns_sample[self.SLVA].iloc[-2]>0), self.INI_WAIT_DAYS, 1), np.where((returns_sample[self.UTIL].iloc[-1]>0) & (returns_sample[self.INDU].iloc[-1]<0) & (returns_sample[self.INDU].iloc[-2]>0), self.INI_WAIT_DAYS, 1), np.where((returns_sample[self.SHCU].iloc[-1]>0) & (returns_sample[self.RICU].iloc[-1]<0) & (returns_sample[self.RICU].iloc[-2]>0), self.INI_WAIT_DAYS, 1) )) ) adjwaitdays = min(60, self.WDadjvar)
Instead I guess we would just hard code self.WDadjvar = 15, no? The above self.WDadjvar is based on how long the decay 50% value of the indicators is.
Jack Pizza
Just had the algo fail with an error, probably trying to enter a position:
Runtime Error: LiveTradingRealTimeHandler.Run(): There was an error in a scheduled event EveryDay: QQQ: 120 min after MarketOpen. The error was KeyError : 'close' Stack Trace: System.Exception: LiveTradingRealTimeHandler.Run(): There was an error in a scheduled event EveryDay: QQQ: 120 min after MarketOpen. The error was KeyError : 'close'
T Smith
Hi folks... moved over from quantopian.
Here is my input to this algo;
- IN-OUT signal unchanged
- IN and OUT assets determined by momentum (IN: QQQ/IWF, OUT: TLT/IEF)
Nathan Swenson
Wow T.Smith! Those are some awesome results and you did that with non-leveraged instruments. Very Nice!
Nathan Swenson
T.Smith, if you check the logs, there are some invalid orders due to lack of margin. I see the order quantity is just slightly too high and the order gets rejected.
Goldie Yalamanchi
Yes I noticed the same thing... I think QQQ needs to be sold first before IWF (Russell) is bought.
2020-09-11 11:31:00 TLT Sell Market Fill: $164.259732735 USD -1048 Filled
2020-09-11 11:31:00 QQQ Buy Market Fill: $273.40 USD 632 Filled
2020-09-25 11:31:00 IWF Buy Market 810 Invalid
2020-10-02 11:31:00 IWF Buy Market 809 Invalid
2020-11-06 11:30:00 QQQ Sell Market Fill: $291.99 USD -632 Filled
That small detail aside, it seems like an interesting iteration that still continues to use the SHY signal but has properly entered the market again in late half 2020 without issues. Something the last iteration of the algo has suffered from.
Goldie Yalamanchi
I may stand corrected, after applying the fix to sell QQQ before IWF buy... the algo proceeds along. But doesn't make a trade after 10/6 again. Just stuck in bonds (IEF). Take a look below, maybe I made a mistake... but here is my backtest....
Jack Pizza
Goldie Yalamanchi are you saying there is an issue with the algo? I don't understand, or you want it to be more active in trades? I would prefer it to be in bonds, if you've noticed QQQ hasn't done much and looks like it's getting ready to roll over.
Nathan Swenson
Yes, I agree. Prefer bonds right now, although entry was too early with 10/6. The maximum out time is 60 days if I'm reading code correctly, which would be 3 months.
Goldie Yalamanchi
So you can review the discussion around SHY. But it fires the signal over and over again due to SHY moving %1 or so. Bond yields are near 0%. TLT and IEF do well when the fed rate is low. But they can't go lower than 0% can they? Hence I believe the SHY signal to be a FED created signal. I guess they can go to negative % which has happened in some countries.
Here is a log of the signals.... if any are true it exits the IN state and switches to bonds. In 2020 this happens....
2020-10-06 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-07 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-08 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-09 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-12 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-20 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-21 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-22 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-23 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-10-26 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-03 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-04 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-06 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-10 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-11 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-12 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-13 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-11-19 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
2020-12-02 11:30:00 : symbol XLI False DBB False IGE False SHY True UUP False G_S False U_I False C_A False
We have in other variations of this algo, removed SHY as a signal. Again it is debateable because since Nov QQQ has had a nice move but the algo sat on the sidelines. I have removed SHY from the algo and it performs very nicely still. Here is your algo minus the SHY signal....
Jack Pizza
Goldie Yalamanchi the removal of SHY seems to have icreased DD a lot, think the original was around 16-18%, now you're closer to 23%
Tentor Testivis
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!