美文网首页量化投资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