问题描述
我正尝试在一定的四个约束下将某项收益率的波动性降至最低。
- 每个资产的权重在0到1之间
- 所有权重的总和等于1
- 由资产组成的投资组合的回报等于给定的回报
- 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