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?
You have chosen to have transaction costs without intercept (the coefficient of
y
in the 1st budget constraint is 0), soy=1
is always good and could just as well be removed. You don't need any integer optimization in that case either. The point ofy
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 forx
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 pushx
to zero because not investing at all minimizes the risk.