我正在使用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文档非常简短。感谢
答案 0 :(得分:2)
有点乱,这个问题。
首先:这种基数约束是NP难的。这意味着,如果不使用整数编程,则无法使用cvxpy表达它(否则会导致P = NP)!
那个说,如果没有试图制定这个约束,那么会有一个纯粹的代码版本会更好。我只是假设它是没有sum_smallest
和sum_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中次优)不能解决这个问题,但我可能错了。
编辑:您可以使用以下两个指标轻松制定您的范围:
并添加一个等于logical_and的约束:
答案 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))