rsolverportfolior-portfolioanalytics

optiSolve package in r


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!


Solution

    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)))