概要
本文主要概述了两种投资组合策略的理论和提供了量化代码,CPPI和TIPP,网上目前不太能搜到python版的这两类量化代码,仅能找到matlab版的CPPI和TIPP的策略代码,因此专门制成了python版量化策略代码,供大家参考。
由于之前做了几只股票的仿真后,感觉这两种策略在保本方面比其他风险性高的策略更具有稳健性,因此,本文提供了两类策略的概述和python代码,希望对大家有所帮助。
CPPI
概念
固定比例投资组合保险策略
公式
例子
固定比例投资组合保险策略的具体操作方式与买入持有策略类似,运用此投资组合保险策略,就是投资者先决定乘数 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策略,把保险额度从固定改为可变,将保险额度和资产净值挂钩,保险额度为某一时点资产净值最高的一个固定比例。当资产净值变动时,即可求出新的保险额度,将新的保险额度与原来的保险额度相比较,取其中较大的作为资产净值变动后的保险额度。
公式
例子
说明图.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
网友评论