社区曾有小伙伴分享过CPPI策略的回测,并加入了一些技术分析判断,链接地址
鉴于前人已经做过相关策略的研究,此处另辟蹊径,想做一个资产组合不同动态调整方法的比较,包括固定组合保险策略(CPPI,Constant Proportion Portfolio Insurance),时间不变性投资组合保险策略(TIPP, Time Invariant Portfolio Protection)以及 恒定混合策略(Constant Mix Strategy),当然加入买入持有策略(Buy and Hold)进行四者比较将会是一个不错的idea
我们先来看看固定组合保险策略的表现情况,此处先进行一个简单的科普:
CPPI概况:
CPPI 策略(Constant Proportion Portfolio Insurance),又称固定比例组合保险策略,是投资组合保险策略中的一种。CPPI策略通过对投资组合进行动态调整,使之既能提供下行风险的保护,又能最大化潜在收益。CPPI策略将资产分配在低风险资产(如固定收益类资产)和风险资产(如权益类资产)上,通过定量化的资产配置达到本金安全,用投资于低风险资产上的现金净流入来冲抵风险资产组合潜在的可能亏损,并通过投资于风险资产来获得超额收益
CPPI原理:
- 计算缓冲额度Cushion,C = V – F , V为投资组合的价值,F为最低要保额度,即floor insurance
- 计算投资于风险性资产(exposure)的部分e : e = m * C , m 为风险参数,一般设置为5左右效果较好
- 计算投资于无风险资产(bond)的部分b : b = V – e
CPPI特点:
- 高买低卖,助涨杀跌
- 连续不断的调整,会使得成本放大,如何兼顾成本与收益也是值得考虑的一点
# 可以自己import我们平台支持的第三方python模块,比如pandas、numpy等。import talib as ta
# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。def init(context):
context.s1 = ‘510300.XSHG’ # 华泰柏瑞沪深300etf
#context.s0 = ‘511010.XSHG’ # 国泰5年期国债etfcontext.m = 3 # 风险系数
context.f = 0.9 # 要保额度设置太高,分钟回测时会出现落单不成功context.asset_value = context.portfolio.starting_cash
context.floor = context.f * context.asset_value # 安全垫
context.risky_value = context.m * (context.asset_value – context.floor) #初始在险资金
print (‘Initial Output:\n’, context.asset_value, ‘\n’, context.risky_value)#scheduler.run_weekly(rebalance, tradingday = 1) # 每周调仓
def handle_bar(context, bar_dict):
context.asset_value = context.portfolio.portfolio_value# 大牛市时竟然有可能导致风险资产的配比超过所有资产额度,故需要加入一个判断语句,上限为所有资金全部配置为风险资产
context.risky_value = min(context.asset_value, context.m * (context.asset_value – context.floor))
context.riskless_value = context.asset_value – context.risky_valueorder_target_value(context.s1, context.risky_value)
plot(‘risky ratio’, context.risky_value/context.asset_value)
plot(“riskless ratio”, context.riskless_value/context.asset_value)
print (‘Continuous Output:\n’, context.asset_value, ‘\n’, context.risky_value, ‘\n’, context.riskless_value)# 此处代码主要是为了调仓方便
#def rebalance(context, bar_dict):
# if context.risky_value > 0:
# order_target_value(context.s1, context.risky_value)
# order_target_value(context.s0, context.riskless_value)
# plot(‘risky ratio’, context.risky_value/context.asset_value)
# plot(‘riskless ratio’, context.riskless_value/context.portfolio.starting_cash)
我们继续来看看TIPP(时间不变性投资组合保险策略)的回测表现,在此之前我们先来大致了解一下TIPP
TIPP概况:
TIPP策略为基金投资设置了一条安全线或叫保底线F,确保基金的净值不会跌穿这条线。该安全线(保底线)随着投资组合收益的变动而调整,在基金净值上涨并持续一段时间后,F上涨,这就锁定部分收益,在基金净值下降时,通过调整资产配置,保证基金净值永远在F上方****,保证投资和前期收益安全。其与CPPI 的差别主要在于把保险额度从固定改为可变,将保险额度和资产净值挂钩,保险额度为某一时点资产净值最高的一个固定比例。当资产净值变动时,即可求出新的保险额度,将新的保险额度与原来的保险额度相比较,取其中较大的作为资产净值变动后的保险额度。因此,在市场走向多头时,TIPP策略这种只上不下的保险额度调整方式,充分反映了投资成本的不断更新,而非任意过去时间的历史成本。
TIPP原理:
$A_{t}= D_{t}+E_{t}$
$F_{t}=max(A_{t} * f,F_{t-1})$
$E_{t}= m * (A_{t}-F_{t})$
$A_{t}$为t期的投资组合总价值;
$D_{t}$为t期的固定收益资产值;
$E_{t}$为t期的风险性资产价值;
$F_{t}$为t期的要保金额(Floor);
f为固定的要保比率(Floor Percentage);
$F_{t-1}$为t一1期的要保金额(Floor);
m为风险乘数,乘数愈大风险偏好程度愈高,且m > 1
TIPP特点:
- TIPP也没能避免CIPP的劣势,也是追涨杀跌的主
- 依然涉及到调仓频率的问题(二者本是同根生)
下面开始贴回测啦,希望小伙伴在此基础上不断完善与改进
截图的两条曲线分别代表floor的变化情况与风险资产占总资产的投资比率的走势情况,不难发现floor的变化是只增不减的,符合TIPP的特性,并且ratio曲线的变化依然和大盘的走势维持大致相同的走势
# 定时间段调整法/市场走向调整法/乘数调整法
def init(context):
context.s0 = ‘511010.XSHG’ # 国泰上证5年期国债etf
context.s1 = ‘510300.XSHG’ # 华泰柏瑞沪深300etf
context.f = 0.9 # 保险额度比率
context.m = 5 # 风险系数
context.init_wealth = 100000
context.floor = context.f * context.init_wealth
scheduler.run_weekly(rebalance, tradingday = 1) # 定时间段调整
def before_trading(context,bar_dict):
context.asset_value = context.portfolio.portfolio_value
context.floor = max(context.asset_value * context.f, context.floor) # 不断调整
context.risky_value = context.m * (context.asset_value – context.floor)
context.riskless_value = context.asset_value – context.risky_value
def rebalance(context,bar_dict):
if context.risky_value > 0: # 此处的判断语句主要是为了克服一个系统bug
order_target_value(context.s1, context.risky_value)
order_target_value(context.s0, context.riskless_value)
print ( context.asset_value, context.risky_value, ‘–>’,context.risky_value/context.asset_value)
plot(‘ratio’,context.risky_value/context.asset_value)
#其实我想以绝对值的方式显示的,但是这样会由于量级差别太大而覆盖掉ratio值
plot(‘floor’,context.floor/context.init_wealth)
前面分享了CPPI和TIPP的策略回测,我们接着来看看Constant Mix的效果如何。让我们继续按照套路出牌:
Constant Mix概况:
Constant Mix ,又叫恒定混合策略,其对资产配置的调整并非基于资产收益率的变动或者投资者的风险承受能力动,而是假定资产的收益情况和投资者偏好没有大的改变,因而最优投资组合的配置比例不变。恒定混合策略适用于风险承受能力较稳定的投资者,在风险资产市场下跌时,他们的风险承受能力不像一般投资者那样下降,而是保持不变,因而其风险资产的比例反而上升,风险收益补偿也随之上升了;反之,当风险资产市场价格上升时,投资者的风险承受能力仍然保持不变,其风险资产的比例将下降,风险收益补偿也下降。有点类似于‘高抛低吸’,在震荡市的表现从其特征来看,应该会比较好,有兴趣的读者不妨一试。
Constant Mix原理:
风险资产的投资额度占总资产的比重始终保持不变,即risky_value / total_value = constant,显然无风险资产占总资产的比率也将是一个恒定值。
Constant Mix特点:
- 高抛低吸大法,听起来amazing,但实际情况又如何呢?
- 在趋势性市场中会面临两边打脸 ,慎用,震荡市才是王道
底下贴两张图验证一下:
第一张:震荡牛皮市
第二张:单边趋势市
# 可以自己import我们平台支持的第三方python模块,比如pandas、numpy等。
# Constant Mix 类似于反转效应策略,越跌越买,越涨越卖,故应该在震荡市效果较好
import talib as ta
# 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。
def init(context):
context.s0 = ‘511010.XSHG’ #国泰上证5年期国债etf
context.s1 = “510300.XSHG” #华泰柏瑞沪深300etf
context.f = 0.1 #为了与前面的策略进行比较,此处风险资产所占的权重保持为10%不变
context.init_wealth = 100000
context.asset = context.init_wealth
context.risky = context.f * context.asset
context.riskless = (1 – context.f) * context.asset
context.period = 14
scheduler.run_weekly(rebalance,tradingday=1)
#scheduler.run_daily(rebalance, time_rule = market_close(hour = 0, minute = 30))
# 你选择的证券的数据更新将会触发此段逻辑,例如日或分钟历史数据切片或者是实时数据切片更新
def before_trading(context, bar_dict):
context.asset = context.portfolio.portfolio_value
context.risky = context.f * context.asset
context.riskless = context.asset – context.risky
def rebalance(context, bar_dict):
#需要一个震荡市指标,而非趋势性指标,RSI可以拿来一用
close = history(context.period+1, ‘1d’, ‘close’)[context.s1].values # 转换成array
real = ta.RSI(close, timeperiod = context.period)[-1]
print (‘real –>’+str(real))
if context.risky > 0 and real > 35 and real < 65 :
order_target_value(context.s1, context.risky)
order_target_value(context.s0, context.riskless)
print (‘Place an order ‘+str(context.risky/context.asset))
print (”)
plot(‘risky’,context.risky/context.asset)
plot(‘risky ratio’,context.risky/context.init_wealth)
#plot(‘riskless’,context.riskless/context.asset)