Permanent Portfolio – Simple Tools
I have previously described and back-tested the Permanent Portfolio strategy based on the series of posts at the GestaltU blog. Today I want to show how we can improve the Permanent Portfolio strategy perfromance using following simple tools:
- Volatility targeting
- Risk allocation
- Tactical market filter
First, let’s load the historical prices for the stocks(SPY), gold(GLD), treasuries(TLT), and cash(SHY) and create a quarterly rebalanced Permanent Portfolio strategy using the Systematic Investor Toolbox.
############################################################################### # Load Systematic Investor Toolbox (SIT) # https://systematicinvestor.wordpress.com/systematic-investor-toolbox/ ############################################################################### setInternet2(TRUE) con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb')) source(con) close(con) #***************************************************************** # Load historical data #****************************************************************** load.packages('quantmod') tickers = spl('SPY,TLT,GLD,SHY') data <- new.env() getSymbols(tickers, src = 'yahoo', from = '1980-01-01', env = data, auto.assign = T) for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T) # extend GLD with Gold.PM - London Gold afternoon fixing prices data$GLD = extend.GLD(data$GLD) bt.prep(data, align='remove.na') #***************************************************************** # Setup #****************************************************************** prices = data$prices n = ncol(prices) period.ends = endpoints(prices, 'quarters') period.ends = period.ends[period.ends > 0] period.ends = c(1, period.ends) models = list() #***************************************************************** # Dollar Weighted #****************************************************************** target.allocation = matrix(rep(1/n,n), nrow=1) weight.dollar = ntop(prices, n) data$weight[] = NA data$weight[period.ends,] = weight.dollar[period.ends,] models$dollar = bt.run.share(data, clean.signal=F)
Now let’s create a version of the Permanent Portfolio strategy that targets the 7% annual volatility based on the 21 day look back period.
#***************************************************************** # Dollar Weighted + 7% target volatility #****************************************************************** data$weight[] = NA data$weight[period.ends,] = target.vol.strategy(models$dollar, weight.dollar, 7/100, 21, 100/100)[period.ends,] models$dollar.target7 = bt.run.share(data, clean.signal=F)
Please note that allocating equal dollar amounts to each investment puts more risk allocation to the risky assets. If we want to distribute risk budget equally across all assets we can consider a portfolio based on the equal risk allocation instead of equal capital (dollar) allocation.
#***************************************************************** # Risk Weighted #****************************************************************** ret.log = bt.apply.matrix(prices, ROC, type='continuous') hist.vol = sqrt(252) * bt.apply.matrix(ret.log, runSD, n = 21) weight.risk = weight.dollar / hist.vol weight.risk = weight.risk / rowSums(weight.risk) data$weight[] = NA data$weight[period.ends,] = weight.risk[period.ends,] models$risk = bt.run.share(data, clean.signal=F)
We can also use market filter, for example a 10 month moving average, to control portfolio drawdowns.
#***************************************************************** # Market Filter (tactical): 10 month moving average #****************************************************************** period.ends = endpoints(prices, 'months') period.ends = period.ends[period.ends > 0] period.ends = c(1, period.ends) sma = bt.apply.matrix(prices, SMA, 200) weight.dollar.tactical = weight.dollar * (prices > sma) data$weight[] = NA data$weight[period.ends,] = weight.dollar.tactical[period.ends,] models$dollar.tactical = bt.run.share(data, clean.signal=F)
Finally, let’s combine market filter and volatility targeting:
#***************************************************************** # Tactical + 7% target volatility #****************************************************************** data$weight[] = NA data$weight[period.ends,] = target.vol.strategy(models$dollar.tactical, weight.dollar.tactical, 7/100, 21, 100/100)[period.ends,] models$dollar.tactical.target7 = bt.run.share(data, clean.signal=F) #***************************************************************** # Create Report #****************************************************************** plotbt.custom.report.part1(models) plotbt.strategy.sidebyside(models)
The final portfolio that combines market filter and volatility targeting is a big step up from the original Permanent Portfolio strategy: the returns are a bit down, but draw-downs are cut in half.
To view the complete source code for this example, please have a look at the bt.permanent.portfolio2.test() function in bt.test.r at github.
Nice post. From the results, it seems volatility targeting may be good on paper but not worth the hassle. If one were to add additional slippage and commission costs to volatility targeting approach, I wonder would it still be above dollar.tactical?
On a side note, I tried few times to understand the toolkit code, specifically the weights processing part to try out money management modelling. Not much success. Looking forward to your release of toolkit and documentation.
Once you add transaction cost and slippage the strategy performance will not be as attractive, but at the same time we only re-balance once a month. I might address this point in subsequent posts.
With regards to the weights, I think the easiest is to visualize the transition map, i.e. allocation to each asset through out the time. At each re-balance point you are filling out the rows with allocations in the weights matrix.
Great write up, Michael.
A small but important quibble with the risk parity approach however: if you include SHY in the basket, the portfolio will be massively skewed to cash at all times. You might consider risk weighting between spy, gld and tlt, and allowing SHY to fill the residual exposure once you size the spy/gld/tlt portfolio to the target volatility. The current RP framework severely penalizes RP.
Otherwise, love your work as always.
Thank you, Adam.
Great point, I might address this it in subsequent posts.