使用python绘制有效性前沿

有效性前沿
在无数风险资产投资组合中,只要给出既定风险水平,我们总是可以找到收益最高的一个组合。当投资者可承受的风险水平变化时,与该风险水平相对应的最优投资组合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

使用python绘制有效性前沿_第1张图片

你可能感兴趣的:(量化模型开发,python,numpy,开发语言)