文章目录
- 一、读取excel文件,获取收益率数据
- 二、给定权重,求组合收益率、标准差、夏普比率
- 三、规划求解
- (一)求解夏普比率最高的投资组合,并且单个资产权重均不超过20%
- (二)求解方差最小的投资组合
- (三)求解方差最小的投资组合,组合收益率为30%
- (四)求解收益率最高的投资组合,组合标准差为40%。
问题导入
已知无风险利率为3%,下表包括六家公司的年度回报(文件),使用Python编制程序,求解最优投资组合。
Year | BMW | ORACLE | YOIGO | SACYR | BALAY | APPLE |
2011 | 1.4866 | 0.0562 | 0.9641 | -1.2367 | 0.0512 | 0.5075 |
2012 | 0.2902 | 0.4356 | 0.2111 | 4.0332 | 0.2491 | 0.4014 |
2013 | 0.1911 | 0.8831 | 0.3746 | 0.3813 | 0.1815 | -1.6639 |
2014 | 0.1288 | 0.3643 | -0.2525 | 0.0666 | 0.2566 | 0.1564 |
2015 | -0.0850 | 0.1460 | 0.4236 | 0.4114 | 0.4718 | 0.2199 |
2016 | 0.1068 | 0.0836 | 0.1333 | -0.9241 | 0.3121 | -0.0216 |
2017 | -0.0040 | -0.6284 | -0.3053 | 3.4962 | -0.0368 | -0.0636 |
2018 | 0.0726 | 0.4273 | -0.2470 | 0.4870 | 0.1204 | -0.2773 |
2019 | -0.2030 | -0.2196 | 0.1404 | 0.0862 | -0.3052 | -0.2985 |
2020 | -0.0437 | 0.0681 | -0.0756 | -1.1594 | -0.1501 | 3.0549 |
首先我们导入相关库。
import pandas as pd
import numpy as np
import scipy.optimize as sco
一、读取excel文件,获取收益率数据
data = pd.read_excel(r'C:\Users\mi\Desktop\收益率.xlsx',index_col=0)
>>>data
BMW ORACLE YOIGO SACYR BALAY APPLE
Year
2011 1.4866 0.0562 0.9641 -1.2367 0.0512 0.5075
2012 0.2902 0.4356 0.2111 4.0332 0.2491 0.4014
2013 0.1911 0.8831 0.3746 0.3813 0.1815 -1.6639
2014 0.1288 0.3643 -0.2525 0.0666 0.2566 0.1564
2015 -0.0850 0.1460 0.4236 0.4114 0.4718 0.2199
2016 0.1068 0.0836 0.1333 -0.9241 0.3121 -0.0216
2017 -0.0040 -0.6284 -0.3053 3.4962 -0.0368 -0.0636
2018 0.0726 0.4273 -0.2470 0.4870 0.1204 -0.2773
2019 -0.2030 -0.2196 0.1404 0.0862 -0.3052 -0.2985
2020 -0.0437 0.0681 -0.0756 -1.1594 -0.1501 3.0549
二、给定权重,求组合收益率、标准差、夏普比率
# 定义无风险收益率
rf = 0.03
# 获取股票平均收益率
returns = data.mean()
# 获取股票收益率的方差协方差矩阵
cov = data.cov(bias=True)
# 定义资产数量
number_assets = 6
# 给定权重,求组合收益率、标准差、夏普比率
def statistics(weights):
weights = np.array(weights)
pret = np.dot(weights, returns) #获取组合收益率
pvol = np.sqrt(np.dot(weights.T, np.dot(cov, weights))) #获取组合标准差
psharpe = (pret - rf) / pvol #获取组合夏普比率
return np.array([pret, pvol, psharpe])
三、规划求解
规划求解需要用到scipy.optimize的minimize函数。函数用法如下所示。
scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)
主要参数如下表所示:
参数 | 说明 |
fun | 目标函数 |
x0 | 初始迭代值 |
args | 要输入到目标函数中的参数 |
method | 求解的算法,目前可选的有Nelder-Mead,Powell,CG,BFGS,Newton-CG,L-BFGS-B,TNC,COBYLA,SLSQP,dogleg,trust-ncg等。一般求极值多用 'SLSQP’算法 |
bounds | 可选项,变量的边界(仅适用于L-BFGS-B,TNC和SLSQP)。以(min,max)对的形式定义 x 中每个元素的边界。如果某个参数在 min 或者 max 的一个方向上没有边界,则用 None 标识。如(None, max) |
constraints | 约束条件(只对 COBYLA 和 SLSQP)。dict 类型。type : str, ‘eq’ 表示等于0,‘ineq’ 表示不小于0 ;fun : 定义约束的目标函数。 |
(一)求解夏普比率最高的投资组合,并且单个资产权重均不超过20%
目标函数fun:夏普比率最大化
约束条件constraints:权重之和=1
变量边界bounds:单个权重不超过0.2
代码如下:
# 设置目标:夏普比率最大
def minus_sharpe(x):
return -statistics(x)[2]
# 约束条件分为eq和ineq
# eq表示函数结果等于0 ;ineq 表示 表达式大于等于0
# 约束条件:权重之和=1
def constraint1(x):
return np.sum(x) -1
con1 = {'type': 'eq', 'fun': constraint1}
# 定义边界约束(优化变量的上下限)
bnds = tuple((None, 0.2) for x in range(number_assets)) #每个资产权重取值范围为0-0.2
# 设置初始权重
x0 = number_assets * [1 / number_assets]
# 规划求解
solution = sco.minimize(minus_sharpe,x0,method='SLSQP', bounds=bnds, constraints=con1)
# 求解出权重、组合收益率、标准差、夏普比率
final_weights = solution.x #权重
finanl_ratio = statistics(final_weights) #组合收益率、标准差、夏普比率
print(f'{final_weights[0]:.1%}投资于{data.columns[0]},{final_weights[1]:.1%}投资于{data.columns[1]},'
f'{final_weights[2]:.1%}投资于{data.columns[2]},{final_weights[3]:.1%}投资于{data.columns[3]},'
f'{final_weights[4]:.1%}投资于{data.columns[4]},{final_weights[5]:.1%}投资于{data.columns[5]},'
f'组合收益率:{finanl_ratio[0]:.1%},组合标准差:{finanl_ratio[1]:.1%},组合夏普比率:{finanl_ratio[2]:.1%}')
结果如下:
18.5%投资于BMW,20.0%投资于ORACLE,20.0%投资于YOIGO,9.7%投资于SACYR,20.0%投资于BALAY,11.7%投资于APPLE,组合收益率:19.7%,组合标准差:22.3%,组合夏普比率:75.0%
(二)求解方差最小的投资组合
目标函数fun:方差最小化,即标准差最小化
约束条件constraints:权重之和=1
变量边界bounds:无
代码如下:
# 设置目标:方差最小
def min_std(x):
return statistics(x)[1]
# 约束条件:权重之和=1
def constraint1(x):
return np.sum(x) -1
con1 = {'type': 'eq', 'fun': constraint1}
# 设置初始权重
x0 = number_assets * [1 / number_assets]
# 规划求解
solution = sco.minimize(min_std,x0,method='SLSQP',constraints=con1)
# 求解出权重、组合收益率、标准差、夏普比率
final_weights = solution.x #权重
finanl_ratio = statistics(final_weights) #组合收益率、标准差、夏普比率
print(f'{final_weights[0]:.1%}投资于{data.columns[0]},{final_weights[1]:.1%}投资于{data.columns[1]},'
f'{final_weights[2]:.1%}投资于{data.columns[2]},{final_weights[3]:.1%}投资于{data.columns[3]},'
f'{final_weights[4]:.1%}投资于{data.columns[4]},{final_weights[5]:.1%}投资于{data.columns[5]},'
f'组合收益率:{finanl_ratio[0]:.1%},组合标准差:{finanl_ratio[1]:.1%},组合夏普比率:{finanl_ratio[2]:.1%}')
结果如下:
3.6%投资于BMW,10.1%投资于ORACLE,18.5%投资于YOIGO,3.4%投资于SACYR,56.0%投资于BALAY,8.4%投资于APPLE,组合收益率:14.9%,组合标准差:18.1%,组合夏普比率:65.9%
(三)求解方差最小的投资组合,组合收益率为30%
目标函数fun:方差最小化,即标准差最小化
约束条件constraints:1、权重之和=1;2、组合收益率=0.3
代码如下:
#设置目标:方差最小
def min_std(x):
return statistics(x)[1]
# 约束条件1:权重之和=1
def constraint1(x):
return np.sum(x) -1
# 约束条件2:组合收益=0.3
def constraint2(x):
return statistics(x)[0]-0.3
con1 = {'type': 'eq', 'fun': constraint1}
con2 = {'type': 'eq', 'fun': constraint2}
cons = (con1,con2)
# 设置初始权重
x0 = number_assets * [1 / number_assets]
# 规划求解
solution = sco.minimize(min_std,x0,method='SLSQP',constraints=cons)
# 求解出权重、组合收益率、标准差、夏普比率
final_weights = solution.x #权重
finanl_ratio = statistics(final_weights) #组合收益率、标准差、夏普比率
print(f'{final_weights[0]:.1%}投资于{data.columns[0]},{final_weights[1]:.1%}投资于{data.columns[1]},'
f'{final_weights[2]:.1%}投资于{data.columns[2]},{final_weights[3]:.1%}投资于{data.columns[3]},'
f'{final_weights[4]:.1%}投资于{data.columns[4]},{final_weights[5]:.1%}投资于{data.columns[5]},'
f'组合收益率:{finanl_ratio[0]:.1%},组合标准差:{finanl_ratio[1]:.1%},组合夏普比率:{finanl_ratio[2]:.1%}')
结果如下:
47.1%投资于BMW,66.5%投资于ORACLE,2.8%投资于YOIGO,22.3%投资于SACYR,-57.0%投资于BALAY,18.3%投资于APPLE,组合收益率:30.0%,组合标准差:42.9%,组合夏普比率:63.0%
(四)求解收益率最高的投资组合,组合标准差为40%。
目标函数fun:组合收益率最大化
约束条件constraints:1、权重之和=1;2、组合标准差=0.4
代码如下:
# 设置目标:收益率最大化
def minus_return(x):
return -statistics(x)[0]
# 约束条件1:权重之和=1
def constraint1(x):
return np.sum(x) -1
# 约束条件2:组合标准差=0.4
def constraint2(x):
return statistics(x)[1]-0.4
con1 = {'type': 'eq', 'fun': constraint1}
con2 = {'type': 'eq', 'fun': constraint2}
cons = (con1,con2)
# 设置初始权重
x0 = number_assets * [1 / number_assets]
# 规划求解
solution = sco.minimize(minus_return,x0,method='SLSQP',constraints=cons)
# 求解出权重、组合收益率、标准差、夏普比率
final_weights = solution.x #权重
finanl_ratio = statistics(final_weights) #组合收益率、标准差、夏普比率
print(f'{final_weights[0]:.1%}投资于{data.columns[0]},{final_weights[1]:.1%}投资于{data.columns[1]},'
f'{final_weights[2]:.1%}投资于{data.columns[2]},{final_weights[3]:.1%}投资于{data.columns[3]},'
f'{final_weights[4]:.1%}投资于{data.columns[4]},{final_weights[5]:.1%}投资于{data.columns[5]},'
f'组合收益率:{finanl_ratio[0]:.1%},组合标准差:{finanl_ratio[1]:.1%},组合夏普比率:{finanl_ratio[2]:.1%}')
结果如下:
43.5%投资于BMW,61.8%投资于ORACLE,4.1%投资于YOIGO,20.8%投资于SACYR,-47.7%投资于BALAY,17.5%投资于APPLE,组合收益率:28.8%,组合标准差:40.0%,组合夏普比率:64.4%