有效性前沿
在无数风险资产投资组合中,只要给出既定风险水平,我们总是可以找到收益最高的一个组合。当投资者可承受的风险水平变化时,与该风险水平相对应的最优投资组合A点也一直存在,并且随着风险水平变化不断的移动。这条由不同风险水平下最优投资组合A点构成的曲线,我们称之为有效曲线,又叫有效前沿。
有效性前沿python计算
输入项:
1、资产收益率irr
2、资产相关性矩阵(协方差):cov
3、随机种子:seed。随机生成的配置比例数量,随机种子越大计算越精确
4、目标收益:r_return。投资者期望获得的收益率。
5、无风险利率:rf。指的是10年期国债收益率或者同业拆借利率。
计算步骤:
1、首先随机生成配置比例,个数为随机种子(seed)。
对应的代码为def random_weight
2、依据随机生成的配置比例,计算目标收益(r_return)下的最优配置比例
对应的代码为def cal_random_weight
3、依据随机生成的配置比例,求有效性前沿最左侧的点,即为有效性前沿的起点。
对应的代码为 self.cal_left_point
4、依据所有的最优点,绘制有效性前沿
对应的代码为 self.cal_ff
5、计算市场组合的预期收益率,预期波动率
对应的代码为 self.cal_market_weight
6、依据无风险利率,及市场组合两点连线,求出资本市场线
python代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as sco
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文显示问题
plt.rcParams['axes.unicode_minus'] = False # 负数显示问题
class EffectiveForefront(object):
"""
有效性前沿类
"""
def __init__(self, irr, cov, seed, r_return, rf):
"""
有效性前沿初始化
:param irr: 收益率
:param cov: 协方差
:param seed: 随机种子数
:param r_return: 目标收益率
:param rf: 无风险利率
"""
self.irr = irr
self.rf = rf
self.cov = cov
self.seed = seed
self.r_return = r_return
self.plt = None
self.cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'eq', 'fun': lambda x: self.opt_return_vol(x)[0] - r_return}) # 输入最优解条件
self.bnds = tuple((0, 1) for x in range(len(irr))) # 输入边界条件
self.frist_weight = len(irr) * [1 / len(irr)] # 生成初始权重
def eff_handler(self):
"""
有效性前沿计算流程
:return: self.plt 有效性前沿及资本市场线
:return: opt_w 目标收益率下资产配置比例
"""
self.plt = self.random_weight()
opt_w = self.cal_random_weight()
left_point_return, left_point_vol = self.cal_left_point()
self.plt.plot(left_point_vol, left_point_return, 'y*', markersize=14)
self.cal_ff(left_point_return)
rm, vm, slope = self.cal_market_weight()
rp_cml, vp_cml = self.cal_cml(slope)
self.plt.plot(vp_cml, rp_cml, 'b--')
self.plt.plot(vm, rm, 'g*', markersize=14)
self.plt.show()
return self.plt, opt_w
def cal_cml(self, slope):
"""
计算资本市场线
:param slope: 计算资本市场线斜率
:return:
"""
rp_cml = np.linspace(0.02, 0.25)
vp_cml = (rp_cml - self.rf) / slope
return rp_cml, vp_cml
def cal_market_weight(self):
"""
市场组合的预期收益率,预期波动率
:return:
"""
# 仅设定权重之和为1的约束条件
cons_sr = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
# 求解最优权重
result_sr = sco.minimize(self.srmin_f, self.frist_weight, method='SLSQP', bounds=self.bnds, constraints=cons_sr)
slope = -result_sr['fun'] # 即资本市场线斜率
# 市场组合的预期收益率
rm = np.sum(irr * result_sr['x']) # 市场组合的收益率
# 市场组合的波动率
vm = (rm - self.rf) / slope # 市场组合的波动率
return rm, vm, slope
def cal_ff(self, left_point_return):
"""
求解有效前沿
:param left_point_return: 最左侧点收益率
:param left_point_vol: 最左侧点波动率
:return:
"""
rp_target = np.linspace(left_point_return, 0.25, 100)
# 设定波动率列表
vp_target = []
for r in rp_target:
cons_new = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'eq', 'fun': lambda x: self.opt_return_vol(x)[0] - r})
result_new = sco.minimize(self.vmin_f, self.frist_weight, method='SLSQP',
bounds=self.bnds, constraints=cons_new)
vp_target.append(result_new['fun'])
self.plt.plot(vp_target, rp_target, 'r-')
def cal_random_weight(self):
"""
计算最有结果权重
:return:
"""
# 求最值
result = sco.minimize(self.vmin_f, self.frist_weight, method='SLSQP', bounds=self.bnds, constraints=self.cons)
# 获取最优化结果的权重
result['x'].round(4)
w_df = pd.DataFrame()
w_df['asset'] = list(self.cov)
w_df['weight'] = result['x']
return w_df
def random_weight(self):
"""
随机生成配置比例
:return:
"""
rp_list = []
vp_list = []
# 设定投资组合各产品的均值
for i in np.arange(self.seed):
x = np.random.random(5)
weights = x / sum(x)
rp_list.append(np.sum(weights * self.irr))
vp_list.append(np.sqrt(np.dot(weights, np.dot(self.cov, weights.T))))
plt.figure(figsize=(8, 6))
plt.scatter(vp_list, rp_list)
plt.xlim(0.12, 0.28)
plt.ylim(-0.1, 0.2)
plt.xlabel('波动率')
plt.ylabel('预期收益率')
plt.grid('True')
return plt
def opt_return_vol(self, w):
"""
计算最优组合的预期收益率、收益波动率
:param w: 资产配置权重
:return:
"""
w = np.array(w)
rp_opt = np.sum(w * self.irr) # 计算最优投资组合的预期收益率
vp_opt = np.sqrt(np.dot(w, np.dot(self.cov, w.T))) # 计算最优投资组合的收益波动率
return np.array([rp_opt, vp_opt])
def vmin_f(self, w):
return self.opt_return_vol(w)[1]
def cal_left_point(self):
"""
求解有效前沿最左侧的点
:return:
"""
cons_vmin = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
result_vmin = sco.minimize(self.vmin_f, self.frist_weight, method='SLSQP',
bounds=self.bnds, constraints=cons_vmin)
# 求解投资组合的最左侧点的收益率
rp_vmin = np.sum(irr * result_vmin['x'])
# 求解投资组合的最左侧点的波动率
vp_vmin = result_vmin['fun']
return rp_vmin, vp_vmin
def srmin_f(self, w):
"""
最优投资组合夏普比率
:param w:
:return:
"""
w = np.array(w)
rp_opt = np.sum(w * irr)
vp_opt = np.sqrt(np.dot(w, np.dot(cov, w.T)))
SR = (rp_opt - self.rf) / vp_opt
return -np.array([rp_opt, vp_opt, SR])[2] # 求解最大的夏普比率就是求解负的最小值
if __name__ == '__main__':
irr = np.array([0.20, 0.08, -0.17, 0.06, -0.04])
cov = pd.DataFrame(
{'A': [0.09, 0.02, 0.02, 0.01, 0.02], 'B': [0.02, 0.12, 0.04, 0.02, 0.03], 'C': [0.02, 0.04, 0.07, 0.02, 0.03],
'D': [0.01, 0.02, 0.02, 0.04, 0.02], 'E': [0.02, 0.03, 0.03, 0.02, 0.04]})
seed = 10000
r_return = 0.1
rf = 0.02
ef = EffectiveForefront(irr, cov, seed, r_return, rf)
plt, opt_w = ef.eff_handler()
print('目标收益率下的配置比例为')
print(opt_w)
plt.show()
该算法的优点:
1、无需用到复杂的tensorflow模型,比较好调试参数
2、资产数较少的时候,计算非常准确。
缺点:
1、资产数较大的时候,要满足相同的精度,需要的随机种子数会指数级增加。在本案例中,五个资产用了一万随机种子。如果资产数增加为六,随机种子数要达到十万个。如果50个资产,随机种子数要达到10^50。不适合资产配置较多的投资组合。
2、大量随机生成的配置点离最优点非常远,浪费掉了。
附上python结果:
目标收益率下的配置比例为:
asset weight
0 A 0.310922
1 B 0.085937
2 C 0.000000
3 D 0.550663
4 E 0.052478