optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

SimonHovmark
This post was updated on .
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]]

_______________________________________________
R-SIG-Finance@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
Reply | Threaded
Open this post in threaded view
|

Re: optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

Eric Berger
I see a few problems with your approach
1. Providing incorrect returns data for stocks when they are NOT in the
index won't work because you need the historical co-movements of the stock
with the rest of the investable universe. e.g. for Sharpe-Ratio
(Mean-Variance) optimization, if the stock first entered the index on July
1, you will need accurate returns data for at least the size of the window
used to compute a covariance for July 1 - e.g. if a 12-month window then
for the full year prior to the first entry into the index.
2. If your optimization problem allows negative weights (i.e. short
positions), then clearly using extreme negative returns for a stock will
identify it as a good candidate for inclusion on the short side.

Possible approach:
Major index membership changes relatively infrequently. e.g. the S&P500
membership is reconstituted annually.
If your 1/0 data shows that changes are infrequent you might be able to
simply perform several runs with each run having a "constant" investable
universe for the rebalancing dates for that run. Then stitch together the
results from the different runs.

Another approach:
Contact the authors of the package to see if they have suggestions. You are
raising a standard concern and they may have addressed it or have thought
about how to deal with it.

Regards,
Eric




On Fri, Oct 19, 2018 at 12:14 AM Simon Hovmark <[hidden email]>
wrote:

> 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]]
>
> _______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-sig-finance
> -- Subscriber-posting only. If you want to post, subscribe first.
> -- Also note that this is not the r-help list where general R questions
> should go.
>

        [[alternative HTML version deleted]]

_______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
Reply | Threaded
Open this post in threaded view
|

Re: optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

Ross Bennett
Hi Simon,

A changing universe is not directly supported in PortfolioAnalytics, but it
can absolutely be done. Eric Berger mentioned several of the key concerns
with return histories of different lengths and an evolving universe of
assets. The optimize.portfolio.rebalancing function is effectively a
wrapper around optimize.portfolio. A solution to your problem with a
dynamic universe is to write your own rebalancing loop. At each iteration,
you can define the assets in the portfolio specification and the returns to
use for that iteration.

This has come up before and I would like to add it to PortfolioAnalytics,
but it has not risen to the top of my priority list. Feel free to open a
feature request on https://github.com/braverock/PortfolioAnalytics. Also,
patches always welcome.

Hope that helps.

Best,
Ross

On Fri, Oct 19, 2018 at 4:48 AM Eric Berger <[hidden email]> wrote:

> I see a few problems with your approach
> 1. Providing incorrect returns data for stocks when they are NOT in the
> index won't work because you need the historical co-movements of the stock
> with the rest of the investable universe. e.g. for Sharpe-Ratio
> (Mean-Variance) optimization, if the stock first entered the index on July
> 1, you will need accurate returns data for at least the size of the window
> used to compute a covariance for July 1 - e.g. if a 12-month window then
> for the full year prior to the first entry into the index.
> 2. If your optimization problem allows negative weights (i.e. short
> positions), then clearly using extreme negative returns for a stock will
> identify it as a good candidate for inclusion on the short side.
>
> Possible approach:
> Major index membership changes relatively infrequently. e.g. the S&P500
> membership is reconstituted annually.
> If your 1/0 data shows that changes are infrequent you might be able to
> simply perform several runs with each run having a "constant" investable
> universe for the rebalancing dates for that run. Then stitch together the
> results from the different runs.
>
> Another approach:
> Contact the authors of the package to see if they have suggestions. You are
> raising a standard concern and they may have addressed it or have thought
> about how to deal with it.
>
> Regards,
> Eric
>
>
>
>
> On Fri, Oct 19, 2018 at 12:14 AM Simon Hovmark <[hidden email]>
> wrote:
>
> > 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]]
> >
> > _______________________________________________
> > [hidden email] mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-sig-finance
> > -- Subscriber-posting only. If you want to post, subscribe first.
> > -- Also note that this is not the r-help list where general R questions
> > should go.
> >
>
>         [[alternative HTML version deleted]]
>
> _______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-sig-finance
> -- Subscriber-posting only. If you want to post, subscribe first.
> -- Also note that this is not the r-help list where general R questions
> should go.
>

        [[alternative HTML version deleted]]

_______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
Reply | Threaded
Open this post in threaded view
|

Re: optimize.portfolio.rebalancing with changing/dynamic stock universe [PortfolioAnalytics]

SimonHovmark
Thanks for your answers! I agree that my current solution was.. non-optimal.

The rebalancing loop around optimize.portfolio seems as the way to go. I’m
not that skilled in R yet and this is for a school project (hard deadline),
so I will probably go with Eric’s solution for now about dividing the data
into pieces determined by the rebalancing dates (yearly) and then sort away
does stocks not in an index at that given date.

But the rebalancing loop could be a nice project for learning and if I get
to a decent result, I’ll post it here.

-Simon

> Den 19. okt. 2018 kl. 09.45 skrev Ross Bennett <[hidden email]>:
>
> Hi Simon,
>
> A changing universe is not directly supported in PortfolioAnalytics, but it
> can absolutely be done. Eric Berger mentioned several of the key concerns
> with return histories of different lengths and an evolving universe of
> assets. The optimize.portfolio.rebalancing function is effectively a
> wrapper around optimize.portfolio. A solution to your problem with a
> dynamic universe is to write your own rebalancing loop. At each iteration,
> you can define the assets in the portfolio specification and the returns to
> use for that iteration.
>
> This has come up before and I would like to add it to PortfolioAnalytics,
> but it has not risen to the top of my priority list. Feel free to open a
> feature request on https://github.com/braverock/PortfolioAnalytics <https://github.com/braverock/PortfolioAnalytics>. Also,
> patches always welcome.
>
> Hope that helps.
>
> Best,
> Ross
>
> On Fri, Oct 19, 2018 at 4:48 AM Eric Berger <[hidden email] <mailto:[hidden email]>> wrote:
>
>> I see a few problems with your approach
>> 1. Providing incorrect returns data for stocks when they are NOT in the
>> index won't work because you need the historical co-movements of the stock
>> with the rest of the investable universe. e.g. for Sharpe-Ratio
>> (Mean-Variance) optimization, if the stock first entered the index on July
>> 1, you will need accurate returns data for at least the size of the window
>> used to compute a covariance for July 1 - e.g. if a 12-month window then
>> for the full year prior to the first entry into the index.
>> 2. If your optimization problem allows negative weights (i.e. short
>> positions), then clearly using extreme negative returns for a stock will
>> identify it as a good candidate for inclusion on the short side.
>>
>> Possible approach:
>> Major index membership changes relatively infrequently. e.g. the S&P500
>> membership is reconstituted annually.
>> If your 1/0 data shows that changes are infrequent you might be able to
>> simply perform several runs with each run having a "constant" investable
>> universe for the rebalancing dates for that run. Then stitch together the
>> results from the different runs.
>>
>> Another approach:
>> Contact the authors of the package to see if they have suggestions. You are
>> raising a standard concern and they may have addressed it or have thought
>> about how to deal with it.
>>
>> Regards,
>> Eric
>>
>>
>>
>>
>> On Fri, Oct 19, 2018 at 12:14 AM Simon Hovmark <[hidden email] <mailto:[hidden email]>>
>> wrote:
>>
>>> 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> <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]]
>>>
>>> _______________________________________________
>>> [hidden email] <mailto:[hidden email]> mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-sig-finance <https://stat.ethz.ch/mailman/listinfo/r-sig-finance>
>>> -- Subscriber-posting only. If you want to post, subscribe first.
>>> -- Also note that this is not the r-help list where general R questions
>>> should go.
>>>
>>
>>        [[alternative HTML version deleted]]
>>
>> _______________________________________________
>> [hidden email] <mailto:[hidden email]> mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-sig-finance <https://stat.ethz.ch/mailman/listinfo/r-sig-finance>
>> -- Subscriber-posting only. If you want to post, subscribe first.
>> -- Also note that this is not the r-help list where general R questions
>> should go.
>>
>
> [[alternative HTML version deleted]]
>
> _______________________________________________
> [hidden email] <mailto:[hidden email]> mailing list
> https://stat.ethz.ch/mailman/listinfo/r-sig-finance <https://stat.ethz.ch/mailman/listinfo/r-sig-finance>
> -- Subscriber-posting only. If you want to post, subscribe first.
> -- Also note that this is not the r-help list where general R questions should go.


        [[alternative HTML version deleted]]

_______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.