QuantConnect: Ray Dalio’s All-Weather Portfolio

The strategy in this post will attempt to recreate a version of the “all-weather portfolio” as recommended by Ray Dalio in Tony Robbin’s book, Money Master the Game. At its heart, it is a simple buy and hold strategy that incorporates re-balancing on a regular basis. The magic sauce is then provided in the form of asset selection and their relative portfolio weight.

QuantConnect Topics

Before we dive deeper into the strategy it is worth pointing out that this post will cover a few principles and topics that users might find useful to port to other algorithms. As such, even if you are not interested in the all-weather portfolio, you should still be able to pick up a tip or two regarding:

  1. Scheduling Events: How to re-balance every bi-annually.
  2. Re-investing Dividends: (Though you should really check out this tutorial)
  3. Simulating Saving. See what the returns would look like if you top up your account with savings each time you re-balance.

The All-Weather Portfolio

As mentioned above, the all-weather portfolio is a buy and hold strategy that allocates your assets in such a way that it should get you through different market regimes. The strategy was shared by Ray in the book after Tony asked this question:

If you couldn’t leave any of your financial wealth to your children but only a portfolio, a specific asset allocation with a list of principles to guide that, what would it be?

It is a great question as it yielded not only the all-weather portfolio but also some very interesting principles that make you pause for thought. To regurgitate the complete answer here would do the book a disservice, so instead, we shall just cover the basic principle and asset allocation behind the all-weather portfolio. However, the whole chapter is fascinating and worth a read if you get the chance.

Essentially, Ray explains that there are only really 4 market environments (which he calls seasons) that can move the price of an asset. Additionally, each season is favorable to specific asset types. As such, the idea is to try and maintain 25% of your total risk in each season so that the portfolio can be truly balanced.

Below is a simple image showing the four seasons and which type of assets do well in each.

Allocation

So now we know what types of asset we can consider during each season, the next question is how much do we buy of each? Fortunately, Ray goes on to provide a suggestion for the exact weightings. These are:

  • Equities:
    • 30% in S&P500 or other indexes for diversification
  • Bonds:
    • 15% in 7-10 Year Treasuries
    • 40% in 20-25 Year Treasuries
  • Commodities
    • 7.5% in Gold
    • 7.5% in Other Commodities

Re-balancing:

Finally, he also notes that we should re-balance our portfolio at least annually. Re-balancing simply involves buying more assets and selling others until the quantity of each asset class you are holding hits the target weight. So for example, if stocks go up in value and Bonds dip, your stocks might now make up 50% of your total portfolio value but we only want them to make up 30%. So in this case, you would sell some stocks (take profits) and invest that money to buy more bonds.

Our Portfolio

We will start with an initial balance of 10,000 USD. Further, we will attempt to simulate saving money between each re-balance and investing that sum to see how that has an effect on the overall outcome. After all, that is what the average retail investor is likely to want to do. We build wealth over time.

Regarding asset selection, we will use ETF’s for each of the asset classes. This will help to keep the portfolio as diversified as possible. The only downside to this is that for backtesting purposes, some ETF’s have not been around for many years. As such, depending on which ETF you select, it can make it difficult to backtest through the tough times like the great recession. In our example, we will use the following ETF’s but you may wish to alter them:

  1. Vanguard S&P 500 ETF (15%)
  2. Vanguard FTSE Developed Markets ETF (15%)
  3. iShares 7-10 Year Treasury Bond ETF (15%)
  4. iShares 20+ Year Treasury Bond ETF (40%)
  5. SPDR Gold Trust (7.5%)
  6. United States Oil Fund (7.5%)

Finally, we will look re-balance bi-annually. This is not so frequent that the commissions will kill a small portfolio. Additionally, it will also help us to take a look at how to re-balance on QuantConnect in specific months only.

The Code

import numpy as np

###
# All-Weather Portfolio
# ---------------------------------------------
# Strategy Author: Ray Dalio 
# Source: Tony Robbins / Money, master the game
# ----------------------------------------------
###
class BasicTemplateAlgorithm(QCAlgorithm): '''Basic template algorithm simply initializes the date range and cash''' def Initialize(self): '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.''' self.SetStartDate(2011,1,1) #Set Start Date self.SetEndDate(2019,1,1) #Set End Date self.SetCash(100000) #Set Strategy Cash # Dividend Handling self.raw_handling = True # Simulate topping up your account with savings every period self.savings_on = False self.savings_amt = 1000 # This is to stop us adding savings on the first rebalance as it is # immediately after starting the algo self.first_rebalance = True # This dictionary will be looped through to add equities and setholdings # It can be expanded to hold more ETF's/Equities. self.all_weather = { "Equity 1":{ "Ticker": "VOO", # Vanguard S&P 500 ETF "Weight": 0.15, }, "Equity 2":{ "Ticker": "VEA", # Vanguard FTSE Developed Markets ETF "Weight": 0.15, }, "Bonds Med-Term":{ "Ticker": "IEF", # iShares 7-10 Year Treasury Bond ETF "Weight": 0.15, }, "Bonds Long-Term":{ "Ticker": "TLT", # iShares 20+ Year Treasury Bond ETF "Weight": 0.4, }, "Commodity 1":{ "Ticker": "GLD", # SPDR Gold Trust "Weight": 0.075, }, "Commodity 2":{ "Ticker": "USO", # United States Oil Fund "Weight": 0.075, }, } # Setup IB Broker simulation self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) # Add The ETF'S! # --------------- for key, asset in self.all_weather.items(): self.AddEquity(asset["Ticker"], Resolution.Daily) # Set Dividend Handling Method # ---------------------------- # https://www.quantconnect.com/forum/discussion/508/update-dividends-splits-and-custom-price-normalization/p1 if self.raw_handling: self.Securities[asset["Ticker"]].SetDataNormalizationMode(DataNormalizationMode.Raw) else: self.Securities[asset["Ticker"]].SetDataNormalizationMode(DataNormalizationMode.TotalReturn) # We will assume that if we can place an order for the Equity, then the other # ETF's should be fine. self.Schedule.On(self.DateRules.MonthStart(self.all_weather["Equity 1"]["Ticker"]), self.TimeRules.AfterMarketOpen(self.all_weather["Equity 1"]["Ticker"]), self.Rebalance) def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' # Log any dividends received. # --------------------------- for kvp in data.Dividends: # update this to Dividends dictionary div_ticker = kvp.Key div_distribution = kvp.Value.Distribution div_total_value = div_distribution * self.Portfolio[div_ticker].Quantity self.Log("DIVIDEND >> {0} - ${1} - ${2}".format(div_ticker, div_distribution, div_total_value)) def Rebalance(self): month = self.Time.month # Return if we don't want to rebalance this month # Add extra months in here to rebalance more often # i.e for March insert 3 into the list. if month not in [1,6]: return self.Log('-------------------->>') self.Log("{0} RE-BALANCE >> Total Value {1} | Cash {2}".format( self.Time.strftime('%B').upper(), self.Portfolio.TotalPortfolioValue, self.Portfolio.Cash)) if self.savings_on and not self.first_rebalance: cash_after_savings = self.Portfolio.Cash + self.savings_amt self.Log("Top Up Savings >> New Cash Balance {0}".format( cash_after_savings)) self.Portfolio.SetCash(cash_after_savings) # Rebalance! for key, asset in self.all_weather.items(): holdings = self.Portfolio[asset["Ticker"]].Quantity price = self.Portfolio[asset["Ticker"]].Price self.Log("{0} >> Current Holdings {1} | Current Price {2}".format( self.Portfolio[asset["Ticker"]].Symbol, holdings, price)) self.SetHoldings(asset["Ticker"], asset["Weight"]) self.Log('-------------------->>') # Set first rebalance to False so we add the savings next time around # (if turned on) self.first_rebalance = False

Code Commentary

As this is a strategy post and not a tutorial, the code will not be dissected in great detail. If you are new to QuantConnect and are struggling to follow, please take a look at the getting started series.

Options

Apart from setting the start and end dates, the strategy has the following options available.

  • raw_handling: If set to true, dividends received will be paid in cash to the account. If false, the dividends will not be paid and the price will be adjusted to reflect the dividend. (see the dividends tutorial for more info)
  • savings_on: If set to true, will invest $x every time we re-balance the portfolio.
  • savings_amt: This is a dollar value of how much extra cash (savings) we will add during each rebalance. Note, do not use a dollar sign here.

In addition to these options, we also have the self.all_weatherdictionary. This is the place to add/remove symbols and set their relative weighting. The weighting is a percentage where 1 is 100% and 0 is 0%. You can add more ETF’s or Equities to the backtest by simply adding additional entries to this dictionary. The rest of the code will not need to be updated as long as the entry “Equity 1” is still present. If you remove it, you will need to edit self.Schedule.On(). Otherwise, an error will be thrown. We use the first equity to make sure that the markets are open for trading on our ideal rebalance date. We assume that if the markets are open for the first ETF, the others should be good. Note that if the markets are not open, QuantConnect will intelligently alter the date to ensure we don’t miss a re-balance.

Speaking of which, scheduled events are a major part of this strategy. In this strategy, use a scheduled event to we call the Rebalance()method (function) on the first trading day of each month. It is then within that Rebalance()method that we buy/sell shares. For more information on scheduling see the official docs below:

Official Docs: https://www.quantconnect.com/docs/algorithm-reference/scheduled-events

OnData()

We do very little here. All we do is make a log of any dividends received. This section of code is a copy and paste from the handling dividends tutorial. For more detailed information regarding what this section of code does, take a look at that post.

Rebalance()

As noted, the Rebalance()function is called at the start of every month. It is called monthly because there are no bi-annual or quarterly DateRulesat the time of writing. So in order to only re-balance once every six months, we need to add a little more logic. We just take a look at what the current month number is and if it is not a month that we want to re-balance in then we returnwithout doing anything else.

# Return if we don't want to rebalance this month    
if month not in [1,6]: return

After this check, we optionally update our cash to add some savings into our account. This is achieved by using adding the self.savings_amt to the available cash in the account. Then we just set the new cash balance using self.Portfolio.SetCash(). This method may not be perfect. However, it gets the job done.

Finally, we just loop through our all-weather dictionary setting our holdings to the appropriate weight. This will result in small transactions that add or take away only a few shares to balance the portfolio. Fortunately, this is a painless process. We are able to use QuantConnect’s SetHolding()function which makes allocation a breeze and will handle all the complexity of working out the right sizes for each asset ourselves.

Results

First of all, let’s take a look at the results without adding any savings. Not bad – Final equity coming in at a respectable $14,037.20.

 

With Savings

And here we can what a difference saving some pennies makes! This time our final equity came in at $30,427.14.

 

It is worth noting though that $15,000 of the final equity value was added during the course of the backtest via savings. So that will make the final figure look a lot more impressive than it is. In reality, it resulted in a $5,427.14 gain over the invested sum ($25,000 spread over 7 years with $10,000 upfront). This is compared to $4,037.20 from a $10,000 lump sum in the first test. Overall, this means that the extra $15,000 only increased our gains by 34%. That might seem like a small amount given that we more than doubled our cash during the backtest but we must remember that each $1000 dollar investment had a different amount of time in the market. The later it was added, the less time it had to grow! To add some more context, if you were able to invest the full $25,000 upfront, it would have resulted in about $35,000 equity by the end of the test and of course, without a magic wand we shouldn’t expect to get near that!   

On that note we will leave it there. Hopefully, this post can inspire some people to take a step back from battling with technical indicators and look at some alternative ways to generate returns. That is not to say this is a better approach. No one could make such a claim confidently without a huge amount of research standing at their side. However, the art of asset selection and re-balancing is often overlooked by us retail warriors especially with all the glitz and glamour of the latest hot indicator or the excitement of day trading a single instrument.

Find This Post Useful?

If this post saved you time and effort, please consider donating a coffee to support the site!  


Dontate with PayPal using any payment method you are comfortable with. 


3PUY12Tgp8xynrMCbBdLE56DShzCbxFG8i

0x9c32a2e1e4a06b0995777ac86745c0db1c13bdfc

LUph5xfqvn2bNfthhEcw9QiVLBYeztacFR