使用 Sympy 和 Scipy 求解欠定方程组和约束
Solving under-determined system of equations and constraints with Sympy and Scipy
我正在编写一个 python 脚本,该脚本将有助于年度投资组合再平衡。对于那些不熟悉这个概念的人来说,它基本上意味着每年 buying/selling 具有 gained/lost 价值的资产,以便您的投资组合实际匹配您想要的资产配置。如果在 1 月 1 日,您的投资组合分配比例为 50/50 stock/bond,并且股市表现出色,到同年 12 月 31 日,您的投资组合中的股票将增持,这意味着您将需要出售一些股票来购买债券,以便将分配比例恢复到 50/50。如果所有资产都在一个账户中,弄清楚如何做到这一点非常容易,但如果您在多个账户中进行投资,它就会变得复杂,尤其是如果它们是退休账户,您在退休年龄之前不应该退出.
对于再平衡步骤,我建立了一个包含 8 个简单线性方程的系统。它们看起来大致像这样(参见第二个代码块中的增广矩阵):
Asset1 + Asset2 + ... = us_bonds_target
Asset1 + Asset2+ ... = total_401k_value
...and so on
然后我按以下方式使用 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)
运行 此代码产生如下输出:
{Asset5: 7200311,
Asset6: -Asset8 - 1037317,
Asset7: 7178371,
Asset3: Asset8 + 8215688,
Asset4: 4665941,
Asset2: 7298011,
Asset1: -Asset8 + 2370854}
这非常接近我想要的,这是一个资产列表及其重新平衡的目标值。但是,有一些限制:
有时它会给我负面的解决方案,这在这种情况下是没有用的。现实生活中资产价值不能小于0。
这是一个更大的问题:我无法对任何变量指定约束。这些资产中的大多数是共同基金,并且为每项资产保持一定的最低价值是有利的,因为一些共同基金具有不同的"classes",如果您满足,它们具有较低的费用率(每年持有投资的成本)最低投资额。对于这些资产中的大多数,我希望每项资产的投资不少于 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 函数?如果是这样,我做错了什么?或者我应该以不同的方式解决这个问题?
The algorithm terminated successfully and determined that the problem is infeasible.
这意味着问题无法在限制条件下解决。要了解原因,让我们仔细看看 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 >= 0
开始 Asset8 <= 2370854
- 从
Asset3 >= 0
开始 Asset8 >= -8215688
- 从
Asset6 >= 0
得出 Asset8 <= -1037317
... 这与 Asset8 >= 0
不兼容。
综上所述,如果所有资产都必须为正,则问题无法解决。
我正在编写一个 python 脚本,该脚本将有助于年度投资组合再平衡。对于那些不熟悉这个概念的人来说,它基本上意味着每年 buying/selling 具有 gained/lost 价值的资产,以便您的投资组合实际匹配您想要的资产配置。如果在 1 月 1 日,您的投资组合分配比例为 50/50 stock/bond,并且股市表现出色,到同年 12 月 31 日,您的投资组合中的股票将增持,这意味着您将需要出售一些股票来购买债券,以便将分配比例恢复到 50/50。如果所有资产都在一个账户中,弄清楚如何做到这一点非常容易,但如果您在多个账户中进行投资,它就会变得复杂,尤其是如果它们是退休账户,您在退休年龄之前不应该退出.
对于再平衡步骤,我建立了一个包含 8 个简单线性方程的系统。它们看起来大致像这样(参见第二个代码块中的增广矩阵):
Asset1 + Asset2 + ... = us_bonds_target
Asset1 + Asset2+ ... = total_401k_value
...and so on
然后我按以下方式使用 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)
运行 此代码产生如下输出:
{Asset5: 7200311,
Asset6: -Asset8 - 1037317,
Asset7: 7178371,
Asset3: Asset8 + 8215688,
Asset4: 4665941,
Asset2: 7298011,
Asset1: -Asset8 + 2370854}
这非常接近我想要的,这是一个资产列表及其重新平衡的目标值。但是,有一些限制:
有时它会给我负面的解决方案,这在这种情况下是没有用的。现实生活中资产价值不能小于0。
这是一个更大的问题:我无法对任何变量指定约束。这些资产中的大多数是共同基金,并且为每项资产保持一定的最低价值是有利的,因为一些共同基金具有不同的"classes",如果您满足,它们具有较低的费用率(每年持有投资的成本)最低投资额。对于这些资产中的大多数,我希望每项资产的投资不少于 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 函数?如果是这样,我做错了什么?或者我应该以不同的方式解决这个问题?
The algorithm terminated successfully and determined that the problem is infeasible.
这意味着问题无法在限制条件下解决。要了解原因,让我们仔细看看 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 >= 0
开始Asset8 <= 2370854
- 从
Asset3 >= 0
开始Asset8 >= -8215688
- 从
Asset6 >= 0
得出Asset8 <= -1037317
... 这与Asset8 >= 0
不兼容。
综上所述,如果所有资产都必须为正,则问题无法解决。