optiSolve package in r

444 views Asked by At

I'm trying to maximize the portfolio return subject to 5 constraints:

1.- a certain level of portfolio risk

2.- the same above but oposite sign (I need that the risk to be exactly that number)

3.- the sum of weights have to be 1

4.- all the weights must be greater or equal to cero

5.- all the weights must be at most one

I'm using the optiSolve package because I didn't find any other package that allow me to write this problem (or al least that I understood how to use it).

I have three big problems here, the first is that the resulting weights vector sum more than 1 and the second problem is that I can't declare t(w) %*% varcov_matrix %*% w == 0 in the quadratic constraint because it only allows for "<=" and finally I don't know how to put a constraint to get only positives weights

vector_de_retornos <- rnorm(5)  
matriz_de_varcov <- matrix(rnorm(25), ncol = 5)

library(optiSolve)

restriccion1 <- quadcon(Q = matriz_de_varcov, dir = "<=", val = 0.04237972)

restriccion1_neg <- quadcon(Q = -matriz_de_varcov, dir = "<=",
                            val = -mean(limite_inf, limite_sup))

restriccion2 <- lincon(t(vector_de_retornos),
                       d=rep(0, nrow(t(vector_de_retornos))), 
                       dir=rep("==",nrow(t(vector_de_retornos))),
                       val = rep(1, nrow(t(vector_de_retornos))),
                       id=1:ncol(t(vector_de_retornos)),
                       name = nrow(t(vector_de_retornos)))
restriccion_nonnegativa <- lbcon(rep(0,length(vector_de_retornos)))

restriccion_positiva <- ubcon(rep(1,length(vector_de_retornos)))

funcion_lineal <- linfun(vector_de_retornos, name = "lin.fun")
funcion_obj <- cop(funcion_lineal, max = T, ub = restriccion_positiva,
                   lc = restriccion2, lb = restriccion_nonnegativa, restriccion1,
                   restriccion1_neg)
porfavor_funciona <- solvecop(funcion_obj, solver = "alabama")

> porfavor_funciona$x
            1             2             3             4             5 
-3.243313e-09 -4.709673e-09  9.741379e-01  3.689040e-01 -1.685290e-09 

> sum(porfavor_funciona$x)
[1] 1.343042

Someone knows how to solve this maximization problem with all the constraints mentioned before or tell me what I'm doing wrong? I'll really appreciate that, because the result seems like is not taking into account the constraints. Thanks!

2

There are 2 answers

2
cookesd On BEST ANSWER
  1. Your restriccion2 makes the weighted sum of x is 1, if you also want to ensure the regular sum of x is 1, you can modify the constraint as follows:
restriccion2 <- lincon(rbind(t(vector_de_retornos),
                             # make a second row of coefficients in the A matrix
                             t(rep(1,length(vector_de_retornos)))),
                       d=rep(0,2), # the scalar value for both constraints is 0
                       dir=rep('==',2), # the direction for both constraints is '=='
                       val=rep(1,2), # the rhs value for both constraints is 1
                       id=1:ncol(t(vector_de_retornos)), # the number of columns is the same as before
                       name= 1:2)

If you only want the regular sum to be 1 and not the weighted sum you can replace your first parameter in the lincon function as you've defined it to be t(rep(1,length(vector_de_retornos))) and that will just constrain the regular sum of x to be 1.

  1. To make an inequality constraint using only inequalities you need the same constraint twice but with opposite signs on the coefficients and right hand side values between the two (for example: 2x <= 4 and -2x <= -4 combines to make the constraint 2*x == 4). In your edit above, you provide a different value to the val parameter so these two constraints won't combine to make the equality constraint unless they match except for opposite signs as below.

restriccion1_neg <- quadcon(Q = -matriz_de_varcov, dir = "<=", val = -0.04237972)

  1. I'm not certain because I can't find precision information in the package documentation, but those "negative" values in the x vector are probably due to rounding. They are so small and are effectively 0 so I think the non-negativity constraint is functioning properly.

restriccion_nonnegativa <- lbcon(rep(0,length(vector_de_retornos)))

1
Erwin Kalvelagen On

A constraint of the form

 x'Qx = a

is non-convex. (More general: any nonlinear equality constraint is non-convex). Non-convex problems are much more difficult to solve than convex ones and require specialized, global solvers. For convex problems, there are quite a few solvers available. This is not the case for non-convex problems. Most portfolio models are formulated as convex QP (quadratic programming i.e. risk -- the quadratic term -- is in the objective) or convex QCP/SOCP problems (quadratic terms in the constraints, but in a convex fashion). So, the constraint

x'Qx <= a

is easy (convex), as long as Q is positive-semi definite. Rewriting x'Qx=a as

x'Qx <= a
-x'Qx <= -a

unfortunately does not make the non-convexity go away, as -Q is not PSD. If we are maximizing return, we usually only use x'Qx <= a to limit the risk and forget about the >= part. Even more popular is to put both the return and the risk in the objective (that is the standard mean-variable portfolio model).

A possible solver for solving non-convex quadratic problems under R is Gurobi.