具有 CVXPY 约束的投资组合优化具有 DCP 错误

Portfolio Optimization with CVXPY constraint has DCP error

我正在优化一个投资组合,其中我最大化活跃 return。我还有四个约束条件:权重之和必须等于 1,所有权重必须小于或等于 0.05,所有权重必须大于或等于 0.0005,主动风险必须等于小于或等于 7% .

我有99只股票。这意味着我在计算中使用了 3 个矩阵。 alphas(预期 returns)是一个形状为 (99,1) 的矩阵。 W_bench 是每只股票在基准中的权重,它的形状与 alpha 相同。 V 是形状为 (99,99) 的协方差矩阵。从我下面的代码中可以看出,Active_Risk 就是有些人所说的跟踪误差。下面的代码是我如何设置优化:

weights = cp.Variable((99,1))
Active_Return = weights.T @ alphas
Active_Risk = cp.sqrt((weights - W_bench).T @ V @ (weights - W_bench))
constraints = [cp.sum(weights) == 1, 0.07 >= Active_Risk, 0.0005 <= weights, weights <= 0.05]
prob = cp.Problem(cp.Maximize(Active_Return), constraints)

我可以使用excel的解算器成功解决这个问题,虽然这需要相当长的时间。但是,我似乎无法在 Python 上使用 CVXPY 让它工作。我很乐意上传我的数据,以便其他人可以帮助我解决问题,但我不知道该怎么做。我也考虑过用随机数据制作这个问题的较小版本,但我担心最佳解决方案可能不可行。

当我 运行 我的代码 result = prob.solve() 我得到以下错误:

---------------------------------------------------------------------------
DCPError                                  Traceback (most recent call last)
<ipython-input-15-e01c2b878783> in <module>
      1 # The optimal objective value is returned by `prob.solve()`.
----> 2 result = prob.solve()

/opt/anaconda3/lib/python3.7/site-packages/cvxpy/problems/problem.py in solve(self, *args, **kwargs)
    287         else:
    288             solve_func = Problem._solve
--> 289         return solve_func(self, *args, **kwargs)
    290 
    291     @classmethod

/opt/anaconda3/lib/python3.7/site-packages/cvxpy/problems/problem.py in _solve(self, solver, warm_start, verbose, parallel, gp, qcp, **kwargs)
    565                     solver, warm_start, verbose, **kwargs)
    566 
--> 567         self._construct_chains(solver=solver, gp=gp)
    568         data, solving_inverse_data = self._solving_chain.apply(
    569             self._intermediate_problem)

/opt/anaconda3/lib/python3.7/site-packages/cvxpy/problems/problem.py in _construct_chains(self, solver, gp)
    508 
    509             except Exception as e:
--> 510                 raise e
    511 
    512     def _solve(self,

/opt/anaconda3/lib/python3.7/site-packages/cvxpy/problems/problem.py in _construct_chains(self, solver, gp)
    497 
    498                 self._intermediate_chain = \
--> 499                     construct_intermediate_chain(self, candidate_solvers, gp=gp)
    500                 self._intermediate_problem, self._intermediate_inverse_data = \
    501                     self._intermediate_chain.apply(self)

/opt/anaconda3/lib/python3.7/site-packages/cvxpy/reductions/solvers/intermediate_chain.py in construct_intermediate_chain(problem, candidates, gp)
     68             append += ("\nHowever, the problem does follow DQCP rules. "
     69                        "Consider calling solve() with `qcp=True`.")
---> 70         raise DCPError("Problem does not follow DCP rules. Specifically:\n" + append)
     71 
     72     elif gp and not problem.is_dgp():

DCPError: Problem does not follow DCP rules. Specifically:
The following constraints are not DCP:
power(var58 + -[[5.39791975e-03]
 [1.26209297e-03]
 [8.00351893e-05]
 [5.26548876e-02]
 [7.59655721e-03]
 [2.57535232e-03]
 [5.03372951e-04]
 [1.47480336e-03]
 [1.38599909e-04]
 [5.74108704e-03]
 [2.17434475e-03]
 [2.60441630e-04]
 [2.15412708e-04]
 [7.40692609e-03]
 [1.87030133e-04]
 [1.99318918e-04]
 [4.49890561e-04]
 [5.54053889e-04]
 [2.40066410e-04]
 [3.25109445e-05]
 [6.29530293e-02]
 [9.26141788e-03]
 [9.06847951e-02]
 [3.82569606e-04]
 [3.28101977e-04]
 [9.46531949e-04]
 [3.65845535e-02]
 [6.64869333e-04]
 [1.82867338e-03]
 [1.16324940e-04]
 [9.39765398e-04]
 [3.12166211e-03]
 [5.94047522e-04]
 [2.93656014e-04]
 [8.15666860e-04]
 [1.53355187e-04]
 [5.43259663e-04]
 [2.11729826e-04]
 [1.25169822e-04]
 [7.45171899e-04]
 [3.65823985e-04]
 [5.55365318e-04]
 [7.91907415e-05]
 [5.30872851e-03]
 [8.73601572e-04]
 [9.36948807e-04]
 [1.03941556e-02]
 [2.95556711e-04]
 [7.67666485e-03]
 [2.14996147e-04]
 [3.91275909e-04]
 [2.27743262e-04]
 [2.31689803e-04]
 [6.84002188e-03]
 [7.09758365e-02]
 [2.27532699e-04]
 [8.05783401e-04]
 [4.63372193e-04]
 [1.91341960e-03]
 [3.45573641e-04]
 [2.30427458e-02]
 [8.18612558e-04]
 [1.43341614e-03]
 [1.53359342e-04]
 [1.72991720e-04]
 [3.30942207e-04]
 [2.12224011e-02]
 [2.93271371e-04]
 [5.22032722e-02]
 [2.96349926e-03]
 [6.83703630e-04]
 [3.92175651e-04]
 [1.55757896e-03]
 [2.73614114e-04]
 [7.23199807e-03]
 [1.06194086e-02]
 [1.89837834e-04]
 [3.06087369e-03]
 [2.11597860e-04]
 [2.87620087e-04]
 [3.61744658e-04]
 [4.09530072e-04]
 [3.72525152e-04]
 [2.96987842e-02]
 [3.82775868e-01]
 [1.09826720e-03]
 [1.49363231e-03]
 [2.34448326e-04]
 [6.51708448e-03]
 [3.43523667e-03]
 [2.72356446e-03]
 [1.01445242e-03]
 [3.21249033e-04]
 [1.73042944e-02]
 [2.56326349e-03]
 [1.53653802e-03]
 [2.31159852e-04]
 [1.11857284e-03]
 [1.00845275e-02]].T * [[ 1.88468032  0.08627036 -0.81308165 ... -2.0625901  -0.64064643
  -1.12708076]
 [ 0.08627036  0.33892061  0.07628398 ...  0.19302222  0.14115192
  -0.02078213]
 [-0.81308165  0.07628398  0.58311836 ...  1.07460175  0.38954524
   0.53023314]
 ...
 [-2.0625901   0.19302222  1.07460175 ...  2.86645017  0.95771264
   1.34641816]
 [-0.64064643  0.14115192  0.38954524 ...  0.95771264  0.47074258
   0.43406168]
 [-1.12708076 -0.02078213  0.53023314 ...  1.34641816  0.43406168
   0.7897458 ]] * (var58 + -[[5.39791975e-03]
 [1.26209297e-03]
 [8.00351893e-05]
 [5.26548876e-02]
 [7.59655721e-03]
 [2.57535232e-03]
 [5.03372951e-04]
 [1.47480336e-03]
 [1.38599909e-04]
 [5.74108704e-03]
 [2.17434475e-03]
 [2.60441630e-04]
 [2.15412708e-04]
 [7.40692609e-03]
 [1.87030133e-04]
 [1.99318918e-04]
 [4.49890561e-04]
 [5.54053889e-04]
 [2.40066410e-04]
 [3.25109445e-05]
 [6.29530293e-02]
 [9.26141788e-03]
 [9.06847951e-02]
 [3.82569606e-04]
 [3.28101977e-04]
 [9.46531949e-04]
 [3.65845535e-02]
 [6.64869333e-04]
 [1.82867338e-03]
 [1.16324940e-04]
 [9.39765398e-04]
 [3.12166211e-03]
 [5.94047522e-04]
 [2.93656014e-04]
 [8.15666860e-04]
 [1.53355187e-04]
 [5.43259663e-04]
 [2.11729826e-04]
 [1.25169822e-04]
 [7.45171899e-04]
 [3.65823985e-04]
 [5.55365318e-04]
 [7.91907415e-05]
 [5.30872851e-03]
 [8.73601572e-04]
 [9.36948807e-04]
 [1.03941556e-02]
 [2.95556711e-04]
 [7.67666485e-03]
 [2.14996147e-04]
 [3.91275909e-04]
 [2.27743262e-04]
 [2.31689803e-04]
 [6.84002188e-03]
 [7.09758365e-02]
 [2.27532699e-04]
 [8.05783401e-04]
 [4.63372193e-04]
 [1.91341960e-03]
 [3.45573641e-04]
 [2.30427458e-02]
 [8.18612558e-04]
 [1.43341614e-03]
 [1.53359342e-04]
 [1.72991720e-04]
 [3.30942207e-04]
 [2.12224011e-02]
 [2.93271371e-04]
 [5.22032722e-02]
 [2.96349926e-03]
 [6.83703630e-04]
 [3.92175651e-04]
 [1.55757896e-03]
 [2.73614114e-04]
 [7.23199807e-03]
 [1.06194086e-02]
 [1.89837834e-04]
 [3.06087369e-03]
 [2.11597860e-04]
 [2.87620087e-04]
 [3.61744658e-04]
 [4.09530072e-04]
 [3.72525152e-04]
 [2.96987842e-02]
 [3.82775868e-01]
 [1.09826720e-03]
 [1.49363231e-03]
 [2.34448326e-04]
 [6.51708448e-03]
 [3.43523667e-03]
 [2.72356446e-03]
 [1.01445242e-03]
 [3.21249033e-04]
 [1.73042944e-02]
 [2.56326349e-03]
 [1.53653802e-03]
 [2.31159852e-04]
 [1.11857284e-03]
 [1.00845275e-02]]), 1/2) <= 0.07 , because the following subexpressions are not:
|--  var58 + -[[5.39791975e-03]
 [1.26209297e-03]
 [8.00351893e-05]
 [5.26548876e-02]
 [7.59655721e-03]
 [2.57535232e-03]
 [5.03372951e-04]
 [1.47480336e-03]
 [1.38599909e-04]
 [5.74108704e-03]
 [2.17434475e-03]
 [2.60441630e-04]
 [2.15412708e-04]
 [7.40692609e-03]
 [1.87030133e-04]
 [1.99318918e-04]
 [4.49890561e-04]
 [5.54053889e-04]
 [2.40066410e-04]
 [3.25109445e-05]
 [6.29530293e-02]
 [9.26141788e-03]
 [9.06847951e-02]
 [3.82569606e-04]
 [3.28101977e-04]
 [9.46531949e-04]
 [3.65845535e-02]
 [6.64869333e-04]
 [1.82867338e-03]
 [1.16324940e-04]
 [9.39765398e-04]
 [3.12166211e-03]
 [5.94047522e-04]
 [2.93656014e-04]
 [8.15666860e-04]
 [1.53355187e-04]
 [5.43259663e-04]
 [2.11729826e-04]
 [1.25169822e-04]
 [7.45171899e-04]
 [3.65823985e-04]
 [5.55365318e-04]
 [7.91907415e-05]
 [5.30872851e-03]
 [8.73601572e-04]
 [9.36948807e-04]
 [1.03941556e-02]
 [2.95556711e-04]
 [7.67666485e-03]
 [2.14996147e-04]
 [3.91275909e-04]
 [2.27743262e-04]
 [2.31689803e-04]
 [6.84002188e-03]
 [7.09758365e-02]
 [2.27532699e-04]
 [8.05783401e-04]
 [4.63372193e-04]
 [1.91341960e-03]
 [3.45573641e-04]
 [2.30427458e-02]
 [8.18612558e-04]
 [1.43341614e-03]
 [1.53359342e-04]
 [1.72991720e-04]
 [3.30942207e-04]
 [2.12224011e-02]
 [2.93271371e-04]
 [5.22032722e-02]
 [2.96349926e-03]
 [6.83703630e-04]
 [3.92175651e-04]
 [1.55757896e-03]
 [2.73614114e-04]
 [7.23199807e-03]
 [1.06194086e-02]
 [1.89837834e-04]
 [3.06087369e-03]
 [2.11597860e-04]
 [2.87620087e-04]
 [3.61744658e-04]
 [4.09530072e-04]
 [3.72525152e-04]
 [2.96987842e-02]
 [3.82775868e-01]
 [1.09826720e-03]
 [1.49363231e-03]
 [2.34448326e-04]
 [6.51708448e-03]
 [3.43523667e-03]
 [2.72356446e-03]
 [1.01445242e-03]
 [3.21249033e-04]
 [1.73042944e-02]
 [2.56326349e-03]
 [1.53653802e-03]
 [2.31159852e-04]
 [1.11857284e-03]
 [1.00845275e-02]].T * [[ 1.88468032  0.08627036 -0.81308165 ... -2.0625901  -0.64064643
  -1.12708076]
 [ 0.08627036  0.33892061  0.07628398 ...  0.19302222  0.14115192
  -0.02078213]
 [-0.81308165  0.07628398  0.58311836 ...  1.07460175  0.38954524
   0.53023314]
 ...
 [-2.0625901   0.19302222  1.07460175 ...  2.86645017  0.95771264
   1.34641816]
 [-0.64064643  0.14115192  0.38954524 ...  0.95771264  0.47074258
   0.43406168]
 [-1.12708076 -0.02078213  0.53023314 ...  1.34641816  0.43406168
   0.7897458 ]] * (var58 + -[[5.39791975e-03]
 [1.26209297e-03]
 [8.00351893e-05]
 [5.26548876e-02]
 [7.59655721e-03]
 [2.57535232e-03]
 [5.03372951e-04]
 [1.47480336e-03]
 [1.38599909e-04]
 [5.74108704e-03]
 [2.17434475e-03]
 [2.60441630e-04]
 [2.15412708e-04]
 [7.40692609e-03]
 [1.87030133e-04]
 [1.99318918e-04]
 [4.49890561e-04]
 [5.54053889e-04]
 [2.40066410e-04]
 [3.25109445e-05]
 [6.29530293e-02]
 [9.26141788e-03]
 [9.06847951e-02]
 [3.82569606e-04]
 [3.28101977e-04]
 [9.46531949e-04]
 [3.65845535e-02]
 [6.64869333e-04]
 [1.82867338e-03]
 [1.16324940e-04]
 [9.39765398e-04]
 [3.12166211e-03]
 [5.94047522e-04]
 [2.93656014e-04]
 [8.15666860e-04]
 [1.53355187e-04]
 [5.43259663e-04]
 [2.11729826e-04]
 [1.25169822e-04]
 [7.45171899e-04]
 [3.65823985e-04]
 [5.55365318e-04]
 [7.91907415e-05]
 [5.30872851e-03]
 [8.73601572e-04]
 [9.36948807e-04]
 [1.03941556e-02]
 [2.95556711e-04]
 [7.67666485e-03]
 [2.14996147e-04]
 [3.91275909e-04]
 [2.27743262e-04]
 [2.31689803e-04]
 [6.84002188e-03]
 [7.09758365e-02]
 [2.27532699e-04]
 [8.05783401e-04]
 [4.63372193e-04]
 [1.91341960e-03]
 [3.45573641e-04]
 [2.30427458e-02]
 [8.18612558e-04]
 [1.43341614e-03]
 [1.53359342e-04]
 [1.72991720e-04]
 [3.30942207e-04]
 [2.12224011e-02]
 [2.93271371e-04]
 [5.22032722e-02]
 [2.96349926e-03]
 [6.83703630e-04]
 [3.92175651e-04]
 [1.55757896e-03]
 [2.73614114e-04]
 [7.23199807e-03]
 [1.06194086e-02]
 [1.89837834e-04]
 [3.06087369e-03]
 [2.11597860e-04]
 [2.87620087e-04]
 [3.61744658e-04]
 [4.09530072e-04]
 [3.72525152e-04]
 [2.96987842e-02]
 [3.82775868e-01]
 [1.09826720e-03]
 [1.49363231e-03]
 [2.34448326e-04]
 [6.51708448e-03]
 [3.43523667e-03]
 [2.72356446e-03]
 [1.01445242e-03]
 [3.21249033e-04]
 [1.73042944e-02]
 [2.56326349e-03]
 [1.53653802e-03]
 [2.31159852e-04]
 [1.11857284e-03]
 [1.00845275e-02]])

我是 CVXPY 的新手,不完全了解 DCP 规则。任何帮助,将不胜感激。谢谢

这个问题的情况很糟糕,因为每个试图解决它的人 运行 都需要做一些工作,而您本可以通过研究一些独立的示例来避免这些工作(是的:即使您对某些最初的失败尝试发表了评论,我认为自己做应该比让我们做更容易。

还有因素上下文。 DCP是一个基于规则的框架,能够表达很多凸问题,但不是所有的凸问题。有一些迹象表明这个问题是 DCP 兼容的,但是一个明确的答案将再次导致我们这边做更多的工作。

这一切导致我方对以下方面的保证有限:

您的问题在:

Active_Risk = cp.sqrt((weights - W_bench).T @ V @ (weights - W_bench))
  • 这是变量的乘积,通常是非凸的,因此,在没有进一步假设的情况下处理一般情况不能在 DCP 中表达
  • 你得到了额外的假设,比如 V 是 PSD(因为它是一个协方差矩阵),但是 cvxpy 不能推断出这个给定的形式
  • 您需要更清楚地向 cvxpy 表达这个额外的结构/假设

而不是使用:

cp.sqrt((weights - W_bench).T @ V @ (weights - W_bench))

您将需要使用:

cp.quad_form((weights - W_bench), V)

我对上面一行中丢失的外部 cp.sqrt 感觉不好,但你可以试试。

重要的是要看到,使用这个非 sqrt quad_form 可以通过更改其他模型组件(缩放参数)来纠正。

所以代替:

0.07 >= Active_Risk

你将拥有:

0.07^2 >= Active_Risk

您不需要触摸 objective(是否平方;objective 会改变,但解向量不会)。

我能够使用以下代码进行优化:

weights = cp.Variable((99,1))
Active_Return = weights.T @ alphas
Active_Risk = cp.quad_form((weights - W_bench), V)
constraints = [sum(weights) == 1, 0.07**2 >= Active_Risk, weights >= 0.0005, weights <= 0.05]
prob = cp.Problem(cp.Maximize(Active_Return), constraints)


result = prob.solve(qcp=True, verbose= False, solver= 'SCS', eps= 1e-10, max_iters = 100000, warm_start= True)

注意:在未指定求解器或使用 ECOS 时,我遇到了几个错误。 SCS 工作完美,但需要大量迭代才能找到最佳解决方案。无论哪种方式,代码 运行.

只需要几秒钟