Search code examples
rsolverportfolior-portfolioanalytics

Is there an R function how to minimize the Tracking Error by finding optimal weights of portfolio assets


Let´s say I have a portfolio of in total n assets and their returns over x periods and als the return of a benchmark over the same periods. My objective is to find a vector of weights w such that

w∗=arg min TE(w)

where TE(w) is the tracking error defined as follows:

 TE = (sum((return.portfolio-return.benchmark)^2)/(x - 1))^0.5

In short, I want to replicate the return of a benchmark using a portfolio of n assets over x periods. The only constraints should be that the sum of all assets is equal to one and that no asset has a weight higher than 0.4.

Does anyone have an idea how I could solve this problem with R?


Solution

  • There are several options. Here is one with constrOptim from stats:

    #some data
    library(quantmod)
    library(PerformanceAnalytics)
    
    getReturns <- function(symbols, from='2000-01-01') {
      returns <- list()
      for (symbol in symbols) { #symbol=symbols[2]
        sym = getSymbols(symbol, from = from, adjustOHLC = TRUE, auto.assign = FALSE)
        return <- Return.calculate(Ad(sym))
        colnames(return) <- gsub("\\.Adjusted", "", colnames(return))
        returns[[symbol]] <- return
      }
      returns <- do.call(cbind, returns)
      return(returns)
    }
    
    symbols <-  c("MSFT","AAPL","AMZN","CSCO","ADBE")
    ret <- getReturns(symbols,from="2020-01-01")[-1,]
    
    x = nrow(ret) # periods
    N = ncol(ret) # assets
    
    set.seed(1324)
    Bench = rnorm(x,0.0015,0.03) #some benchmark
    plot(ts(Bench))
    
    # const opt
    ## needs a feasible starting value
    #  A'b >= b0  x < 0.4 --> -x >= -.4 x> -0.4 or 0 if not short selling
    # 
    fTE <- function(b,rets,Ben) {
      Pret=apply(rets,1,function(rl)sum(rl*b))
      sd(Pret-Ben)
    }
    upblim=0.4
    loblim=-0.4
    Amat <- matrix(1, 1, N)
    Amat <- rbind(Amat,-diag(N)) # upper limit
    Amat <- rbind(Amat,diag(N)) # if lower limit
    bvec       <- c(.9999999,rep(-upblim,N),rep(loblim,N))
    
    # feasable sol
    ws=c(3,4,5,6,1)
    (ws=ws/sum(ws))
    Amat %*% ws >= bvec
    
    (sol <- constrOptim(ws, fTE, NULL, ui = Amat, ci = bvec,rets=ret,Ben=Bench))
    sum(sol$par)
    pie(abs(sol$par),labels = colnames(ret))
    
    #> data.frame(Assets=colnames(ret),ws=sol$par)
    #  Assets          ws
    #1  MSFT -0.13233070
    #2  AAPL  0.39999989
    #3  AMZN  0.39999997
    #4  CSCO  0.26382163
    #5  ADBE  0.06850912