[R-SIG-Finance] optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

Simon Hovmark @imonhovm@rk @ending from gm@il@com
Thu Oct 18 23:14:00 CEST 2018


I am using the PortfolioAnalytics package to run a multi-period optimization on a quite large stock universe (14 European indicies ~ 800 stocks) but I’ve run into a problem. All examples and vignettes using the package has a fixed stock universe over the backtest period (like EDHEC) while I’ve a dynamic universe. For example, stock XYZ might have been in an index from may, 2002 to October 2007 and after that excluded from the index. However, the optimizer is only allowed to include stock XYZ in the optimization if it was part of an index at the rebalance date.

>From my understanding, it is not possible to set up a custom constraint (like it is with objectives) to say that, stock XYZ should only be included in the optimization rebalancing if the rebalancing date is between may, 2002 to October 2007. Does anybody have an idea how to solve this?

Data
I’ve a dataset (daily data) with a 1 if the stocks were in their respective index at the date and a 0 if they were not (see last sentence). Alternatively, I also have a list like the below with the precise dates:

Name  		Entry  		 Exit
SBMO-NL        2003-03-03     2015-03-20
SMBO-NL        2016-03-21     2018-03-16
TA-NL  		2008-03-26     2008-06-27
TLNL-NL          2000-12-29     2002-03-01
TLNL-NL          2004-03-02     2005-10-10

My returns series have the returns for the stocks for the complete listing period (not dependent on the stock being in an index or not). My current (very very ugly) work-around was to multiply the 1/0 matrix and the return matrix, so I only had returns for the periods when the stocks where in an index, and then replace all 0/NAs with -100. This -100 would penalize stocks outside of an index to such a degree, that it would not be included in the index. However, this is ugly and incorrect plus it does not work.

Code
This is not my exact code but see this as an example of a simple optimization:
 
returns <- read_excel("R", sheet = 1, col_names = TRUE)
returns <- xts(returns[,-1], order.by = as.Date(paste(returns$Dato, format = "%Y.%m.%d")))
returns <- Return.calculate(returns, method = "log")
returns <- returns[-1,] 

fund.names <- colnames(returns)
tranch1 <- portfolio.spec(assets = fund.names)

 # Constraints
tranch1 <- add.constraint(portfolio = tranch1, type = "leverage")
tranch1 <- add.constraint(portfolio = tranch1, type = "long_only")

# Objective
tranch1 <- add.objective(portfolio=tranch1, type="return", name="mean")
tranch1 <- add.objective(portfolio=tranch1, type="risk", name="StdDev")

tranch1_opt <- optimize.portfolio.rebalancing(R=returns, portfolio=tranch1,
                                           optimize_method="DEoptim",
                                           #momentFUN = tranch1_boudt,
                                           #momentargs=ac.moments,
                                           rebalance_on = “year”,
                                           training_period = 26,
                                           rolling_window = 26,
                                           trace=TRUE, traceDE=5, search_size=2000)

Here is some data from my sample (dput):
structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46.276, 46.327, 46.122, 46.924,
46.071, 46.856, 47.351, 45.645, 46.003, 47.436, 48.358, 49.501,
49.364, 49.433, 48.597, 48.204, 47.061, 47.266, 47.436, 49.211,
50.729, 51.514, 51.19, 49.996, 51.156, 51.856, 51.412, 52.385,
55.797, 55.371, 54.176, 53.784, 52.896, 53.511, 54.603, 55.115,
54.654, 52.658, 52.692, 54.603, 54.569, 56.258, 54.944, 54.654,
54.552, 54.603, 53.954, 53.852, 54.262, 54.262, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 123.067, 122.712, 123.422, 127.323, 127.855, 128.032,
124.84, 117.215, 121.826, 120.939, 117.747, 122.358, 119.875,
114.91, 115.619, 113.846, 116.328, 118.634, 119.698, 120.407,
119.166, 118.811, 120.584, 120.584, 120.584, 119.875, 119.166,
120.584, 123.422, 121.294, 119.52, 123.244, 122.89, 125.018,
126.082, 125.55, 126.082, 126.259, 125.55, 126.614, 126.082,
130.692, 132.288, 132.465, 134.061, 131.047, 131.401, 133.884,
134.771, 141.155, 38.773, 39.16, 39.419, 41.067, 40.356, 41.131,
41.099, 39.548, 40.453, 40.13, 39.419, 42.327, 42.392, 43.425,
43.684, 44.976, 44.944, 44.976, 44.136, 44.362, 43.813, 43.878,
43.942, 44.298, 44.912, 45.881, 45.235, 45.558, 47.658, 47.173,
45.59, 46.786, 47.335, 47.561, 47.27, 48.337, 47.82, 46.883,
45.396, 47.432, 47.917, 48.434, 48.66, 48.983, 48.983, 48.627,
49.015, 50.728, 51.697, 51.859), .Dim = c(50L, 5L), .Dimnames = list(
    NULL, c("BAER-CH-B4R2R5", "BALN-CH-7124594", "CFR-CH-BCRWZ1",
    "CIBN-CH-5196744", "CLN-CH-7113990")), index = structure(c(883353600,
883440000, 883526400, 883699200, 883958400, 884131200, 884217600,
884304000, 884563200, 884649600, 884736000, 884822400, 884908800,
885168000, 885254400, 885340800, 885427200, 885513600, 885772800,
885859200, 885945600, 886032000, 886118400, 886377600, 886464000,
886550400, 886636800, 886723200, 886982400, 887068800, 887155200,
887241600, 887328000, 887587200, 887673600, 887760000, 887846400,
887932800, 888192000, 888278400, 888364800, 888451200, 888537600,
888796800, 888883200, 888969600, 889056000, 889142400, 889401600,
889488000), tzone = "UTC", tclass = c("POSIXct", "POSIXt")), class = c("xts",
"zoo"), .indexCLASS = c("POSIXct", "POSIXt"), tclass = c("POSIXct",
"POSIXt"), .indexTZ = "UTC", tzone = "UTC”)    

As an alternative, I have also uploaded a full index here (https://ufile.io/0cxn6 <https://ufile.io/0cxn6>) where the first sheet is the 1/0 matrix, the second the full return matrix and the third is the 1/0 matrix times the full return matrix.
 
	[[alternative HTML version deleted]]



More information about the R-SIG-Finance mailing list