1

I know there are good resources for calculating stock and portfolio returns using performance analytics in tidyquant for R. For example, assume we want to determine the annual portfolio returns (2011 through 2015) for a portfolio that contains "XOM" (0.5), "MA" (0.3), and "GOOG" (0.2), where () indicates the asset weight within the portfolio. The code would simply be:

Ra_symbols <- c("XOM", "MA", "GOOG")

wts_map <- tibble(
  symbols = c("XOM", "MA", "GOOG"),
  weights = c(0.5, 0.3, 0.2)
  )

Ra_2010to2020 <- Ra_symbols %>% 
  tq_get(get  = "stock.prices",
         from = "2010-12-31",
         to   = "2015-12-31") %>%
  group_by(symbol) %>%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = "yearly",
               col_rename = "Ra")

Ra_weightedportfolioreturn <- Ra_2010to2020 %>% 
  tq_portfolio(assets_col  = symbol, 
               returns_col = Ra, 
               weights     = wts_map, 
               col_rename  = "Ra_using_wts_map")

But I cannot find any resource to help with the code if the portfolio contains different assets in each year with varying weights. For example, assume we have two dataframes: one with the symbols for each year and one with the respective portfolio weights. Below is the code to read in the csv file with the symbols and weights, along with images to illustrate the dataframes.

symbols2011to2015 <- read_csv("Symboltest_2011to2015.csv")
weights2011to2015 <- read_csv("wtstest_2011to2015.csv")

Dataframe with symbols by year

Dataframe with symbol weights by year

I'm thinking it's going to involve some tidyverse functions like "apply" or "map", but am not sure. Also, can tidyquant work with symbols (and weights) in a dataframe as I have it, or do I need to convert them to character values similar to the results from c("XOM", "MA", "GOOG")? Eventually, I'd like to expand this concept for monthly periods, but once I figure it out for multiple years the process should be similar.

Any help would be much appreciated!

Jcarl
  • 161
  • 2
  • 9
  • 1
    If an alternative package is acceptable, too, then maybe this helps: https://quant.stackexchange.com/questions/60505/backtesting-a-portfolio-strategy-with-several-assets/60529#60529 – Enrico Schumann Jan 19 '21 at 08:06
  • I've been playing around with the PMwR package and I believe that it will work. But am having troubles getting my dataframes with symbols/adjusted price, weights with dates, and dates for rebalancing in the proper format. It appears from your documentation "Computing Returns" that the data has to be as a zoo time series. Is that correct? Currently my list of symbols and adjusted price are in a wide dataframe format, which keeps giving me errors. Also, are there any examples of the "returns" function you mentioned that utilize portfolio weights and rebalance.when? – Jcarl Jan 22 '21 at 03:50

1 Answers1

2

(This is a follow up to the comment.)

There is documentation in the PMwR manual.

The input to returns does not have to be a zoo series: matrices or data-frames will work as well, as long as the prices are arranged in columns. However, working with zoo is quite convenient. To take your example:

library("zoo")
library("tidyquant")

P <- tq_get(c("XOM", "MA", "GOOG"),
            get  = "stock.prices",
            from = "2010-12-31",
            to   = "2015-12-31")

P <- do.call(merge, by(P, P$symbol, function(x) zoo(x$adjusted, x$date)))
##                GOOG       MA      XOM
## 2010-12-31 295.8760 21.09419 50.33540
## 2011-01-03 301.0466 20.78734 51.31982
## 2011-01-04 299.9358 21.05560 51.56077
## 2011-01-05 303.3978 21.67588 51.42308
## 2011-01-06 305.6045 21.76818 51.75351
## 2011-01-07 307.0690 21.76536 52.03575

Now suppose you have a matrix Wof weights

W <- rbind(c(1/3, 1/3, 1/3),
           c(0.6, 0.2, 0.2),
           c(1/3, 1/3, 1/3),
           c(0.6, 0.2, 0.2),
           c(1/3, 1/3, 1/3))

and dates when to rebalance:

when <- as.Date(c("2010-12-31", "2011-12-30", "2012-12-31",
                  "2013-12-31", "2014-12-31"))

Each element in when corresponds to a row in W. Now we can compute portfolio returns.

library("PMwR")
R <- returns(P, rebalance.when = when, weights = W)
##  2011-01-03   2011-01-04   2011-01-05   2011-01-06  ....
## 0.007495421  0.004549025  0.012637607  0.005984054  ....

## ## If 'P' had not been a zoo matrix:
## returns(coredata(P), t = index(P), rebalance.when = when, weights = W)
## ## would work as well

returns expects as input a price series. We can easily create such a series, based on R.

tmp <- cumprod(1 + returns(P, rebalance.when = when, weights = W, pad = 0))

This series we can transform into holding-period returns.

returns(tmp, period = "month")
##       Jan Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec  YTD
## 2011  5.7 3.5 -0.4  2.7 -0.9 -0.1  4.5 -1.7 -3.5 10.4  4.8  3.4 31.4
## 2012 -7.3 8.4  2.2 -1.7 -6.3  3.0  5.7  4.2  8.4 -5.4  2.3  0.5 13.2
## 2013  5.5 2.1  1.4  1.7  3.8  0.6  3.6 -3.8  4.7  9.7  4.6  7.9 49.7
## 2014 -0.4 3.3 -5.7 -2.6  4.3  1.0 -0.6  0.6 -0.9  1.0 -2.1 -1.6 -4.0
## 2015 -2.8 5.5 -3.3  1.8 -0.1 -1.0  6.8 -3.4 -1.8 13.0  1.3  0.7 16.6

returns(tmp, period = "year")
## 2011 2012 2013 2014 2015 
## 31.4 13.2 49.7 -4.0 16.6 
Enrico Schumann
  • 1,278
  • 7
  • 8
  • This all makes sense and I've gotten it to work great! Thank you! One last question, I think. Is it possible to handle different assets in the portfolio when you rebalance? Consider the images I included in my original post where the symbols and weights vary from one rebalance period to the next. Can that situation be handled with the "returns" function? I've been trying with various simple examples and can't seem to get it to work. If so, will the "when" matrix be similar to symbols2011to2015 and will "weights" by similar to weights2011to2015? Then does the "P" matrix need to one matrix? – Jcarl Jan 24 '21 at 19:53
  • 1
    The function cannot work directly with the data structure you show. `P` needs to contain all the prices, and `weights` needs to have as many columns as `P`, but some weights can be zero, of course. If you do not want to merge your data, you would need a loop around `returns`. – Enrico Schumann Jan 25 '21 at 21:25
  • Enrico, a related but different question. I'm considering the function in your PMwR package "rebalance" for calculating the exact trades to make to rebalance an investment portfolio. I think this function is exactly what I'm looking for. But I'm curious if it'll work if the number of components (i.e. # of symbols) of the existing portfolio does not match the target portfolio? For example, will it work if I have 10 symbols (each with # of shares and prices) in the existing portfolio, and I want to rebalance to 15 symbols (each with % allocations)? Thanks! – Jcarl Feb 19 '21 at 22:03
  • 1
    It should work as well, and would be easiest with named portfolio vectors: `price <- c(a = 10, b = 20); current <- c(a = 5); target <- c(a = 0.2, b = 0.8); rebalance(current = current, target = target, price = price, match.names = TRUE)` – Enrico Schumann Feb 20 '21 at 17:13