Portfolio Optimization in R with transaction costs

109 views Asked by At

The goal is to rebalance the portfolio using the transaction costs as a penalty.

I tried to rewrite the example for portfolio optimization with transaction costs from the mosek homepage: https://docs.mosek.com/latest/rmosek/case-studies-portfolio.html#transaction-costs to minimize the risk instead of maximizing the return given a certain risk.

 GT     <- rbind( c(0.30758, 0.12146, 0.11341, 0.11327, 0.17625, 0.11973, 0.10435, 0.10638),
                  c(0.     , 0.25042, 0.09946, 0.09164, 0.06692, 0.08706, 0.09173, 0.08506),
                  c(0.     , 0.     , 0.19914, 0.05867, 0.06453, 0.07367, 0.06468, 0.01914),
                  c(0.     , 0.     , 0.     , 0.20876, 0.04933, 0.03651, 0.09381, 0.07742),
                  c(0.     , 0.     , 0.     , 0.     , 0.36096, 0.12574, 0.10157, 0.0571 ),
                  c(0.     , 0.     , 0.     , 0.     , 0.     , 0.21552, 0.05663, 0.06187),
                  c(0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.22514, 0.03327),
                  c(0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.2202 ))

 # max traded weight per x_j
  u <- 1.0

  n <- ncol(GT)
  
  w_prev <- rep(0.0, n)
  
  prob <- list(sense="min")
  prob$c <- c(rep(0,3*n), 1)
  tc <- rep(0.005, n)

  # Specify linear constraints
  # [ e'  g'  0  ]   [ x ]  =   1
  # [ I  -I   0  ] * [ z ]  <=  w_prev
  # [ I   I   0  ]   [ y ]  >=  w_prev
  # [ 0   I  -U  ]          <=  0
  prob$A <- rbind(cbind(Matrix(1.0,ncol=n), t(tc),                    Matrix(0.0,ncol=n), 0),
                  cbind(Diagonal(n, 1.0),   -Diagonal(n, 1.0),        Matrix(0,n,n),      0),
                  cbind(Diagonal(n, 1.0),   Diagonal(n, 1.0),         Matrix(0,n,n),      0),
                  cbind(Matrix(0,n,n),      Diagonal(n, 1.0),         Diagonal(n,-u),     0))
  
  
  prob$bc <- rbind(blc=c(1.0, rep(-Inf,n), w_prev, rep(-Inf,n)),
                   buc=c(1.0, w_prev, rep(Inf,n), rep(0.0,n)))
  
  # No shortselling and the linear bound 0 <= y <= 1     
  prob$bx <- rbind(blx=c(rep(0.0,n), rep(-Inf,n), rep(0.0,n), 0.0),
                   bux=c(rep(Inf,n), rep(Inf, n), rep(1.0,n), Inf))
  
  # Specify the affine conic constraints for risk
  prob$F <- rbind(
    cbind(Matrix(0.0,nrow=1,ncol=3*n),     1), 
    cbind(GT, Matrix(0.0,nrow=n,ncol=2*n), 0)
  )
  prob$g <- c(0, rep(0,n))
  prob$cones <- matrix(list("QUAD", 1+n, NULL), nrow=3, ncol=1)
  rownames(prob$cones) <- c("type","dim","conepar")
  
  # Demand y to be integer (hence binary)
  prob$intsub <- (2*n+1):(3*n);
  
  # Solve the problem
  r <- mosek(prob,list(verbose=10))
  stopifnot(identical(r$response$code, 0))
  
  stopifnot(identical(r$sol$int$solsta, 'INTEGER_OPTIMAL'))    
  
  # Return the solution
  x <- r$sol$int$xx[1:n]
  z <- r$sol$int$xx[(n+1):(2*n)]
  y <- r$sol$int$xx[(2*n+1):(3*n)]
  t_value <- r$sol$int$xx[3*n+1]

The problem is that no matter the value of w_prev, y is always 1 for every asset j and z which should give the traded amount in asset j is always 1-tc.

I figured there must be something wrong with my linear constraints in rows 2 and 3 but I just dont see it.

Also, is that even the right approach for the problem or should the transaction costs be in the objective?

1

There are 1 answers

1
Michal Adamaszek On

You have chosen to have transaction costs without intercept (the coefficient of y in the 1st budget constraint is 0), so y=1 is always good and could just as well be removed. You don't need any integer optimization in that case either. The point of y is to activate a fixed const if you trade at all, and otherwise keep it at 0, which is where the combinatorics comes in..

With that choice the optimum is to push as much as possible into z to leave as little as possible for x over which the risk is minimized.

Here you are hit by another issue, that is you lost the returns completely. If you removed maximizing returns from the objective, then possibly you should have a constraint such as mu'*x >= min_return. Without it the optimizer is always incentivised to push x to zero because not investing at all minimizes the risk.