用Sympy和Scipy解决方程和约束的欠定系统

时间:2018-08-20 02:28:06

标签: python scipy sympy finance

我正在研究一个Python脚本,该脚本将有助于年度投资组合的重新平衡。对于那些不熟悉该概念的人来说,它基本上意味着每年购买/出售具有增值/亏损值的资产,以便您的投资组合组成实际上与所需的资产分配相匹配。如果在1月1日,您的投资组合的股票/债券比例为50/50,并且股市表现出色,那么到同年12月31日,您的投资组合中的股票将超重,这意味着您将需要卖出一些股票来购买债券,以使分配比例回到50/50。如果所有资产都在一个账户中,那么如何做到这一点很容易,但是如果您在多个账户中进行投资,则可能会变得很复杂,尤其是如果这些账户是退休账户,而您不应该在退休年龄之前退出的话。

对于再平衡步骤,我建立了一个由8个简单线性方程式组成的系统。它们大致如下所示(请参见第二代码块中的增强矩阵):

Asset1 + Asset2 + ... = us_bonds_target 
Asset1 + Asset2+ ... = total_401k_value
...and so on

然后我以以下方式使用Sympy的resolve_linear_system函数:

from sympy import Matrix, symbols, solve_linear_system

Assets_Matrix = Matrix([[1, 0, 0, 0, 1, 0, 0, 1, 9571165],
                        [0, 1, 0, 0, 0, 0, 0, 0, 7298011],
                        [0, 0, 0, 1, 0, 0, 0, 0, 4665941],
                        [0, 0, 1, 0, 0, 1, 0, 0, 7178371],
                        [0, 0, 0, 0, 0, 0, 1, 0, 7178371],
                        [1, 1, 1, 1, 0, 0, 0, 0, 22550494],
                        [0, 0, 0, 0, 1, 0, 0, 0, 7200311],
                        [0, 0, 0, 0, 0, 1, 1, 1, 6141054]])

Asset1, Asset2, Asset3, Asset4, Asset5, Asset6, Asset7, Asset8 = symbols('Asset1, Asset2, Asset3, Asset4, Asset5, Asset6, Asset7, Asset8', nonnegative = True)
solution = solve_linear_system(Assets_Matrix, Asset1, Asset2, Asset3, Asset4, Asset5, Asset6, Asset7, Asset8)

运行此代码将产生如下输出:

{Asset5: 7200311,
 Asset6: -Asset8 - 1037317,
 Asset7: 7178371,
 Asset3: Asset8 + 8215688,
 Asset4: 4665941,
 Asset2: 7298011,
 Asset1: -Asset8 + 2370854}

这与我想要的非常接近,这是资产列表及其重新平衡的目标值。但是,有一些限制:

  1. 有时它会带给我负面的解决方案,在这种情况下这是没有用的。现实生活中资产值不能小于0。

  2. 这是一个更大的问题:我无法在任何变量上指定约束。这些资产大多数都是共同基金,有利于为每种资产维持一定的最小值,因为如果您达成一项投资,某些共同基金具有不同的“类别”,它们的费用比率(每年持有该投资的成本是多少)会较低。最低。对于这些资产中的大多数,我希望每笔投资不少于1万美元。我意识到有时这会导致系统无法解决,但是我至少想先尝试使用这些约束来解决它,然后在求解器失败时放宽它们。

在遍历堆栈溢出和Google之后,我了解到使用线性编程可以解决此问题。因此,我将问题设置如下(请注意,我尚未将资产的最低价值要求并入代码中(除了它们必须为正值外,我只是想证明这种方法将产生有用的解决方案)) :

from scipy.optimize import linprog

A = [[1, 0, 0, 0, 1, 0, 0, 1],
     [0, 1, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 1, 0, 0, 0, 0],
     [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 0, 0, 0, 0, 0, 1, 0],
     [1, 1, 1, 1, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 0, 0, 0],
     [0, 0, 0, 0, 0, 1, 1, 1]]
B = [9571165, 7298011, 4665941, 7178371, 7178371, 22550494, 7200311, 6141054]
C = [0, 0, 0, 0, 0, 0, 0, 0]

linprog(c = C, A_eq = A, b_eq = B, bounds = (0, None), method = 'interior-point')

但是,当我运行此代码时,得到以下输出:

     con: array([  2370852.29007765,         0.        ,         0.        ,
         7178369.8786112 ,         0.        ,  10586541.29564287,
               0.        ,  -1037319.12695402])
     fun: 0.0
 message: 'The algorithm terminated successfully and determined that the problem is infeasible.'
     nit: 4
   slack: array([], dtype=float64)
  status: 2
 success: False
       x: array([  3.97865052e-02,   7.29801100e+06,   6.64570623e-01,
         4.66594100e+06,   7.20031100e+06,   4.56818174e-01,
         7.17837100e+06,   1.67013585e+00])

由于某种原因,似乎linprog不喜欢我的方程式。我的问题是否非常适合linprog函数?如果是这样,我在做什么错?还是应该以其他方式解决这个问题?

1 个答案:

答案 0 :(得分:1)

  

算法成功终止,并确定该问题不可行

这意味着无法在约束范围内解决问题。要了解原因,让我们仔细看一下sympy结果:

Asset5: 7200311
Asset6: -Asset8 - 1037317
Asset7: 7178371
Asset3: Asset8 + 8215688
Asset4: 4665941
Asset2: 7298011
Asset1: -Asset8 + 2370854
  • 资产2、4、5、7是唯一定义的。
  • 资产1、3、6、8享有一个自由度

这意味着我们可以为资产1、3、6或8中的任何一个选择任何值,而其他值是从中得出的。现在应用资产不能为负的约束。

  • Asset1 >= 0Asset8 <= 2370854
  • Asset3 >= 0Asset8 >= -8215688
  • Asset6 >= 0开始,Asset8 <= -1037317 ...与Asset8 >= 0不兼容。

总而言之,如果所有资产都必须为正数,则无法解决该问题。