【发布时间】:2013-08-15 14:46:58
【问题描述】:
我正在尝试执行投资组合优化,以返回最大化我的效用函数的权重。我可以很好地完成这部分,包括权重总和为 1 并且权重也给我一个目标风险的约束。我还包括了 [0
def rebalance(PortValue, port_rets, risk_tgt):
#convert continuously compounded returns to simple returns
Rt = np.exp(port_rets) - 1
covar = Rt.cov()
def fitness(W):
port_Rt = np.dot(Rt, W)
port_rt = np.log(1 + port_Rt)
q95 = Series(port_rt).quantile(.05)
cVaR = (port_rt[port_rt < q95] * sqrt(20)).mean() * PortValue
mean_cVaR = (PortValue * (port_rt.mean() * 20)) / cVaR
return -1 * mean_cVaR
def solve_weights(W):
import scipy.optimize as opt
b_ = [(0.0, 1.0) for i in Rt.columns]
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1},
{'type':'eq', 'fun': lambda W: sqrt(np.dot(W, np.dot(covar, W))\
* 252) - risk_tgt})
optimized = opt.minimize(fitness, W, method='SLSQP', constraints=c_, bounds=b_)
if not optimized.success:
raise BaseException(optimized.message)
return optimized.x # Return optimized weights
init_weights = Rt.ix[1].copy()
init_weights.ix[:] = np.ones(len(Rt.columns)) / len(Rt.columns)
return solve_weights(init_weights)
现在我可以深入研究这个问题,我将权重存储在 MultIndex 熊猫系列中,这样每个资产都是对应于资产类别的 ETF。当打印出等重的投资组合时,如下所示:
Out[263]:净值 CZA 0.045455
IWM 0.045455
间谍 0.045455
intl_equity EWA 0.045455
EWO 0.045455
IEV 0.045455
债券 IEF 0.045455
害羞 0.045455
TLT 0.045455
intl_bond BWX 0.045455
BWZ 0.045455
IGOV 0.045455
商品 DBA 0.045455
DBB 0.045455
DBE 0.045455
pe ARCC 0.045455
BX 0.045455
PSP 0.045455
高频 DXJ 0.045455
SRV 0.045455
现金 BIL 0.045455
GSY 0.045455
名称:2009-05-15 00:00:00,数据类型:float64
如何添加额外的界限要求,以便当我将这些数据组合在一起时,权重总和落在我为该资产类别预先确定的分配范围之间?
所以具体来说,我想包括一个额外的边界,这样
init_weights.groupby(level=0, axis=0).sum()
Out[264]:净值 0.136364 国际股权 0.136364 债券 0.136364 国际债券 0.136364 商品 0.136364 体育 0.136364 高频 0.090909 现金 0.090909 数据类型:float64
在这些范围内
[(.08,.51), (.05,.21), (.05,.41), (.05,.41), (.2,.66), (0,.16), (0,.76), (0,.11)]
[更新] 我想我会用一个我不太满意的笨拙的伪解决方案来展示我的进步。即因为它不是使用整个数据集来解决权重,而是使用资产类别来解决资产类别。另一个问题是它返回的是序列而不是权重,但我相信比我自己更合适的人可以提供一些关于 groupby 函数的见解。
因此,对我的初始代码进行轻微调整后,我有:
PortValue = 100000
model = DataFrame(np.array([.08,.12,.05,.05,.65,0,0,.05]), index= port_idx, columns = ['strategic'])
model['tactical'] = [(.08,.51), (.05,.21),(.05,.41),(.05,.41), (.2,.66), (0,.16), (0,.76), (0,.11)]
def fitness(W, Rt):
port_Rt = np.dot(Rt, W)
port_rt = np.log(1 + port_Rt)
q95 = Series(port_rt).quantile(.05)
cVaR = (port_rt[port_rt < q95] * sqrt(20)).mean() * PortValue
mean_cVaR = (PortValue * (port_rt.mean() * 20)) / cVaR
return -1 * mean_cVaR
def solve_weights(Rt, b_= None):
import scipy.optimize as opt
if b_ is None:
b_ = [(0.0, 1.0) for i in Rt.columns]
W = np.ones(len(Rt.columns))/len(Rt.columns)
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1})
optimized = opt.minimize(fitness, W, args=[Rt], method='SLSQP', constraints=c_, bounds=b_)
if not optimized.success:
raise ValueError(optimized.message)
return optimized.x # Return optimized weights
下面的单行将返回一些优化的系列
port = np.dot(port_rets.groupby(level=0, axis=1).agg(lambda x: np.dot(x,solve_weights(x))),\
solve_weights(port_rets.groupby(level=0, axis=1).agg(lambda x: np.dot(x,solve_weights(x))), \
list(model['tactical'].values)))
Series(port, name='portfolio').cumsum().plot()
[更新 2]
以下更改将返回受约束的权重,尽管仍然不是最优的,因为它在组成资产类别上进行了分解和优化,因此当考虑目标风险的约束时,只有初始协方差矩阵的折叠版本可用
def solve_weights(Rt, b_ = None):
W = np.ones(len(Rt.columns)) / len(Rt.columns)
if b_ is None:
b_ = [(0.01, 1.0) for i in Rt.columns]
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1})
else:
covar = Rt.cov()
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1},
{'type':'eq', 'fun': lambda W: sqrt(np.dot(W, np.dot(covar, W)) * 252) - risk_tgt})
optimized = opt.minimize(fitness, W, args = [Rt], method='SLSQP', constraints=c_, bounds=b_)
if not optimized.success:
raise ValueError(optimized.message)
return optimized.x # Return optimized weights
class_cont = Rt.ix[0].copy()
class_cont.ix[:] = np.around(np.hstack(Rt.groupby(axis=1, level=0).apply(solve_weights).values),3)
scalars = class_cont.groupby(level=0).sum()
scalars.ix[:] = np.around(solve_weights((class_cont * port_rets).groupby(level=0, axis=1).sum(), list(model['tactical'].values)),3)
return class_cont.groupby(level=0).transform(lambda x: x * scalars[x.name])
【问题讨论】:
-
不确定我是否理解问题所在。您想检查该值是否在提供的范围内并返回一个布尔 True/False 数组,还是想以这些范围表示的类别表示您的值?
-
我正在尝试使用 scipy.opt 给我返回符合单独绑定到 (0,1) 的权重,然后也让分组的类权重受我显示的限制的约束以上。
-
对于问题的第一部分:等待不是已经有界了吗?您将
bounds参数正确地提供给minimize方法,所以它应该返回一个有界权重列表? -
是的,所有的权重都在 (0,1) 之间。现在我想添加第二个键
-
出于说明的目的,假设我要使用伪代码中的约束来执行此操作: c_ = ({'type':'eq', 'fun': lambda W: sum(W ) - 1}, {'type':'eq', 'fun': lambda W: .08
标签: python optimization pandas scipy finance