How to setup an SOCP with multiple conic constraints?
Closed this issue · 1 comments
Hi,
I am trying to solve a portfolio optimization problem using ecos in R. The objective functions is to maximize the portfolio return, subject to bounds on the weights, a portfolio volatility constraint (weights %% covariance matrix %% weights), and a tracking error constraint ( (portfolio weights - benchmark weights) %% covariance matrix %% (portfolio weights - benchmark weights ) ).
My issue is that I cannot get ecos to work with these two conic constraints simultaneously(i.e. one for portfolio volatility, and one for tracking error). I can get it working perfectly using either of these constraints on their own, however.
For example, here's how to solve the problem using only the portfolio volatility constraint:
The decision variables are
x = portfolio weights
w_a = active weights (not used for now)
r = portfolio return (what is being maximized)
f = portfolio risk
g = asset contribution to portfolio risk
and f >= g is the conic constraint
require(Matrix)
require(ECOSolveR)
# create returns data
ret <- cbind(rnorm(1000,.05/252,.15/sqrt(252)),
rnorm(1000,.03/252,.12/sqrt(252)),
rnorm(1000,.06/252,.16/sqrt(252)),
rnorm(1000,.08/252,.15/sqrt(252)),
rnorm(1000,.03/252,.15/sqrt(252)))
# number of assets / factors
n <- ncol(ret)
# treat other factor returns as contrast to baseline factor
ret[,2:n] <- ret[,2:n] - ret[,1]
# covariance matrix
covmat <- cov(ret)
# mu
mu <- colMeans(ret)
# benchmark weights
w_b <- c(1,runif(4))
# factorize covariance matrix
G <- chol(covmat)
# define equality matrix
Amat <- rbind(
cbind(mu,rep(0,n), -1, 0, Matrix(0,ncol = n,sparse = T)), # portfolio return = r
cbind(diag(n),-diag(n), rep(0,n),rep(0,n),Matrix(0,n,n)), # active weights = w_a
cbind(G,Matrix(0,n,n), rep(0,n), rep(0,n), -diag(n)), # portfolio risk = g
cbind(Matrix(c(1,rep(0,n-1)),n), # baseline factor loading = 1
Matrix(0,n,n-1),
Matrix(0,n,n),
Matrix(0,n),
Matrix(0,n),
Matrix(0,n,n))
)
bvec <- c(0, # portfolio return = t
w_b, # benchmark weights
rep(0,n), # portfolio risk = g
c(1,rep(0,n-1)) # baseline factor loading = 1
)
# define inequality matrix
Gmat <- rbind(
cbind(-diag(n), Matrix(0,n,n), rep(0,n),rep(0,n), Matrix(0,n,n)), # weight lower bounds
cbind(diag(n), Matrix(0,n,n), rep(0,n),rep(0,n) ,Matrix(0,n,n)), # weight upper bounds
cbind(rep(0,n),rep(0,n), 0, 1,Matrix(0,ncol = n)), # f <= maxRisk
cbind(rep(0,n), rep(0,n) ,0, -1,Matrix(0,ncol = n)), # f >= g
cbind(Matrix(0,n,n), Matrix(0,n,n),rep(0,n), rep(0,n), -diag(n)) # g <= f
)
hvec <- c(c(1,rep(0,n-1)), # weight lower bounds
c(1,rep(1,n-1)), # weight upper bounds
.15/sqrt(252), # max risk
0, # f >= g
rep(0,n) # g <= f
)
# Define the number of inequality CONSTRAINTS (nrows in Gmat)
dims <- list(l=as.integer(n + n + 1), # portfolio weights, active weights, portfolio return
q= as.integer(c(n+1)), # The cone - portfolio volatility
e=0L)
solution_ecos <- ECOS_csolve(c = c(rep(0,n),rep(0,n),-1,0,rep(0,n)) # maximize return
,A = Amat
,b = bvec
,G = Gmat
,h = hvec
,dims = dims
,control = ecos.control(maxit = 500L,verbose = 1L)
)
Now here is how I am trying to solve the problem with both constraints:
The decision variables are:
x = portfolio weights
w_a = active weights (portfolio weight - benchmark weight)
r = portfolio return (what is maximized)
f = portfolio risk
g = asset contribution to portfolio risk
f2 = portfolio tracking error
g2 = asset contribution to tracking error
I placed the two conic constraints
f >= g
f2 >= g2
in the last elements of the inequality vector and specified their dimensions in the "dims" argument as a vector of length 2, with each element of that vector being the combined size f and g (1 + 5 in this example).
# define Amat
Amat <- rbind(
cbind(mu,rep(0,n), -1, 0, Matrix(0,ncol = n,sparse = T),0,Matrix(0,ncol = n)), # portfolio return = r
cbind(diag(n),-diag(n), rep(0,n),rep(0,n),Matrix(0,n,n),rep(0,n),Matrix(0,n,n)), # active weights = w_a
cbind(G,Matrix(0,n,n), rep(0,n), rep(0,n), -diag(n),rep(0,n),Matrix(0,n,n)), # portfolio risk = g
cbind(Matrix(0,n,n), G, rep(0,n), rep(0,n),Matrix(0,n,n),rep(0,n), -diag(n)), # portfolio active risk = g2
cbind(Matrix(c(1,rep(0,n-1)),n), # baseline factor loading = 1
Matrix(0,n,n-1),
Matrix(0,n,n),
Matrix(0,n),
Matrix(0,n),
Matrix(0,n,n),
Matrix(0,n),
Matrix(0,n,n))
)
# define bvec
bvec <- c(0, # portfolio return = t
w_b, # benchmark weights
rep(0,n), # portfolio risk = g,
rep(0,n), # portfolio tracking error = g2
c(1,rep(0,n-1)) # baseline factor loading = 1
)
Gmat <- rbind(
cbind(-diag(n), Matrix(0,n,n), rep(0,n),rep(0,n), Matrix(0,n,n),rep(0,n),Matrix(0,n,n)), # weight lower bounds
cbind(diag(n), Matrix(0,n,n), rep(0,n),rep(0,n) ,Matrix(0,n,n),rep(0,n),Matrix(0,n,n)), # weight upper bounds
cbind(rep(0,n),rep(0,n), 0, 1,Matrix(0,ncol = n),0,Matrix(0,ncol = n)), # f <= maxRisk
cbind(rep(0,n),rep(0,n), 0, 0 ,Matrix(0,ncol = n),1,Matrix(0,ncol = n)), # f2 <= max tracking error
cbind(rep(0,n), rep(0,n) ,0, -1,Matrix(0,ncol = n),0,Matrix(0,ncol = n)), # f >= g
cbind(Matrix(0,n,n), Matrix(0,n,n),rep(0,n), rep(0,n), -diag(n),rep(0,n),Matrix(0,n,n)), # g <= f
cbind(rep(0,n), rep(0,n) ,0, 0, Matrix(0,ncol = n),-1,Matrix(0,ncol = n)), # f2 >= g2
cbind(Matrix(0,n,n), Matrix(0,n,n),rep(0,n), rep(0,n), Matrix(0,n,n),rep(0,n),-diag(n)) # g2 <= f2
)
hvec <- c(c(1,rep(0,n-1)), # weight lower bounds
c(1,rep(1,n-1)), # weight upper bounds
.15/sqrt(252), # max risk
.05/sqrt(252), # max tracking error
0, # f >= g
rep(0,n), # g <= f
0, # f2 >= g
rep(0,n) # g2 <= f2
)
# Defines the number of inequality CONSTRAINTS (nrows in Gmat)
dims <- list(l=as.integer(n + n + 1), # portfolio weights, active weights, portfolio return
q= as.integer(c(n+1,n+1)), # The cones - portfolio volatility + tracking error
e=0L)
solution_ecos <- ECOS_csolve(c = c(rep(0,n),rep(0,n),-1,0,rep(0,n),0,rep(0,n)) # maximize return
,A = Amat
,b = bvec
,G = Gmat
,h = hvec
,dims = dims
,control = ecos.control(maxit = 500L,verbose = 1L)
)
When I run this program, ecos returns a NULL value, with no error messages at all, even with the verbose = 1L setting.
It's important to note that both the portfolio volatility and tracking error constraints work fine on their own. It's only when I try to include both that this happens. This leads me to believe I have not set up the two conic constraints the way ecos wants.
What am I doing wrong here? How should I go about specifying a problem with more than one conic constraint?
Never mind, I found the problem. I had forgotten to update the number of linear constraints in dims 😔