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

时间:2021-01-30 20:22:54

I'm working on a python script that will help with annual portfolio rebalancing. For those of you unfamiliar with the concept, it basically means annually buying/selling assets that have gained/lost value so that your portfolio composition actually matches what your desired asset allocation is. If on January 1st, you have a portfolio with a 50/50 stock/bond split, and the stock market has a great year, by Dec 31st of that same year you'll be overweight stocks in your portfolio, which means you'll need to sell some stocks to buy bonds in order to get that split back to 50/50. Figuring out how to do this is trivially easy if all the assets are in one account, but it can get complicated if you have investments in multiple accounts, especially if they're retirement accounts that you're not supposed to withdraw from until retirement age.

我正在研究一个有助于年度投资组合重新平衡的python脚本。对于那些不熟悉这个概念的人来说,它基本上意味着每年购买/出售已经获得/失去价值的资产,这样你的投资组合实际上与你想要的资产配置相匹配。如果在1月1日,你有一个50/50股票/债券分割的投资组合,股票市场有一个伟大的一年,到同年12月31日,你将超重股票在你的投资组合,这意味着你将需要卖出一些股票来购买债券才能将这一分割回到50/50。如果所有资产都在一个账户中,弄清楚如何做到这一点非常简单,但如果你在多个账户上投资,它会变得复杂,特别是如果他们是退休账户,你不应该退休直到退休年龄。

For the rebalancing step, I set up a system of 8 simple linear equations. They look roughly like this (see augmented matrix in 2nd code block):

对于重新平衡步骤,我建立了一个由8个简单线性方程组成的系统。它们看起来大致相似(参见第二个代码块中的增广矩阵):

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

I then use Sympy's solve_linear_system function in the following manner:

然后我以下列方式使用Sympy的solve_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)

Running this code produces output like this:

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

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

This is pretty close to what I want, which is a list of assets and their target values for rebalancing. However, there are a few limitations:

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

  1. Sometimes it will give me negative solutions, which in this context isn't useful. The asset values can't be less than 0 in real life.

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

  2. This is a bigger issue: I can't specify constraints on any of the variables. Most of these assets are mutual funds, and it's advantageous to maintain certain minimum values for each asset, because some mutual funds have different "classes" that have lower expense ratios (how much it costs to hold the investment annually) if you meet an investment minimum. For most of these assets, I'd like to have no less than $10k invested in each. I realize that sometimes this will cause the systemto be unsolvable, but I'd at least like to attempt solving it with these constraints first, then relaxing them if the solver fails.

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

After poking around on stack overflow and google, I learned that this solve of problem should be solvable using linear programming. So I set up the problem as follows (note that I haven't incorporated minimum values requirements for assets into the code yet--other than them needing to be positive--I'm just trying to prove this approach will produce useful solutions):

在堆栈溢出和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')

However, when I run this code, I get the following output:

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

     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])

It seems like linprog doesn't like my equations for some reason. Is my problem well-suited for the linprog function? And if so, what am I doing wrong? Or should I be approaching this problem in a different manner?

由于某种原因,linprog似乎不喜欢我的方程式。我的问题是否适合linprog功能?如果是这样,我做错了什么?或者我应该以不同的方式处理这个问题?

1 个解决方案

#1


1  

The algorithm terminated successfully and determined that the problem is infeasible.

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

This means that the problem is impossible to solve within the constraints. To understand why, let's take a closer look at the sympy results:

这意味着在约束条件下无法解决问题。为了理解原因,让我们仔细研究一下这个问题的结果:

Asset5: 7200311
Asset6: -Asset8 - 1037317
Asset7: 7178371
Asset3: Asset8 + 8215688
Asset4: 4665941
Asset2: 7298011
Asset1: -Asset8 + 2370854
  • Assets 2, 4, 5, 7 are uniquely defined.
  • 资产2,4,5,7是唯一定义的。
  • Assets 1, 3, 6, 8 share one degree of freedom
  • 资产1,3,6,8共享一个*度

This means we can choose any value for one of assets 1, 3, 6, or 8 and the others are derived from that. Now apply the constraint that assets can't be negative.

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

  • From Asset1 >= 0 follows that Asset8 <= 2370854
  • 从Asset1> = 0开始,Asset8 <= 2370854
  • From Asset3 >= 0 follows that Asset8 >= -8215688
  • 从Asset3> = 0后,Asset8> = -8215688
  • From Asset6 >= 0 follows that Asset8 <= -1037317 ... this is incompatible with Asset8 >= 0.
  • 从Asset6> = 0后,Asset8 <= -1037317 ...这与Asset8> = 0不兼容。

In summary, the problem can't be solved if all assets must be positive.

总之,如果所有资产必须是积极的,那么问题就无法解决。

#1


1  

The algorithm terminated successfully and determined that the problem is infeasible.

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

This means that the problem is impossible to solve within the constraints. To understand why, let's take a closer look at the sympy results:

这意味着在约束条件下无法解决问题。为了理解原因,让我们仔细研究一下这个问题的结果:

Asset5: 7200311
Asset6: -Asset8 - 1037317
Asset7: 7178371
Asset3: Asset8 + 8215688
Asset4: 4665941
Asset2: 7298011
Asset1: -Asset8 + 2370854
  • Assets 2, 4, 5, 7 are uniquely defined.
  • 资产2,4,5,7是唯一定义的。
  • Assets 1, 3, 6, 8 share one degree of freedom
  • 资产1,3,6,8共享一个*度

This means we can choose any value for one of assets 1, 3, 6, or 8 and the others are derived from that. Now apply the constraint that assets can't be negative.

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

  • From Asset1 >= 0 follows that Asset8 <= 2370854
  • 从Asset1> = 0开始,Asset8 <= 2370854
  • From Asset3 >= 0 follows that Asset8 >= -8215688
  • 从Asset3> = 0后,Asset8> = -8215688
  • From Asset6 >= 0 follows that Asset8 <= -1037317 ... this is incompatible with Asset8 >= 0.
  • 从Asset6> = 0后,Asset8 <= -1037317 ...这与Asset8> = 0不兼容。

In summary, the problem can't be solved if all assets must be positive.

总之,如果所有资产必须是积极的,那么问题就无法解决。