如何在资产数量受限的情况下进行投资组合优化?

问题描述

我正尝试在一定的四个约束下将某项收益率的波动性降至最低。

  1. 每个资产的权重在0到1之间
  2. 所有权重的总和等于1
  3. 由资产组成的投资组合的回报等于给定的回报
  4. 5个资产中最多只能使用3个

我尝试用Scipy最小化这种方式:

cons_3 = ({'type': 'ineq','fun': lambda x: -np.count_nonzero(x) + 3},{'type': 'eq','fun': lambda x: statistics(x)[0] - tret_3},'fun': lambda x: np.sum(x) - 1})
x0 = [1/3,1/3,0]
res_3 = sco.minimize(min_func_port,x0,method='SLSQP',bounds=bnds_3,constraints=cons_3)

但是显然不可能。

我还阅读到可以使用Z3Py,但是我似乎找不到正确的编码方式。这是我到目前为止所发现的:

import pandas as pd
from z3 import *

DesiredReturn = 0.05
df = pd.DataFrame(columns=['Name','Return','Volatility'],data=[['Asset_1',0.01744,0.694149],['Asset_2',0.03818,0.475544],['Asset_3',0.08218,0.500724],['Asset_4',0.09818,0.489052],['Asset_5',0.04272,0.706223]])

W = [Real(row.Name) for row in df.itertuples()]
Vol = Real('Vol')
Ret = Real('Ret')
s = Optimize()
s.add(And([And(w >= 0,w <= 1) for w in W]))
s.add(Sum([w for w in W]) == 1)
#Missing constraint
s.add(Ret == Sum([w * row.Return for w,row in zip(W,df.itertuples())]))
s.add(Vol == Sum([w * row.Volatility for w,df.itertuples())]))
s.add(Ret == DesiredReturn)
h1 = s.minimize(Vol)
print(s.check())
print(s.model())

在此示例中,假设资产之间的相关性等于零。

非常感谢:)

解决方法

您的编码非常接近。下面是我将如何编码它。 (因为我没有安装熊猫,所以我避免了熊猫。)

from z3 import *

Data = [  ['Asset_1',0.01744,0.694149],['Asset_2',0.03818,0.475544],['Asset_3',0.08218,0.500724],['Asset_4',0.09818,0.489052],['Asset_5',0.04272,0.706223]
       ]

# Weights
W = [Real(row[0]) for row in Data]

# Return and Volatility
Ret = Sum([w * row[1] for (w,row) in zip(W,Data)])
Vol = Sum([w * row[2] for (w,Data)])

o = Optimize()

# 1. Every weight is between 0 and 1
o.add([And(w >= 0,w <= 1) for w in W])

# 2. They sum up to 1
o.add(Sum([w for w in W]) == 1)

# 3. Return is what was requested
DesiredReturn = 0.05
o.add(Ret == DesiredReturn)

# 4. Only a maximum of 3 out of the 5 assets are used
o.add(Sum([If(w == 0,1) for w in W]) <= 3)

# Want to minimize volatility
o.minimize(Vol)

r = o.check()
if r == sat:
    m = o.model()
    prec = 10
    for w in W:
         print("%s    = %s" % (w,m[w].as_decimal(prec)))
    print("Return     = %s" % m.evaluate(Ret).as_decimal(prec))
    print("Volatility = %s" % m.evaluate(Vol).as_decimal(prec))
else:
    print("solver said: %s" % r)

运行时,将打印:

Asset_1    = 0
Asset_2    = 0.803
Asset_3    = 0
Asset_4    = 0.197
Asset_5    = 0
Return     = 0.05
Volatility = 0.478205076