投资组合优化中的基数约束

时间:2017-07-27 09:43:04

标签: optimization convex-optimization cvxpy mixed-integer-programming

我正在使用cvxpy来解决一些简单的投资组合优化问题。我无法理解的唯一限制因素是非零投资组合持股数量的基数约束。我尝试了两种方法,一种是MIP方法,另一种是传统的凸方法。

这是一个传统工作的虚拟代码。

import numpy as np
import cvxpy as cvx

np.random.seed(12345)
n = 10
k = 6
mu = np.abs(np.random.randn(n, 1))
Sigma = np.random.randn(n, n)
Sigma = Sigma.T.dot(Sigma)
w = cvx.Variable(n)

ret = mu.T*w 
risk = cvx.quad_form(w, Sigma)
objective = cvx.Maximize(ret - risk)

constraints = [cvx.sum_entries(w) == 1, w>= 0, cvx.sum_smallest(w, n-k) >= 0,  cvx.sum_largest(w, k) <=1 ]

prob = cvx.Problem(objective, constraints)  
prob.solve()

print prob.status

output = []
for i in range(len(w.value)):
    output.append(round(w[i].value,2))


print 'Number of non-zero elements : ',sum(1 for i in output if i > 0)

我有想法使用,sum_smallest和sum_largest(cvxpy manual)我的想法是将最小的nk条目约束为0并让我的目标范围k总和为1,我知道我无法改变不平等的方向是为了保持凸起,但也许任何人都知道一种巧妙的方法来限制问题,同时仍然保持简单。

我的第二个想法是将其作为一个混合整数问题,按照

的方式进行
import numpy as np
import cvxpy as cvx

np.random.seed(12345)
n = 10
k = 6
mu = np.abs(np.random.randn(n, 1))
Sigma = np.random.randn(n, n)
Sigma = Sigma.T.dot(Sigma)
w = cvx.Variable(n)

binary = cvx.Bool(n)
integer = cvx.Int(n)

ret = mu.T*w 
risk = cvx.quad_form(w, Sigma)
objective = cvx.Maximize(ret - risk)

constraints = [cvx.sum_entries(w) == 1, w>= 0, cvx.sum_entries(binary) == k ]

prob = cvx.Problem(objective, constraints)  
prob.solve()

print prob.status

output = []
for i in range(len(w.value)):
    output.append(round(w[i].value,2))


print sum(1 for i in output if i > 0)

for i in range(len(w.value)):
    print round(binary[i].value,2)

print output

看着我的二进制矢量它似乎正在做正确的事但是sum_entries约束不起作用,查看二进制矢量值我注意到0不是0它非常小例如xxe ^ -20我假设这个会弄乱的。如果这是正确的方法,任何人都可以给我任何指导?我可以使用标准求解器,如果有帮助,可以使用Mosek。我希望有一个非MIP实现,因为我知道这是一个组合问题,并且对于更大的问题会变得很慢。最后,我想要限制目标馆藏的确切数量或范围,例如: 20-30。

此外,MIP周围的cvxpy文档非常简短。感谢

2 个答案:

答案 0 :(得分:2)

有点乱,这个问题。

首先:这种基数约束是NP难的。这意味着,如果不使用整数编程,则无法使用cvxpy表达它(否则会导致P = NP)!

那个说,如果没有试图制定这个约束,那么会有一个纯粹的代码版本会更好。我只是假设它是没有sum_smallestsum_largest约束的第一个代码。

让我们来解决MIP方法:

  • 您尝试执行此操作的代码根本没有任何意义
    • 你引入了一些二元变量,但它们根本没有与任何其他变量的连接(因此对它的总和的约束是没用的)!
    • 你介绍了一些整数变量,但它们完全没有任何用处!

所以这是一个MIP方法:

import numpy as np
import cvxpy as cvx

np.random.seed(12345)
n = 10
k = 6
mu = np.abs(np.random.randn(n, 1))
Sigma = np.random.randn(n, n)
Sigma = Sigma.T.dot(Sigma)
w = cvx.Variable(n)

ret = mu.T*w
risk = cvx.quad_form(w, Sigma)
objective = cvx.Maximize(ret - risk)

binary = cvx.Bool(n)  # !!!

constraints = [cvx.sum_entries(w) == 1, w>= 0, w - binary <= 0., cvx.sum_entries(binary) == k] # !!!

prob = cvx.Problem(objective, constraints)
prob.solve(verbose=True)

print(prob.status)

output = []
for i in range(len(w.value)):
    output.append(round(w[i].value,2))


print('Number of non-zero elements : ',sum(1 for i in output if i > 0))

所以我们只是添加了一些二进制变量并将连接它们添加到w以指示w is nonzero or not

如果w is nonzero

  • w将是&gt; 0因为约束w>= 0
  • binary需要为1,否则约束w - binary <= 0.未得到履行

所以它只是引入这些二进制文件和这一个指标约束。

现在cvx.sum_entries(binary) == k会做它应该做的事情。

小心我们在这里使用的含义方向。在对k进行约束时可能是相关的(例如&lt; =)。

请记住,默认的MIP解算器很糟糕。我也担心Mosek的界面(在cvxpy中次优)不能解决这个问题,但我可能错了。

编辑:您可以使用以下两个指标轻松制定您的范围:

  • (k> = a)&lt; = ind_0
  • (k <= b)&lt; = ind_1

并添加一个等于logical_and的约束:

  • ind_0 + ind_1&gt; = 2

答案 1 :(得分:0)

我遇到了一个类似的问题,即我的权重可以为负,并且不需要求和为1(但仍然需要限制),因此我修改了sascha的示例,以使用CVXpy绝对值来放宽这些约束。功能。这应该允许使用MIP解决基数约束的更通用方法

import numpy as np
import cvxpy as cvx

np.random.seed(12345)
n = 10
k = 6
mu = np.abs(np.random.randn(n, 1))
Sigma = np.random.randn(n, n)
Sigma = Sigma.T.dot(Sigma)
w = cvx.Variable(n)

ret = mu.T*w
risk = cvx.quad_form(w, Sigma)
objective = cvx.Maximize(ret - risk)

binary = cvx.Variable(n,boolean=True)  # !!!
maxabsw=2
constraints = [ w>= -maxabsw,w<=maxabsw, cvx.abs(w)/maxabsw - binary <= 0., cvx.sum(binary) == k] # !!!

prob = cvx.Problem(objective, constraints)
prob.solve(verbose=True)

print(prob.status)

output = []
for i in range(len(w.value)):
    output.append(round(w[i].value,2))


print('Number of non-zero elements : ',sum(1 for i in output if i > 0))