Skip to content
All library documents

Changing Portfolio Rebalancing Intervals with Endpoint Spacing

Article Quant Q&A · Author: FerdinandC

Summary

The document addresses how to use PortfolioAnalytics in R to run portfolio optimization on intervals such as semiannual or nine-month schedules. The question describes attempts to pass a spacing parameter through the rebalance setting, which continued to produce quarterly endpoints. The proposed workaround is to copy and modify the rebalancing function so its calls to the endpoint generator include a spacing argument, then specify monthly endpoints with an interval of six for semiannual rebalancing.

This illustrates that the schedule is controlled by the endpoint-generation logic inside the wrapper, rather than by an extra argument passed through the original interface. The example supplies an adapted function and a monthly rebalance call. It does not demonstrate a nine-month configuration, discuss package-version differences, or validate the modified function, so users should verify endpoint dates and optimization windows for their own data and installed package version.

Key ideas

  • The package wrapper may determine rebalancing dates through its internal endpoint calls.
  • A custom wrapper can pass an interval spacing value to monthly endpoint generation.
  • A six-month interval is presented as the method for semiannual rebalancing.
  • The document does not show the corresponding setting for a nine-month interval.
  • Custom function copies should be checked against the package version and resulting dates.

Tags

Full text
# PortfolioAnalytics [R] - optimize.portfolio.rebalancing / rebalancing period


# PortfolioAnalytics [R] - optimize.portfolio.rebalancing / rebalancing period












I am having difficulties trying to set up the rebalancing period to semi-annual or every 9 months in the `optimize.portfolio.rebalancing` function in the package PortfolioAnalytics (R). Is it possible to add the factor $k=1,2$ (endpoints) in the rebalancing function?

I tried `optimize.portfolio.rebalancing(...rebalance_on=list("quarters",k=2)...)` and `optimize.portfolio.rebalancing(... ,rebalance_on="quarters", k=2, ...)`. However, I did not obtain semi-annual rebalancing periods and I only got the standard quarterly rebalancing period.

## Answer by Rime (score 1)

https://quant.stackexchange.com/a/50145

Just add the `k=6` to the `endpoints()` wrapper and use that function instead...

```
optimize.portfolio.rebalancing2 = function (R, portfolio = NULL, constraints = NULL, objectives = NULL, 
          optimize_method = c("DEoptim", "random", "ROI"), search_size = 20000, 
          trace = FALSE, ..., rp = NULL, rebalance_on = NULL, training_period = NULL, 
          rolling_window = NULL) 
{
  stopifnot("package:foreach" %in% search() || requireNamespace("foreach", 
                                                                quietly = TRUE))
  stopifnot("package:iterators" %in% search() || requireNamespace("iterators", 
                                                                  quietly = TRUE))
  if (inherits(portfolio, "portfolio.list")) {
    n.portf <- length(portfolio)
    opt.list <- vector("list", n.portf)
    for (i in 1:length(opt.list)) {
      if (hasArg(message)) 
        message = match.call(expand.dots = TRUE)$message
      else message = FALSE
      if (message) 
        cat("Starting optimization of portfolio ", i, 
            "\n")
      opt.list[[i]] <- optimize.portfolio.rebalancing(R = R, 
                                                      portfolio = portfolio[[i]], constraints = constraints, 
                                                      objectives = objectives, optimize_method = optimize_method, 
                                                      search_size = search_size, trace = trace, ... = ..., 
                                                      rp = rp, rebalance_on = rebalance_on, training_period = training_period, 
                                                      rolling_window = rolling_window)
    }
    out <- combine.optimizations(opt.list)
    class(out) <- "opt.rebal.list"
    return(out)
  }
  if (inherits(portfolio, "mult.portfolio.spec")) {
    R <- proxy.mult.portfolio(R = R, mult.portfolio = portfolio)
    portfolio <- portfolio$top.portfolio
  }
  call <- match.call()
  start_t <- Sys.time()
  if (!is.null(portfolio) & !is.portfolio(portfolio)) {
    stop("you must pass in an object of class 'portfolio' to control the optimization")
  }
  if (hasArg(message)) 
    message = match.call(expand.dots = TRUE)$message
  else message = FALSE
  if (hasArg(trailing_periods)) {
    trailing_periods = match.call(expand.dots = TRUE)$trailing_periods
    rolling_window <- trailing_periods
  }
  if (!is.null(constraints)) {
    if (inherits(constraints, "v1_constraint")) {
      if (is.null(portfolio)) {
        tmp_portf <- portfolio.spec(assets = constraints$assets)
      }
      message("constraint object passed in is a 'v1_constraint' object, updating to v2 specification")
      portfolio <- update_constraint_v1tov2(portfolio = tmp_portf, 
                                            v1_constraint = constraints)
    }
    if (!inherits(constraints, "v1_constraint")) {
      portfolio <- insert_constraints(portfolio = portfolio, 
                                      constraints = constraints)
    }
  }
  if (!is.null(objectives)) {
    portfolio <- insert_objectives(portfolio = portfolio, 
                                   objectives = objectives)
  }
  call <- match.call()
  if (optimize_method == "random") {
    if (hasArg(rp_method)) 
      rp_method = match.call(expand.dots = TRUE)$rp_method
    else rp_method = "sample"
    if (hasArg(eliminate)) 
      eliminate = match.call(expand.dots = TRUE)$eliminate
    else eliminate = TRUE
    if (hasArg(fev)) 
      fev = match.call(expand.dots = TRUE)$fev
    else fev = 0:5
    if (is.null(rp)) 
      if (inherits(portfolio, "regime.portfolios")) {
        rp <- rp.regime.portfolios(regime = portfolio, 
                                   permutations = search_size, rp_method = rp_method, 
                                   eliminate = eliminate, fev = fev)
      }
    else {
      rp <- random_portfolios(portfolio = portfolio, 
                              permutations = search_size, rp_method = rp_method, 
                              eliminate = eliminate, fev = fev)
    }
  }
  else {
    rp = NULL
  }
  if (is.null(training_period) & !is.null(rolling_window)) 
    training_period <- rolling_window
  if (is.null(training_period)) {
    if (nrow(R) < 36) 
      training_period = nrow(R)
    else training_period = 36
  }
  if (is.null(rolling_window)) {
    ep.i <- endpoints(R, on = rebalance_on,k=6)[which(endpoints(R, 
                                                            on = rebalance_on) >= training_period)]
    ep <- ep.i[1]
    out_list <- foreach::foreach(ep = iterators::iter(ep.i), 
                                 .errorhandling = "pass", .packages = "PortfolioAnalytics") %dopar% 
      {
        optimize.portfolio(R[1:ep, ], portfolio = portfolio, 
                           optimize_method = optimize_method, search_size = search_size, 
                           trace = trace, rp = rp, parallel = FALSE, ... = ...)
      }
  }
  else {
    ep.i <- endpoints(R, on = rebalance_on,k=6)[which(endpoints(R, 
                                                            on = rebalance_on) >= training_period)]
    out_list <- foreach::foreach(ep = iterators::iter(ep.i), 
                                 .errorhandling = "pass", .packages = "PortfolioAnalytics") %dopar% 
      {
        optimize.portfolio(R[(ifelse(ep - rolling_window >= 
                                       1, ep - rolling_window, 1)):ep, ], portfolio = portfolio, 
                           optimize_method = optimize_method, search_size = search_size, 
                           trace = trace, rp = rp, parallel = FALSE, ... = ...)
      }
  }
  names(out_list) <- index(R[ep.i])
  end_t <- Sys.time()
  elapsed_time <- end_t - start_t
  if (message) 
    message(c("overall elapsed time:", end_t - start_t))
  out <- list()
  out$portfolio <- portfolio
  out$R <- R
  out$call <- call
  out$elapsed_time <- elapsed_time
  out$opt_rebalancing <- out_list
  class(out) <- c("optimize.portfolio.rebalancing")
  return(out)
}
```

Use the `rebalance_on="months"`

```
opt1 <- optimize.portfolio.rebalancing2(R=R, portfolio=port1,optimize_method="DEoptim",itermax=10, 
                                       search_size=2000,trace=TRUE,rebalance_on = "months",
                                       training_period = 125,rolling_window = 125)
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.