美文网首页量化投资Stata连享会-Python量化
CPPI和TIPP两种投资组合策略及python代码

CPPI和TIPP两种投资组合策略及python代码

作者: 学编程的电工_凡 | 来源:发表于2020-01-09 01:08 被阅读0次

    概要

    本文主要概述了两种投资组合策略的理论和提供了量化代码,CPPI和TIPP,网上目前不太能搜到python版的这两类量化代码,仅能找到matlab版的CPPI和TIPP的策略代码,因此专门制成了python版量化策略代码,供大家参考。
    由于之前做了几只股票的仿真后,感觉这两种策略在保本方面比其他风险性高的策略更具有稳健性,因此,本文提供了两类策略的概述和python代码,希望对大家有所帮助。

    CPPI

    概念

    固定比例投资组合保险策略

    公式

    A_t=D_t+E_t\\E_t=min\lbrace\ M*(A_t-F_t),A_t\rbrace\\A_t指t期总资产,D_t指无风险资产,\\E_t指风险资产,F_t指最低保险金额,M指投资乘数

    例子

    固定比例投资组合保险策略的具体操作方式与买入持有策略类似,运用此投资组合保险策略,就是投资者先决定乘数 M,并确定要保金额,再将要保金额值的现值投资于无风险资产,另将剩余的金额投资于风险资产。
    为了便于说明,下面我们举一个简单的例子:设投资人的起初总资产价值为100万,某风险资产组合的总市值也为100万,最低保险金额为80万,乘数大小为2。因此,期初以40万投资在风险资产组合上,作为主动性资产,其余60万投资在无风险资产上,作为保留资产。当风险资产组合的总市值从100%跌至80%时,此时投资人在风险资产组合上的投资值变为32万。此时投资人的资产总值为92万。根据CPPI的策略,此时投资人应投资在主动性资产上的仓位为24万。这时投资人在风险资产组合中的仓位减少8万。同理,当风险资产组合的总市值从100万升到120万,投资人投资在风险资产组合中的仓位从40万升到56万。


    CPPI_example.png

    代码

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    import warnings
    warnings.filterwarnings("ignore")
    
    
    def MaxDrawdown(return_list):
        '''最大回撤率'''
        i = np.argmax((np.maximum.accumulate(return_list) -
                       return_list) / np.maximum.accumulate(return_list))  # 结束位置
        if i == 0:
            return 0
        j = np.argmax(return_list[:i])  # 开始位置
        return (return_list[j] - return_list[i]) / (return_list[j])
    
    
    def calc_rate(day, rate, rate_type):
        if rate_type == 0:
            return (1 + rate*day)
        elif rate_type == 1:
            return np.exp(rate*day)
    
    
    name = 'zhongzheng500.xlsx'
    raw_data = pd.read_excel(name)[['日期', 'rate']]
    raw_data.rename(columns={'日期': 'day', 'rate': 'random_ret'}, inplace=True)
    #raw_data.drop(columns = '日净值',inplace = True)
    
    raw_data['day'] = pd.to_datetime(raw_data['day'], format='%Y%m%d')
    raw_data.set_index('day', inplace=True)
    # raw_data.drop(labels='Unnamed:0',axis=0,inplace=True)
    year = raw_data.resample('y')
    year.sum()  # 做一次无意义的运算  year才可以用来循环
    data = list()
    for i, j in year:
        # print(str(i.year))
        data.append(j)
    
    
    Return = list()
    for i in range(len(data)):
        # print(data[i].shape[0])
        test_num = 1
        rate_type = 0  # 0: simple  1:compound
    
        # 定义市场参数
        trading_year = 1
        trading_day_per_year = data[i].shape[0]
        days_per_calendar_year = 365
        rf = 0.04  # 无风险利率
        rf_daily = rf / trading_day_per_year
        trading_day_sim = trading_year * trading_day_per_year
        init_nav = 1e4  # 初始本金
        adj_period = 5  # 调整周期
        guarantee_rate = 0.8  # 保本比例
        risk_multipler = 2  # 风险乘数
        risk_trading_fee_rate = 6 / 1000  # 风险资产交易费率
        datashape = [trading_day_sim+1, test_num]
        risk_asset = np.zeros(datashape)  # 风险资产
        rf_asset = np.zeros(datashape)  # 无风险资产
        min_pv_asset = np.zeros(datashape)  # 价值底线
        nav = np.zeros(datashape)  # 总资产
        nav[1, :] = init_nav
    
        # CPPI策略
        # 第1天
        min_pv_asset[1, :] = guarantee_rate * init_nav / calc_rate(trading_day_sim, rf_daily, rate_type)  # 第1天的价值底线
        risk_asset[1, :] = np.maximum(np.zeros(
            test_num), risk_multipler * (nav[1, :] - min_pv_asset[1, :]))  # 风险资产 w/o fee
        rf_asset[1, :] = (nav[1, :] - risk_asset[1, :])  # 无风险资产
        risk_asset[1, :] = risk_asset[1, :] * (1 - risk_trading_fee_rate)  # 扣去手续费
        # 第二天到最后一天
        for t in range(2, trading_day_sim+1):
            min_pv_asset[t, :] = guarantee_rate * init_nav / calc_rate(trading_day_sim-t+1, rf_daily, rate_type)  # 价值底线
    
            risk_asset[t, :] = (1 + data[i].iloc[t-1]) * risk_asset[t-1, :]
            #risk_asset[t,:] = (1+random_ret[t-1,:]) * risk_asset[t-1,:]
            rf_asset[t, :] = calc_rate(1, rf_daily, rate_type) * rf_asset[t-1, :]
            nav[t, :] = risk_asset[t, :] + rf_asset[t, :]
    
            # 定期调整
            if np.mod(t-1, adj_period) == 0:
                risk_asset_b4_adj = risk_asset[t, :]
                risk_asset[t, :] = np.maximum(
                    np.zeros(test_num), risk_multipler * (nav[t, :] - min_pv_asset[t, :]))  # 风险资产
                rf_asset[t, :] = nav[t, :] - risk_asset[t, :]  # 无风险资产
                risk_asset[t, :] = risk_asset[t, :] - abs(risk_asset_b4_adj -
                        risk_asset[t, :]) * risk_trading_fee_rate  # 手续费
    
            # 检查是否被强制平仓
            rf_asset[t, risk_asset[t, :] <= 0] = nav[t, risk_asset[t, :] <=
                                                     0] - risk_asset[t, risk_asset[t, :] <= 0] * risk_trading_fee_rate
            risk_asset[t, risk_asset[t, :] <= 0] = 0
        Return.append(nav[1:, 0]/init_nav)
    
    annual_return = list()
    annual_volatility = list()
    Sharpe = list()
    Maxdrawdown = list()
    for l in range(10):
        df_return = pd.DataFrame(Return[l])
        annual_return.append(Return[l][len(Return[l])-1])
        volatility = (df_return.shift(1) - df_return)/df_return
        annual_volatility.append(float(volatility.std()*np.sqrt(trading_day_sim)))
        Sharpe.append((annual_return[l]-1)/annual_volatility[l])
        Maxdrawdown.append(float(df_return.apply(MaxDrawdown, axis=0)))
    
    Results = pd.DataFrame()
    Results['annual_return'] = annual_return
    Results['annual_volatility'] = annual_volatility
    Results['Sharpe'] = Sharpe
    Results['Maxdrawdown'] = Maxdrawdown
    print(Results)
    # Results.to_excel(name+'results1.xlsx')
    
    # 绘每期收益图
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    # plt.title('净值')
    plt.xlabel('时间(天)')
    plt.ylabel('净值(元)')
    plt.plot(range(1, len(nav)), nav[1:])
    

    TIPP

    概念

    时间不变性投资组合保险策略。

    TIPP策略,把保险额度从固定改为可变,将保险额度和资产净值挂钩,保险额度为某一时点资产净值最高的一个固定比例。当资产净值变动时,即可求出新的保险额度,将新的保险额度与原来的保险额度相比较,取其中较大的作为资产净值变动后的保险额度。

    公式

    A_t=D_t+E_t\\F_t=max\lbrace\ A_t*f,F_{t-1}\rbrace\\E_t=m*(A_t-F_t)\\A_t指t期总资产,D_t指无风险资产,f指固定的要保比率\\E_t指风险资产,F_t指t期最低保险金额,m指投资乘数

    例子

    说明图.png

    代码

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    def MaxDrawdown(return_list):
        '''最大回撤率'''
        i = np.argmax((np.maximum.accumulate(return_list) - return_list) / np.maximum.accumulate(return_list))  # 记录结束位置的下标(最小值的下标)
        if i == 0:
            return 0
        j = np.argmax(return_list[:i])  # 开始位置(最大值的下标)
        return ((return_list[j] - return_list[i]) / (return_list[j]))
    
    def calc_rate(day, rate, rate_type):
        if rate_type==0:
            return (1 + rate*day)
        elif rate_type==1:
            return np.exp(rate*day) 
    
    
    name = 'zhongzheng500.xlsx'
    raw_data = pd.read_excel(name)[['日期','rate']]
    raw_data.rename(columns = {'日期':'day','rate':'random_ret'}, inplace = True)    
    
    raw_data['day'] = pd.to_datetime(raw_data['day'],format = '%Y%m%d')
    raw_data.set_index('day',inplace = True)
    year = raw_data.resample('y')
    year.sum()  #做一次无意义的运算  year才可以用来循环
    data = list()
    for i,j in year:
        data.append(j)
        
    Return = list()
    for i in range(len(data)):
        rate_type=0  # 0为单利,1为复利 
        test_num = 1 # 测试频次
        
        #定义市场参数
        trading_year = 1
        trading_day_per_year = data[i].shape[0]
        #days_per_calendar_year = 365
        rf = 0.04       #无风险利率
        rf_daily = rf / trading_day_per_year
        trading_day_sim = trading_year * trading_day_per_year
        
        
        # 定义TIPP参数
        is_take_profit = 0
        init_nav = 1e4 # 初始本金
        adj_period = 5 # 调整周期
        guarantee_rate = 0.8 # 保本比例
        risk_multipler = 2 # 风险乘数
        risk_trading_fee_rate = 6 / 1000 # 风险资产交易手续费率
        gaurant_adj_thresh = 0.03 # 一旦收益超过上一个保本率的 1+百分之几
        gaurant_inc = 0.02 # 就进一步提高保本率
        take_profit_thresh = 0.12
        gaurant_inc_counter = 0
        
        risk_asset = np.zeros(trading_day_sim) # 风险资产
        rf_asset = np.zeros(trading_day_sim) # 无风险资产
        min_pv_asset = np.zeros(trading_day_sim) # 价值底线
        nav = np.zeros(trading_day_sim ) # 总资产
        nav[1] = init_nav
        
        # TIPP策略
        #第1天
        min_pv_asset[1] = guarantee_rate * init_nav /calc_rate(trading_day_sim, rf_daily, rate_type)# 第1天的价值底线
        risk_asset[1] = max ( 0,risk_multipler * (nav[1] - min_pv_asset[1]))   # 风险资产 w/o fee
        rf_asset[1] = (nav[1] - risk_asset[1]) # 无风险资产
        risk_asset[1] = risk_asset[1] * (1 - risk_trading_fee_rate)  # 扣去手续费
        # 第2天到最后1天
        for t in range(2, trading_day_sim ):
            # 未止盈
            if is_take_profit == 0:
                #检查是否已经可以止盈
                fv = nav[t -1] * calc_rate(trading_day_sim - t ,rf_daily,rate_type) - risk_trading_fee_rate * risk_asset[t-1] #去除所有的手续费后,看看是否满足止盈条件
                if fv/init_nav - 1 > take_profit_thresh: # 止盈
                    risk_asset[t] = 0
                    rf_asset[t] = rf_asset[t-1] * calc_rate(1, rf_daily, rate_type) + (1 - risk_trading_fee_rate) * risk_asset[t - 1]
                    is_take_profit = 1
                    nav[t] = rf_asset[t]
                else: #没有止盈 
                    #如果已实现收益,提高保本额度
                    if nav[ t -1] / init_nav > guarantee_rate * (gaurant_adj_thresh +1) :
                        guarantee_rate = guarantee_rate + gaurant_inc
                        gaurant_inc_counter = gaurant_inc_counter + 1
                    
                    min_pv_asset[t] = guarantee_rate * init_nav /calc_rate(trading_day_sim -t + 1, rf_daily, rate_type)  #价值底线
                    risk_asset[t] = (1 +data[i].iloc[t-1]) * risk_asset[t-1]     
                    #risk_asset[t] = (1 + random_ret[0][t-1] ) * risk_asset[t -1]
                    rf_asset[t] = calc_rate(1, rf_daily, rate_type)* rf_asset[t -1]
                    nav[t] = risk_asset[t] + rf_asset[t]
                    
                    #定期调整
                    if (t -1) % adj_period == 0:
                        risk_asset_b4_adj = risk_asset[t]
                        risk_asset[t] = max(0,risk_multipler * (nav[t] - min_pv_asset[t])) #风险资产
                        rf_asset[t] = nav[t] - risk_asset[t] #无风险资产
                        trade_value = risk_asset_b4_adj - risk_asset[t]
                        risk_asset[t] = risk_asset[t] - abs(trade_value) * risk_trading_fee_rate #手续费
                    
                    #检查是否被强制平仓
                    if risk_asset[t] <= 0:
                        rf_asset[t] = nav[t] - risk_asset[t] * risk_trading_fee_rate
                        risk_asset[t] = 0
            else:
                # 止盈
                rf_asset[t] = rf_asset[t -1] *calc_rate(1, rf_daily, rate_type)
                nav[t] = rf_asset[t]
        Return.append(nav[1:] /init_nav)
    
    if rate_type ==0 :
        print('单利计算')
    elif rate_type ==1:
        print('复利计算')
    
    annual_return = list()
    annual_volatility = list()
    Sharpe = list()
    Maxdrawdown = list()
    for k in range(10): #10年期间
        df_return = pd.DataFrame(Return[k])
        annual_return.append(Return[k][len(Return[k])-1]) #记录年末收益
        volatility = (df_return.shift(1) - df_return)/df_return #shift时间序列往后推一个
        annual_volatility.append(float(volatility.std()*np.sqrt(trading_day_sim)))
        Sharpe.append((annual_return[k]-1)/annual_volatility[k])
        Maxdrawdown.append(float(df_return.apply(MaxDrawdown)))
    
    Results = pd.DataFrame()
    Results['annual_return'] = annual_return
    Results['annual_volatility'] = annual_volatility
    Results['Sharpe'] = Sharpe
    Results['Maxdrawdown'] = Maxdrawdown
    print(Results)
    
    # 绘每期收益图
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    # plt.title('净值')
    plt.xlabel('时间(天)')
    plt.ylabel('净值(元)')
    plt.plot(range(1, len(nav)), nav[1:])
    

    三种结果对比(以中证500指数为例子)

    对比下列三类曲线,可以对两类策略有一个更直观的了解,CPPI和TIPP的保本比例均设为0.8,其他条件均一致

    原始变化


    中证500_20100105-20191107.png

    CPPI


    中证500_CPPI.png

    TIPP


    中证500_TIPP.png

    结语

    本文两类策略的代码是本人与本人的同事rrq以及领导yhl先生的指导下共同完成,感谢二人对本人给予的支持与帮助。

    希望上面策略和代码对后人有所帮助。

    对应文件可在github上下载:https://github.com/David-xyf/CPPI-TIPP

    相关文章

      网友评论

        本文标题:CPPI和TIPP两种投资组合策略及python代码

        本文链接:https://www.haomeiwen.com/subject/ximeactx.html