Back

Dual Momentum Sector Rotation

Working version of the Dual Momentum Sector Rotation Strategy, based on Gary Antanacci's book Dual Momentum. If the SPY is trending up, strategy re-balances each month to the top 4 sectors (based on yearly momentum) of the SPY. If the SPY is trending down, the strategy moves 100% to the AGG Bond ETF.

Gary's book doesn't specify how the many sectors he invests in at a given time, or if this number may vary based on other criteria. I have used 4 here for simplicity. Wanted to post this to see what you all think, and maybe some additional rules may be that could improve performance (MichaelH, you linked to an algo that utilized the CAPE metric to identify down moves, that may be something to look into).
Update Backtest








Thanks for sharing @Reuben! I would also recommend looking into volatility, it may have provided an indication of the choppiness in summer 2011 and pointed towards a regime change in fall 2008. My goal would be trying to improve the performance at those two points, in an up-trending market the results are great!! Maybe look for sharp moves upward in the CBOE Volatility Index.
0

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.


I think this is really cool. Just my two cents: I think that you could also add slight efficiency by doing your strong-sector calculation before liquidating your portfolio. This way you can get rid of the possibility of selling and then re-buying the same sector (since the quantity would be the same and you would just lose money on trade cost, minimal, but everything counts). Another option would be to weight the sectors differently based on their strength to try to optimize returns
1

I think this is really cool. Just my two cents: I think that you could also add slight efficiency by doing your strong-sector calculation before liquidating your portfolio. This way you can get rid of the possibility of selling and then re-buying the same sector (since the quantity would be the same and you would just lose money on trade cost, minimal, but everything counts). Another option would be to weight the sectors differently based on their strength to try to optimize returns
0

@Benjamin Staiger, I think both of those things are worth looking at. The liquidating at the end of each month was done just as a quick-and-dirty way to backtest with monthly re-balancing; for execution, would handle differently. There may be some very good ways to weight sectors too; it would be interesting to see if that made a significant difference (FWIW, I found very little difference between selecting, 3,4, or 5 strong sectors).

When I went back and looked at this again, I realized that the strategy wasn't using daily data. The difference in results isn't huge, but it is a little different, and I wondered if someone more familiar with how the consolidators work could explain to me exactly what is happening differently between this backtest (attached) and the one I posted originally above:
0


As an example of where the difference between daily bars vs not is much more significant, note the difference in the number of trades between this strategy and the next one. #1 (Daily Consolidated GEM strategy)
0


vs #2 (Minute Data GEM Strategy). Note that all indicators are still working on the Daily time frame.
0


Hey Reuben!

It looks like we're reusing the same consolidator for many symbols. Using the consolidators directly is a bit of an advanced topic. It's hard to be confident in the order the event handlers will fire when attaching them to multiple symbols. Each consolidator should only be registered to exactly one symbol. It looks like your OnDailyData event will fire once for each symbol per day. This could lead to hard to predict behavior since some if the indicators will have been updated while others have not (depending on the order the consolidated events get handled). Take a look at the dates the algorithm began trading, you'll notice the daily data event handler one is a day behind.

Each time a new indicator is created using the algorithm helper functions (SMA, EMA, MOM, ect..) a brand new consolidator is created.It is then registered to call Update on the indicator when there's new data.

I think it's easiest (and most predictable) to scan for signals using the main OnData(TradeBars data) method like you had posted in your first algorithm.
0

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.


Ok, that makes sense. Is there any way to force buying on open or close using the main OnData(TradeBars data) method? Looking at some other strategies like buying on close-open gap.
0

Hey Reuben! I actually just finished writing MarketOnOpen and MarketOnClose order types this week :). They still need to be reviewed and tested before being pushed public, so maybe by the end of this weekend you'll be able to submit these orders using:// in algorithm
MarketOnOpenOrder("SPY", 10);
MarketOnCloseOrder("SPY", -10);
0

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.


Hey @Reuben, thanks for sharing!

I cloned the algo, and tried to look at the `MOM()` calculation, I wanted to look at the `Momentum` values generated in the Log - but (and maybe this is just a misunderstanding of the Momentum object) but I keep logging `0.0` for each value when I put the `LOG(MomScore.Current.Value)` in the `OnData` function.

After adding the `MOM()` calculation for each symbol in the `Initialize()` method ... it should be calculated correctly when it's dereferenced during each `OnData()` call? Correct?

Looking for documentation on Mom and Momentum now.
0

Whoops sorry! Comment didn't look to have submitted from other view, and now I can't delete...hate these type of message boards!!!
0

Hey Jordan!

Without looking at your code I can't say for sure, but I imagine the indicator just hasn't been updated yet. The OnData function receives data each minute, but the momentum indicator is defined on the daily time frame. I've attached a slight edit to the shared algorithm which will print the momentum value each morning after it is ready (252 trading days worth of data).

All of the LEAN engine is open source, so feel free to inspect the implementations! Here's the momentum indicator source code.
0

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.


@MichaelH, ok great thank you! I have a feeling I was just seeing logging output that wasn't in a long-enough time-frame. But I generally understand now.

I'll have a look at the LEAN source-code when I have a question like this in the future! I'm still playing with how exactly I can develop in Visual Studio, but that's a question for another thread.

Here is what I was trying to do (Setting up the data in the same way as the initialize method in the original project, and just removing other lines):

//Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
public void OnData(TradeBars data)
{
var _momSPY = GEMSymbolData["SPY"].MomScore;
var _momTbill = GEMSymbolData["BIL"].MomScore;
var Bonds = GEMSymbolData["AGG"].Symbol;

decimal holdingPercent = 1m;

var orderedMomScores = SectorSymbolData.OrderByDescending(x => x.MomScore.Current.Value).ToList();
int numberOfSectors = 4;

foreach (var x in orderedMomScores)
{
Log(">>SCORE>>" + x.Symbol + ">>" + x.MomScore);
}

}


I assumed that as the 'onData()' event hit it would display the MomScore() during that period (Daily) if this tick fell on that day...
0

@JordanBaucke - The OnData event is registered with a resolution of minute, so it will fire for each minute during equity trading hours. The momentum indicators are registered to receive daily consolidated data, so they'll get data updates at the beginning of each day for the previous day's data, so the call frequency between OnData on the indicator updates are much different.

Typically what I'll do when I only want to make a decision each day is something similar to what Reuben has:if (lastSampleTime.Date == Time.Date) return;
0

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.


Hi Reuben, I am new here but have  brief comment. I think you just need to add the "dual" aspect. Invest in all sectors that are positive for 1-year & have 1-year return greater than SPY & have a 1-yea return greater than Bond fund.  

To avoid putting too much $ into one sector (like at time of market turmiol or turnign points), only invest 1/3 in each of the top three sectors. If there are only two, 1/3 is in cash. Only one, 2/3 in cash, etc.

0

Reuben & Tripp, I have modified the original code and tried to add in the "dual" aspect. The code now goes through each of the selected "strong sectors", and applies the "downside protection" method (

http://blog.alphaarchitect.com/2015/08/13/avoiding-the-big-drawdown-with-trend-following-investment-strategies/#gs.KjDirXc

) by checking against "TMOM" and "MA" rules. TMOM refers to time-series momentum, whether the asset's momentum is greater than that of the T-Bill. MA refers to moving average, whether the asset is above a certain moving average. If both conditions are met, 25% of the allocation goes to that sector. If only one of the conditions are met, 12.5% of the allocation goes to that sector and 12.5% of the allocation goes to bonds. If both conditions are not met, 25% that originally goes to the sector would be allocated to bonds.  

I am trying to implement a custom momentum indicator, to exclude the most recent month from the calculation. Any suggestions as to how this should be done? I have implemented a "MMomentumPercent" class in the code, but can't seem to get it to work. 


 
1


Update Backtest





0

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.


Loading...

This discussion is closed