【问题标题】:How to solve a system of equations and constraints for portfolio optimization?如何解决投资组合优化的方程和约束系统?
【发布时间】:2020-01-17 15:25:39
【问题描述】:

我有一个DataFrame,如下:

Name  Volatility    Return
a      0.0243        0.212
b      0.0321        0.431
c      0.0323        0.443
d      0.0391        0.2123
e      0.0433        0.3123

我希望有一个 Volatility0.035 以及用于该波动性的最大化 Return

也就是说,我想在一个新的DfName 中,以及该资产在我的portfolio 中的百分比给出Return 的最大Volatility 等于@ 987654332@.

因此,我需要求解具有多个条件的方程组,以获得固定结果 (Volatility == 0.035) 的最佳解 (HighestReturn)。

条件是:

  • 每个资产的权重介于 0 和 1 之间。
  • 权重之和为 1。
  • 权重之和乘以每项资产的波动率就是“期望波动率”。
  • 权重总和乘以每项资产的回报就是“总回报”。这应该最大化。

【问题讨论】:

  • 告诉我们你到目前为止所做的尝试。
  • stackoverflow.com/questions/59492434/… 展示如何编写自定义函数并将其应用于数据帧
  • 你能展示一下这个例子的结果吗?
  • “我想求解具有多个条件的方程组”——哪些方程?有哪些条件?它们与输入有何关系?

标签: python solver z3py


【解决方案1】:

这是一种使用Z3Py 的方法,这是一个开源的SAT/SMT 求解器。 在 SAT/SMT 求解器中,您可以将代码编写为条件列表,程序会找到最佳解决方案(或仅当 Z3 用作求解器时满足所有条件的解决方案)。

最初 SAT 求解器仅使用纯布尔表达式,但现代 SAT/SMT 求解器也允许使用固定位和无限的整数、分数、实数甚至函数作为中心变量。

为了将给定的方程写入 Z3,它们被完全转换为 Z3 表达式。下面的代码对每个步骤进行了说明。

import pandas as pd
from z3 import *

DesiredVolatility = 0.035
df = pd.DataFrame(columns=['Name', 'Volatility', 'Return'],
                  data=[['a', 0.0243, 0.212],
                        ['b', 0.0321, 0.431],
                        ['c', 0.0323, 0.443],
                        ['d', 0.0391, 0.2123],
                        ['e', 0.0433, 0.3123]])

# create a Z3 instance to optimize something
s = Optimize()
# the weight of each asset, as a Z3 variable
W = [Real(row.Name) for row in df.itertuples()]
# the total volatility
TotVol = Real('TotVol')
# the total return, to be maximized
TotReturn = Real('TotReturn')

# weights between 0 and 1, and sum to 1
s.add(And([And(w >= 0, w <= 1) for w in W]))
s.add(Sum([w for w in W]) == 1)
# the total return is calculated as the weighted sum of the asset returns
s.add(TotReturn == Sum([w * row.Return for w, row in zip(W, df.itertuples())]))
# the volatility is calculated as the weighted sum of the asset volatility
s.add(TotVol == Sum([w * row.Volatility for w, row in zip(W, df.itertuples())]))
# the volatility should be equal to the desired volatility
s.add(TotVol == DesiredVolatility)
# we're maximizing the total return
h1 = s.maximize(TotReturn)
# we ask Z3 to do its magick
res = s.check()
# we check the result, hoping for 'sat': all conditions satisfied, a maximum is found
if res == sat:
    s.upper(h1)
    m = s.model()
    #for w in W:
    #    print(f'asset {w}): {m[w]} = {m[w].numerator_as_long() / m[w] .denominator_as_long() : .6f}')
    # output the total return
    print(f'Total Return: {m[TotReturn]} = {m[TotReturn].numerator_as_long() / m[TotReturn] .denominator_as_long() :.6f}')
    # get the proportions out of the Z3 model
    proportions = [m[w].numerator_as_long() / m[w] .denominator_as_long() for w in W]
    # create a dataframe with the result
    df_result = pd.DataFrame({'Name': df.Name, 'Proportion': proportions})
    print(df_result)
else:
    print("No satisfiable solution found")

结果:

Total Return: 452011/1100000 = 0.410919
  Name  Proportion
0    a    0.000000
1    b    0.000000
2    c    0.754545
3    d    0.000000
4    e    0.245455

您可以轻松添加其他约束,例如“任何资产都不能超过总数的 30%”:

# change 
s.add(And([And(w >= 0, w <= 1) for w in W]))`
# to
s.add(And([And(w >= 0, w <= 0.3) for w in W]))`

这会导致:

Total Return: 558101/1480000 = 0.377095
  Name  Proportion
0    a    0.082432
1    b    0.300000
2    c    0.300000
3    d    0.017568
4    e    0.300000

【讨论】:

  • 有没有办法限制:No item can have more than 30% of the total allocation?
  • 当然,只需将s.add(And([And(w &gt;= 0, w &lt;= 1) for w in W])) 更改为s.add(And([And(w &gt;= 0, w &lt;= 0.3) for w in W]))。在这种情况下,我们得到 0.377 的回报和比例 [0.082, 0.3, 0.3, 0.018, 0.3]
猜你喜欢
  • 2016-03-12
  • 2016-08-18
  • 1970-01-01
  • 2018-03-17
  • 2016-07-19
  • 2014-07-28
  • 2021-12-18
  • 2023-01-13
  • 2017-11-14
相关资源
最近更新 更多