Back to Community
Asset Class Rotation Strategy for Retirement Accounts

Quantopian works great when linked to an IRA or regular brokerage account. But what about my 401k, where most of my investable assets are? I want to implement an "algorithm" to deploy with that money.

Momentum is real phenomenon in the market backed by academic research. What if we look at the momentum of 5 uncorrelated asset classes to determine where we should rebalance our portfolio into? I chose the following 5 asset classes and used the following mutual fund ticker to look at historical performance going back to 1997.

  • Intermediate Treasuries FGOVX
  • S&P 500 VFINX
  • Mid-Cap Value TRMCX
  • International Developed Markets Small Cap VINEX
  • Emerging Markets VEIEX

To select the asset class with the best momentum indicator I calculate the ratio of its 20 day moving average to its 120 day moving average. The asset class with the highest ratio we invest into. Rebalance/check every week. This link provides a plot of the returns: https://engineeredportfolio.files.wordpress.com/2017/02/asset-class-rotation-strategy.jpg

In the attached backtest I built the same strategy in Quantopian using ETFs, specifically:

  • Intermediate Treasuries IEF
  • S&P 500 IVV
  • Mid-Cap Value IJJ
  • International Developed Markets Small Cap SCZ
  • Emerging Markets EEM

I came up with these 5 asset classes after doing backtesting on 40 years of historical data. The data can be found on the bogleheads forum.

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a10bac98a6065defa8d7b0
There was a runtime error.
19 responses

Here's a notebook which you can use to calculate the asset class to allocate your money into. I also included a tear sheet of the above back test.

@Quantopian, it would be nice if we could set up email alerts. I know this has been brought up before but I wanted to reiterate the request.

Loading notebook preview...
Notebook previews are currently unavailable.

This is a great idea, but the performance was great until September 2014. September 2014 to date has underperformed just holding SPY.

Good point Tyler,

This strategy is definitely for folks that can exhibit some patience and not be too concerned with yearly returns. In retirement accounts you can afford to do this where you can't even access the money for 20+ years. But it is a good point, and humans (myself included) can have a hard time sticking with a strategy when it is under performing.

Part of the reason for its under-performance in that time period is how hard hit emerging markets was in 2015 (lost 15% vs a 1% gain for the S&P 500). Emerging markets has big boom years but... it has also has big bust years. If you remove emerging markets as an option in this rotation strategy, the overall returns go down, but its much more consistent. I actually have to do this anyways in my 401k because the emerging markets fund has a 90 day redemption period. I've attached the returns of this version.

Here are the returns going back to 1997: https://engineeredportfolio.files.wordpress.com/2017/02/asset-class-rotation-strategy-no-emerging-markets1.jpg

One other point is that this type of strategy is probably a good idea for some percentage of your retirement account, not the whole thing. I for example have allocated 50% to this, the rest is in a buy-and-hold strategy.

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a1c1f5f66e315e043698c4
There was a runtime error.

Finally I'm posting a version for folks that don't have a wide range of asset class options, namely those in a Thrift Savings Plan. The options in this one include:

  • Domestic Large Cap
  • Domestic Small Cap
  • International Developed Market Equities
  • US Aggregate Bond
  • Short Term US Treasuries

This under performs the S&P 500; but it offers a much smoother ride if you're into that sort of thing. You can play around too with removing some of the options (only doing small cap, international, and aggregate bonds for example).

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a1bde1347e3f5e5b135e36
There was a runtime error.

Here's a version that rotates between short duration, intermediate, and long term treasury bonds based on momentum between the three. I weight this bond class 80%, then static allocation of 15% to the S&P 500, 5% to EAFE. It rebalances monthly, pretty simple stuff!

This has been a nice steady eddy version with a beta of 0 and would be something to consider for folks concerned with risk.

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a4740232607761a30a527a
There was a runtime error.

Now here's a more traditional sector rotation strategy paired with the treasury rotation.

For the sectors, it looks at the ratio of the 20 day to the 240 day moving average for all sectors. Then it even weights (12%) the top 5 sectors for a total equity allocation of 60%.

For the treasuries it looks at the 20 to the 60 day ratio and allocates 40% to that bond class.

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a5d861323f415e040d3c6e
There was a runtime error.

Finally, we'll check the median 20:240 SMA of our sectors, and if it's less than 1 we're going to go full bonds. Otherwise the performance will be the same as above with the 60:40 stocks:bonds.

Clone Algorithm
71
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58a5dabf323f415e040d3cde
There was a runtime error.

Stephen, very nice work. Something useful for the majority of folks out there stuck in a 401K with limited asset options.

I also find this same 'top down' approach very helpful in algorithm design for use outside of a 401K. Get the 'feel' of the interactions between the various asset classes using this approach. Get the volatility and drawdown under control through weighting and timing. Then simply add leverage to increase returns. Then start replacing some of the equity ETFs with baskets of actual stocks which mimic the ETFs but hopefully outperform them.

Keep it up!

Did you hard-code the 5 stock basket into the algorithm? I tried changing some of the parameters around, to either make it a basket of 7 or 3 stocks, and the results drove me over leverage. Where is this affected in the algorithm?

Delman,

Good question, and I was admittedly a bit lazy on those last two versions I posted. I had hard coded in a 0.12 weighting to each sector assuming there would be 5. Here I added a line (line 51) in the initialize function to set the allocation to the "safe" asset class. Then the algorithm even weights the equity_assets that are in the top 50th percentile in terms of ratio of short term SMA to long term. And this should be done regardless of the number of assets you are using.

Clone Algorithm
84
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b0e07f4426585e2f16f750
There was a runtime error.

With the new line added, my understanding is that if you change line 90

context.equity_ratio = np.percentile(ratios, 50)  

to
context.equity_ratio = np.percentile(ratios, 30) it will take the top 30% instead, regardless of how many ETFS are in your basket. Is this correct?

It will actually do the inverse, it will take the top 70% regardless of how many ETFs are in there.

In this version I count how many sector ETFs have a "bearish" trend (defined by their 20:240 day SMA ratio being below 1) to dynamically adjust the bond allocation between 10% and 60%.

I also removed a couple of the sectors (materials, and industrials) because they have never been the best sector for a 3 year period over the last 40 years, indicating that they don't go on sustainable runs of outperformance that we need to be a part of. The analysis and dataset is available on my blog: historical performance of US sectors. And with the removal of these sectors, we only need to hold the top 3 sectors.

Clone Algorithm
84
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b1a7cb6162a75e25e8d802
There was a runtime error.

I wanted to circle back to the original point of this post: a rotation strategy you can use in your retirement account. I had tried to implement the strategy but... my 401k plan started flagging me for "excessive" trading. So I went back to the drawing board and tried to find a version that works with only quarterly rebalancing. To determine our funds I'm looking at the ratio of the 20 day simple moving average to the 60 day moving average. The top ratio gets a 50% allocation, the second gets a 30% allocation, and the 3rd best ratio gets a 20% allocation. Rebalance every quarter.

Clone Algorithm
84
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b6239d41b5805e3cb60811
There was a runtime error.

Here's a version that can be used for folks in a thrift savings plan (limited/basic options).

Clone Algorithm
84
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b62136c0e33661b9f6d3fe
There was a runtime error.

Stephen,

Make sure that quarterly rebalancing results heavily depend from date you start it.
Metrics of following backtest which I started 2 month earlier than you not as bright as in original.

Clone Algorithm
0
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b667ff9a773d5e345724cd
There was a runtime error.

Thanks Vladimir,

It's a good point; maybe I got a bit lucky with how long term treasuries were moving out-of-phase with equities perfectly at quarterly starts/ends.

Here's a cleaner version which looks at the 20:200 day moving average to smooth the timing element. I also dynamically adjust the bond allocation based on sector ETFs bullish/bearish trends (the more sectors with 20 day above their respective 200 day SMA = the less bonds we own). This seems to be less dependent on start date.

Clone Algorithm
84
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 58b6f7b9148a8b5e34f459c4
There was a runtime error.

Any new progress or developments/ideas for this algorithm?

2017 has been pretty productive for this type of asset class momentum strategy - the year started out with international equities having higher momentum signals and they have continued to outperform throughout the year. My 401k that used a similar strategy to the original post is up 15% YTD. But... I've grown weary of the strategy for a couple reasons:

  • I'm nervous that it relies too much on treasuries moving out of phase with equities and on treasury upside performance during market downturns. I think the fed may have made treasuries look like better investments over the past 20 years than what we can expect over the next 20 years.
  • I've learned from live trading a couple strategies in a Robinhood account that a 14 year plot/backtest doesn't adequately describe what the investor has to feel on a day-to-day, even month-to-month basis. These momentum only indicator based strategies can be highly volatile and difficult to stick with.
  • I had spent a lot of work on dynamic asset allocation and wanted to adapt that for a 401k strategy.

So in this attached backtest I only use the 5 asset classes in a thrift savings plan, virtually any/every 401k should have these:

  • S&P500
  • EAFE stocks
  • US small cap stocks
  • Total US bond
  • Short term treasuries.

It dynamically solves for the most efficient allocation between the three stock assets, and it counts the number of bearish sectors to determine how much bonds to hold. The code currently allocates 20% bonds per bearish sector but you can obviously play with that. Within bonds it picks the asset class that has the highest 20:60 day momentum. Rebalances monthly like you can do in a 401k.

Clone Algorithm
13
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 5975f98377afc74e1b0ff864
There was a runtime error.

Here's what I am actually running in my 401k that has access to some of my favorite asset classes:

  • Mid-Cap value
  • Consumer Staples
  • Small Cap ex-US
  • Long Term Treasuries
  • TIPS

It's the same general logic though.

Clone Algorithm
13
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 59727038e2493e52086da26f
There was a runtime error.