Introduction to Portfolio Returns

by Jonathan Regenstein

Continuing our previous work on returns, let’s combine our assets into one portfolio and calculate portfolio returns.

By way of quick reminder, our ultimate goal is to build and analyze the returns of a 5-asset portfolio consisting of the following.

+ SPY (S&P500 fund) weighted 25%
+ EFA (a non-US equities fund) weighted 25%
+ IJS (a small-cap value fund) weighted 20%
+ EEM (an emerging-mkts fund) weighted 20%
+ AGG (a bond fund) weighted 10%

A brief interlude on portfolios: a portfolio is a weighted collection of assets (its etymology harkens back to the Latin for “to carry sheets of paper”, which I suppose made its way to the world of investments because securities used to be sheets of paper). The theoretical reason that rational investors prefer a portfolio to a single asset is that a portfolio can offer a better risk/return trade-off due to low or negative covariance amongst portfolio components.

Back to the task at hand: transform a collection of daily ETF prices into an object of portfolio log returns.

Let’s load up our packages.

library(tidyverse)
library(tidyquant)
library(timetk)

To get our objects into the global environment, the next code chunk is copy/paste from the previous post: we will create one xts object and one tibble, in long/tidy format, of monthly log returns.

# The symbols vector holds our tickers. 
symbols <- c("SPY","EFA", "IJS", "EEM","AGG")

# The prices object will hold our raw price data throughout this book.
prices <- 
  prices <- 
  getSymbols(symbols, 
             src = 'yahoo', 
             from = "2012-12-31",
             to = "2017-12-31",
             auto.assign = TRUE, 
             warnings = FALSE) %>% 
  map(~Ad(get(.))) %>% 
  reduce(merge) %>%
  `colnames<-`(symbols)

# XTS
prices_monthly <- to.monthly(prices, indexAt = "lastof", OHLC = FALSE)
asset_returns_xts <- na.omit(Return.calculate(prices_monthly, method = "log"))

# Tidyverse method, to long, tidy format
asset_returns_long <- 
  prices %>% 
  to.monthly(indexAt = "lastof", OHLC = FALSE) %>% 
  tk_tbl(preserve_index = TRUE, rename_index = "date") %>%
  gather(asset, returns, -date) %>% 
  group_by(asset) %>%  
  mutate(returns = (log(returns) - log(lag(returns))))

Have a peek at both asset return objects.

head(asset_returns_xts)
                   SPY         EFA          IJS          EEM           AGG
2013-01-31  0.04992311  0.03660641  0.052133484 -0.002935494 -0.0062309021
2013-02-28  0.01267821 -0.01296938  0.016175381 -0.023105250  0.0058910464
2013-03-31  0.03726766  0.01296938  0.040257940 -0.010235048  0.0009849727
2013-04-30  0.01903006  0.04896773  0.001222544  0.012085043  0.0096390038
2013-05-31  0.02333571 -0.03065563  0.041976371 -0.049483592 -0.0202136957
2013-06-30 -0.01343432 -0.02715331 -0.001402974 -0.054739116 -0.0157787232
head(asset_returns_long)
# A tibble: 6 x 3
# Groups:   asset [1]
  date       asset returns
  <date>     <chr>   <dbl>
1 2012-12-31 SPY   NA     
2 2013-01-31 SPY    0.0499
3 2013-02-28 SPY    0.0127
4 2013-03-31 SPY    0.0373
5 2013-04-30 SPY    0.0190
6 2013-05-31 SPY    0.0233

Now on to constructing a portfolio and calculating returns. To turn these five ETFs into a portfolio we need to assign them weights. Let’s first create a weights vector.

w <- c(0.25, 0.25, 0.20, 0.20, 0.10)

Before we use the weights in our calculations, we will run a quick sanity check in the next code chunk. This might not be necessary with five assets as we have today, but it is good practice because if we had 50 assets, it could save us a lot of grief to catch a mistake early.

# Make sure the weights line up with assets.
asset_weights_sanity_check <- tibble(w, symbols)
asset_weights_sanity_check
# A tibble: 5 x 2
      w symbols
  <dbl> <chr>  
1  0.25 SPY    
2  0.25 EFA    
3  0.2  IJS    
4  0.2  EEM    
5  0.1  AGG    

Make sure that tibble match up with the portfolio we want to create.

Finally, make sure the weights sum to 100%, or 1. Again, we can eyeball this with five assets, but with 50 assets it would be easier to run the sanity check.

sum(asset_weights_sanity_check$w)
[1] 1

They sum to 1. Good to go, and on to portfolio returns.

We will start with the textbook equation for the return of a multi-asset portfolio which is: \[Return_{portfolio} = W_{1}*Return_{asset1}~+~W_{2}*Return_{asset2}~+~W_{3}*Return_{asset3}~+~W_{4}*Return_{asset4}~+~W_{5}*Return_{asset5}\] Here’s the LaTeX code for that equation.

# $$Return_{portfolio} = W_{1}*Return_{asset1}~+~W_{2}*Return_{asset2}~+~W_{3}*Return_{asset3}~+~W_{4}*Return_{asset4}~+~W_{5}*Return_{asset5}$$

Now let’s grind through the R calculation by hand instead of using built-in functions.

First, assign each weight from our w vector to a variable. Next, assign each asset return stored in asset_returns_xts to a variable. Last, we insert those new variables into the equation.

w_1 <- w[1]
w_2 <- w[2]
w_3 <- w[3]
w_4 <- w[4]
w_5 <- w[5]


asset1 <- asset_returns_xts[,1]
asset2 <- asset_returns_xts[,2]
asset3 <- asset_returns_xts[,3]
asset4 <- asset_returns_xts[,4]
asset5 <- asset_returns_xts[,5]

portfolio_returns_byhand <-   
  (w_1 * asset1) + 
  (w_2 * asset2) + 
  (w_3 * asset3) +
  (w_4 * asset4) + 
  (w_5 * asset5)

names(portfolio_returns_byhand) <- "returns"

Our first portfolio returns calculation is now complete and stored as portfolio_returns_byhand. From a substantive perspective, we are finished and could head to visualization.

We want to cover more methods, though, so let’s head to to the xts world and the PerformanceAnalytics package. We didn’t explicitly load that package in the setup, because tidyquant imports it for us.

We will use the Return.portfolio function, which requires two arguments: an xts object of asset returns and a vector of weights. We have those at hand with asset_returns_xts and w. It’s not necessary, but we will set rebalance_on = "months" so we can confirm it matches our by-hand calculations. Remember, in the by-hand equation, the portfolio weights are fixed, meaning they never change on a month-to-month basis. That is equivalent to re-balancing every month, which in practice would be quite rare.

portfolio_returns_xts_rebalanced_monthly <- 
  Return.portfolio(asset_returns_xts, weights = w, rebalance_on = "months") %>%
  `colnames<-`("returns")

We can take a peek at our two portfolio objects and see how the annual re-balance made a small but important difference.

head(portfolio_returns_byhand)
                 returns
2013-01-31  0.0308488899
2013-02-28 -0.0008696624
2013-03-31  0.0186623366
2013-04-30  0.0206248670
2013-05-31 -0.0053527951
2013-06-30 -0.0229531963
head(portfolio_returns_xts_rebalanced_monthly)
                 returns
2013-01-31  0.0308488899
2013-02-28 -0.0008696624
2013-03-31  0.0186623366
2013-04-30  0.0206248670
2013-05-31 -0.0053527951
2013-06-30 -0.0229531963

We could stop here and have accomplished our substantive task (twice already - by hand and using the built-in function from PerformanceAnalytics), but we want to explore alternate methods in the world of tidyverse/tidyquant. We will use our long, tidy-formatted asset_returns_long and convert to portfolio returns using the tq_portfolio function from tidyquant.

The tq_portfolio function takes a tibble and then asks for an assets column to group by, a returns column to find return data, and a weights column. It’s a wrapper for Return.portfolio, and thus also accepts the argument rebalance_on = "months". Since we are re-balancing by months, we should again get a portfolio returns object that matches our two existing objects portfolio_returns_byhand and portfolio_returns_xts_rebalanced_monthly.

portfolio_returns_tq_rebalanced_monthly <- 
  asset_returns_long %>%
  tq_portfolio(assets_col  = asset, 
               returns_col = returns,
               weights     = w,
               col_rename  = "returns",
               rebalance_on = "months")

Let’s take a quick look and compare how a tidy tibble of portfolio returns compares to an xts object of portfolio returns.

head(portfolio_returns_xts_rebalanced_monthly)
                 returns
2013-01-31  0.0308488899
2013-02-28 -0.0008696624
2013-03-31  0.0186623366
2013-04-30  0.0206248670
2013-05-31 -0.0053527951
2013-06-30 -0.0229531963
head(portfolio_returns_tq_rebalanced_monthly)
# A tibble: 6 x 2
  date         returns
  <date>         <dbl>
1 2012-12-31  0       
2 2013-01-31  0.0308  
3 2013-02-28 -0.000870
4 2013-03-31  0.0187  
5 2013-04-30  0.0206  
6 2013-05-31 -0.00535 

Again, we can see a discrepancy for January of 2005. Our xts object elides that date completely, while our tibble records it as a 0.00.

Since there is only one column of returns, there is no wide versus long format for the tibble, and it looks almost identical to the xts object. The only difference is the date: the tibble has a column that holds the date that can be accessed with the $ operator, whereas the xts object has a date index, accessed with index.

That’s all for today. The xts and tidyquant object have their own uses and advantages depending on our end goal. Next time we will think about how to visualize portfolio returns, and how the different objects fit into different visualization paradigms.

Share Comments · · ·