投资组合--最优化求解(Python)

除了用蒙特卡洛模拟进行投资组合求解外,也可以通过python的scipy.optimize库进行最优化求解。接着上一篇文章,我们继续使用scipy.optimize进行投资组合最优化求解工作。

import scipy.optimize as sco

# 定义计算投资组合的状态函数如下
def portfolio_status(weights):
    weights = array(weights)[:,newaxis]
    port_rets = weights.T @ array(returns.mean() * 250)[:,newaxis]
    port_vols = sqrt(multi_dot([weights.T, returns.cov()*250, weights]))
    # 返回组合收益率,组合波动率和夏普比率
    return array([port_rets, port_vols, port_rets/port_vols]).flatten()

用最优化module求解最大夏普比率对应的组合

# 定义夏普比率(取负号)函数
def sharpe_ratio(weights):
    return -portfolio_status(weights)[2]  #取return结果中的第3个数

# 设置每一只股票权重范围为0<=x<=1
tuple((0,1) for x in range(numofasset))

# 明确组合最优化约束条件和股票权重范围
cons = ({'type':'eq','fun': lambda x: sum(x)-1})
bnds = tuple((0,1) for x in range(numofasset))
initial_wts = numofasset*[1./numofasset]  #初始化weights的值便于优化算法快速完成计算

# 用最优化module求解最大夏普比率,opt_sharpe(目标函数, 入参, 优化方法, 边界条件, 约束条件)
opt_sharpe = sco.minimize(sharpe_ratio, initial_wts, method='SLSQP',
                         bounds = bnds, constraints = cons)
opt_sharpe

# 最大夏普比率组合权重
list(zip(symbols, around(opt_sharpe['x']*100, 2)))  #两个list对应元素生成一个tuple

# 最大夏普比率组合数
status = ['Returns', 'Volatility', 'Sharpe Ratio']
list(zip(status, around(portfolio_status(opt_sharpe['x']), 4)))

用最优化module求解最小波动率组合

# 定义波动率(方差)函数
def variance(weights):
    return portfolio_status(weights)[1]**2

# 用最优化module求解最小方差
opt_var = sco.minimize(variance, initial_wts, method='SLSQP',
                         bounds = bnds, constraints = cons)
opt_var

# 最小方差组合权重
list(zip(symbols, around(opt_var['x']*100,2)))

# status = ['Returns', 'Volatility', 'Sharp Ratio']
list(zip(status, around(portfolio_status(opt_var['x']),4)))

用最优化module求解有效边界(effective frontier)

# 定义波动率(标准差)函数
def volatility(weights):
    return portfolio_status(weights)[1]  #portfolio_status(weights)函数返回列表第2个元素为vol

# 设定有效边界参数
targetrets = linspace(0.04, 0.42, 500)  # 将目标收益率进行500等分
targetvols_ = []

for i in tqdm(targetrets):
    ef_cons = ({'type':'eq','fun': lambda x: sum(x)-1},
              {'type':'eq','fun': lambda x: portfolio_status(x)[0]-i})  #两个约束条件
    opt_ef = sco.minimize(volatility, initial_wts, method='SLSQP',
                         bounds=bnds, constraints=ef_cons)
    targetvols_.append(opt_ef['fun'])
    
targetvols = array(targetvols_)

# 记录有效边界数据到DataFrame
ef_port = pd.DataFrame({
    'targetrets': around(targetrets*100, 2),
    'targetvols': around(targetvols*100, 2),
    'targetsharpe': around(targetrets/targetvols, 2)
    })

ef_port.head()

# 有效边界画图
fig = px.scatter(
        ef_port, x='targetvols', y='targetrets', color='targetsharpe',
        labels={'targetrets': '预期收益率', 'targetvols': '预期波动率',
               'targetsharpe': '夏普比率'},
        title='投资组合有效边界'
        ).update_traces(mode='markers', marker=dict(symbol='circle'))

# 标出最大夏普比率组合(市场组合)
fig.add_scatter(mode='markers',
                x=[100*portfolio_status(opt_sharpe['x'])[1]],  #[1]为vols
                y=[100*portfolio_status(opt_sharpe['x'])[0]],  #[0]为rets
                marker=dict(color='Blue', size=14, symbol='square'),
                name = '市场组合'
               ).update(layout_showlegend=False)

# 标出最小方差(波动率)组合
fig.add_scatter(mode='markers',
                x=[100*portfolio_status(opt_var['x'])[1]],  #[1]为vols
                y=[100*portfolio_status(opt_var['x'])[0]],  #[0]为rets
                marker=dict(color='Red', size=14, symbol='square'),
                name = '最小波动率组合'
               ).update(layout_showlegend=False)

fig.update_xaxes(showspikes=True)
fig.update_yaxes(showspikes=True)

最后可视化结果如下其中红色方块和蓝色方块分别为最小波动率组合和最大夏普比率组合所在的位置

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值