结论:
1、在双低可转债之中,按可转债的溢价率排序,选择低溢价的构建组合并轮动,则组合的投资收益会进一步提高(但加入手续费后,该部分收益提高有限,主要原因是换手率显著上升)2、双低策略的主要收益来源,依然是低溢价,而非低价格;并且双低-低溢价策略与单纯的低溢价策略的Alpha收益的相关性很低,两者可以构建双策略组合
3、通过低溢价策略和双低之低溢价策略,可以发现,可转债的主要超额收益都是来源于股类收益、而非债类收益——这似乎是一句正确的废话
前言
首先,感谢 @wanghc02 分享的Python代码、数据和前期的研究成果分享,才促使我对于可转债策略的开发有进一步的思考和回测。本帖使用的可转债数据,是他在微信公众号文中提供的2018/01/02 - 2021/05/21的数据;策略代码,是基于下面最新的代码。(如有想获得最新回测代码的,可以看《可转债回测框架更新及常见问题统一答复》)
其次,感谢 @liu11liu1 指出我先前的错误,并进行了校验回测——也对我之前怼他,表示道歉。
第三,这篇帖子会比较长。虽然我发帖的习惯是先上结论;但是个人认为,对于一个真正的投资者来说,单一的策略或结论都不重要,重要的是推理的过程和逻辑。我个人很享受这个过程,也会如实记录在本帖中,希望有人能共同分享快乐。
最后,数据质量和统计方法的局限性:
1、2018年的可转债数量稀少
进入转股期的可转债,上半年只有13-29个、直到年末才达到46个;因此从可转债备选池的角度看,2018年业绩的区分度很低、仅供有限参考
2、统计方法的局限性:
a)为了贴合个人投资者的操作实际,下面的数据分析,一般是选择排名前10或前20的可转债;而非分为几组、测算其中的某一组
b)策略的业绩是基于每日轮动、且手续费为0
一、前期工作
根据我在《10行Python搞定可转债分组回测》中的回帖,可以发现:1、低溢价策略、双低策略,都有不错Alpha回报;但是低价策略和可转债等权重指数相比,虽然其业绩的夏普比率显著提高,但这主要是得益于低价转债的低波动率、而非组合业绩显著高于基准。
2、通过双低策略,分别与低溢价和低价策略相比,就会发现一个有意思的事情——双低策略的Alpha与低溢价策略为负相关、与低价策略为正相关,那么,为什么双低策略的Alpha反而会更高呢?难道其收益不应该与低价策略更接近吗?这一点明显和我们的直观感受相悖。
3、这个反常的点,引起了我的高度兴趣——只有找到市场存在严重偏见的地方,才有可能发现有价值的机会。
二、回测过程
1、对问题的猜想:既然低价策略本身,没有贡献什么Alpha;那么按查理芒格的名言“遇到事情,总是要反过来想”,是不是在双低组合中,其实其中的高价或者低溢价的那部分转债才是组合收益的主要贡献者呢?
2、对数据的回测:
1)选取排名前20的、排名前10的,分别构建传统的双低组合
2)根据双低组合的名单,再按转债的价格排序,前10名构建“高价10”组合;同理,分别构建”低价10、高溢价10、低溢价10“组合,并测算其Alpha和相关性
3)另外,对于低溢价策略,在按转债的价格排序,分别构建“高价10、低价10”组合,并测算其Alpha和相关性
4)获得的结果如下:
各策略Alpha的相关性:
3、对结果的分析:
1)在双低策略中,低溢价依然是Alpha的主要贡献者;我将其命名为”双低之低溢价策略“;其他的子分类都显著弱于它、甚至是负贡献。
2)从Alpha的相关性看,”双低20之低溢价10“:
与低价策略的相关性,已经从原先的40%降至20%——这可能也是收益提高的来源;
与低溢价策略的相关性,也为20%;
这也为后续构建”双低20之高溢价10“ +”低溢价之前20“的双策略组合,提供了数据支持。
该双策略组合的业绩表现如下:
4)其他:
我也测了上述两者和我在《量化多因子组合》帖子提到的股票量化因子的Alpha之间的相关性,发现可转债Alpha与股票量化Alpha的相关性也很弱(-7% ~ 0%之间),因此,完全还可以在股票和可转债上创建一个夏普比率更优的组合,但这里就不展开了。
4、再思考:
1)根据 wanghc02 公众号文章《转债因子全回测-续》 大致可以发现,除了”低溢价“以外,其他的动量类单因子风险收益比并不好;通过上述对于双低策略的细分,对于如何优化这些单因子提供了有益的思路——类似于对某种未知因子的提纯
2)逻辑最重要,数据只是验证逻辑
本文的推理逻辑,出发点是低价策略本身不赚钱,那就反过来搞
文末,再次感谢 wanghc02,他的代码写得简洁优雅,看着舒心!
追更:
1)经@yyb凌波 @GLZ0514 提醒,测算了”双低之高价10“的持仓转债平均价格,确实发现:- 双低系列:债券均价的波动不高,长期看基本在100-110左右波动,并没有跟随基准价格而上涨;但也不是什么转股门槛的130
- 低溢价系列:债券均价的波动很大
2)另外,根据 @yyb凌波 的意见,测试了一下在双低前15%中选择前10名低溢价的组合,确实业绩更好。这也从侧面说明了,专家经验的价值。
对比结果:
3)8月19日,测了一下含交易成本(单边千二)的情况,发现在这种情形下,双低中选低溢价的收益下降较多;对比双低20,单日调仓的年化收益低了2%,5日调仓的年化收益只高了3%——唯有和价格的相关性下降,这点依然有效。所以,实战中如何选择,还是看个人喜好吧。
赞同来自: Duckruck 、neverfailor 、Robinjancy 、jadepan 、小飞龙 、 、更多 »
等市场风格转变(比如大蓝筹吃香),低溢价可能就瓦特了。
正股牛市->转债牛市->低溢价好(跑不赢正股哦)
正股震荡->转债震荡->双低跑赢正股
正股熊市->转债熊市->低价有债性的勉强还能持有,其余的也亏
如果认为自己没有超额的选股能力
模型就是,假设长期来看正股能上涨,年化6%-9%吧,其中70%时间震荡,30%时间趋势,
最终转债跑赢正股,年化跑赢10%。
总体看,怕是双低法跑赢的多?
赞同来自: WASDFG 、yangtcm 、neverfailor
赞同来自: UniqueLy 、WASDFG 、sothin 、jx58245858 、丢失的十年 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、更多 »
@liu11liu11 : 感谢你指出错误,为之前怼你,表示道歉!
@yyb凌波 :确实,从双低前15%中选,比 从双低前20中选,业绩更好
结论:
1、在双低可转债之中,按可转债的溢价率排序,选择低溢价的构建组合并轮动,则组合的投资收益会进一步提高
2、双低策略的主要收益来源,依然是低溢价,而非低价格;并且双低-低溢价策略与单纯的低溢价策略的Alpha收益的相关性很低,两者可以构建双策略组合
赞同来自: Campanella
secShortNameBond bondPremRatio
0 凌钢转债 10.8350
1 广汇转债 19.4242
2 利群转债 6.6607
3 嘉泽转债 7.4736
4 荣晟转债 4.6261
5 君禾转债 7.4879
6 花王转债 20.5792
7 塞力转债 10.5676
8 永安转债 14.7320
9 灵康转债 6.5247
10 博世转债 5.3978
11 万顺转2 17.7707
12 本钢转债 24.7250
13 鸿达转债 4.9696
14 百川转债 5.2689
15 星帅转债 3.6685
16 搜特转债 13.3810
17 文科转债 7.4998
18 齐翔转2 1.8272
19 润建转债 10.1506, '\n\n')
secShortNameBond bondPremRatio
0 利群转债 6.6607
1 嘉泽转债 7.4736
2 荣晟转债 4.6261
3 君禾转债 7.4879
4 灵康转债 6.5247
5 博世转债 5.3978
6 鸿达转债 4.9696
7 百川转债 5.2689
8 星帅转债 3.6685
9 齐翔转2 1.8272
tradeDate
2019-01-02 1.001371
2019-01-03 1.015258
2019-01-04 1.042656
2019-01-07 1.044623
2019-01-08 1.062550
2019-01-09 1.062626
2019-01-10 1.075033
2019-01-11 1.075861
2019-01-14 1.088538
2019-01-15 1.089914
2019-01-16 1.081004
2019-01-17 1.077113
2019-01-18 1.083269
2019-01-21 1.078427
2019-01-22 1.077188
2019-01-23 1.085541
2019-01-24 1.088148
2019-01-25 1.078755
2019-01-28 1.069222
2019-01-29 1.065479
2019-01-30 1.060256
2019-01-31 1.075567
2019-02-01 1.087545
2019-02-11 1.092964
2019-02-12 1.114638
2019-02-13 1.120036
2019-02-14 1.117425
2019-02-15 1.153970
2019-02-18 1.157866
2019-02-19 1.179228
...
2020-11-20 2.456312
2020-11-23 2.471142
2020-11-24 2.491056
2020-11-25 2.470778
2020-11-26 2.432231
2020-11-27 2.432969
2020-11-30 2.468771
2020-12-01 2.466856
2020-12-02 2.493080
2020-12-03 2.459083
2020-12-04 2.494039
2020-12-07 2.509795
2020-12-08 2.468156
2020-12-09 2.467200
2020-12-10 2.446338
2020-12-11 2.465716
2020-12-14 2.390847
2020-12-15 2.378731
2020-12-16 2.395485
2020-12-17 2.401282
2020-12-18 2.412863
2020-12-21 2.377484
2020-12-22 2.359345
2020-12-23 2.291055
2020-12-24 2.316800
2020-12-25 2.294416
2020-12-28 2.293423
2020-12-29 2.318779
2020-12-30 2.333886
2020-12-31 NaN
赞同来自: 苦瓜爱人ccc 、vimliu 、塔格奥 、Campanella
import numpy as np
bond_df = pd.read_csv('zz_bond.csv')
df = pd.read_csv('转债理论价值.csv',dtype={'tickerBond': object})ticker_list = list(set(df['tickerBond'].tolist()))
data = DataAPI.MktConsBondPerfGet(beginDate=u"20190101",endDate=u"20201231",secID=u"",tickerBond=ticker_list,tickerEqu=u"",field=u"tickerBond,tradeDate,secShortNameBond,closePriceBond,remainSize,totalSize,bondPremRatio,chgPct",pandas="1")
pricedf=data[['tickerBond','tradeDate','closePriceBond']]
pricedf=pricedf.set_index(['tradeDate','tickerBond']).unstack()['closePriceBond']
pricedf.index=pd.to_datetime(pricedf.index)
day_return=pricedf.pct_change().shift(-1)
premdf=data[['tickerBond','tradeDate','bondPremRatio']]
premdf=premdf.set_index(['tradeDate','tickerBond']).unstack()['bondPremRatio']
premdf.index=pd.to_datetime(premdf.index)
factor=premdf+pricedf
N=20
def selectSmallN(tmp):
tmp=tmp.copy()
# print (tmp)
symbols=tmp.nsmallest(N).index
tmp[:]=np.nan
tmp[symbols]=1
return tmp
def selectLargetsN(tmp):
tmp=tmp.copy()
symbols=tmp.nlargest(N).index
tmp[:]=np.nan
tmp[symbols]=1
return tmp
signal=factor.apply(selectTopN,axis=1)
1、选择双低中排名最前的20个
N = 20signal_1 = factor.apply(selectSmallN, axis=1)
shuangdi_df = signal_1.stack().reset_index()
shuangdi_df.columns=['tradeDate','tickerBond','flag']
shuangdi_df = shuangdi_df[(shuangdi_df.flag == 1) & (shuangdi_df.tradeDate == '2020-12-30') ]
shuangdi_prem = DataAPI.MktConsBondPerfGet(beginDate=u"20201230",endDate=u"20201230",secID=u"",tickerBond=shuangdi_df['tickerBond'].tolist(),tickerEqu=u"",field=u"secShortNameBond,bondPremRatio",pandas="1")
print (shuangdi_prem.head(20),'\n\n')
2、在TOP20中,选择高价的前10个
temp_price = signal_1 * premdfN = 10
signal_2 = temp_price.apply(selectSmallN, axis=1)
signal = signal_2
shuangdi_df_high = signal.stack().reset_index()
shuangdi_df_high.columns=['tradeDate','tickerBond','flag']
shuangdi_df_high = shuangdi_df_high[(shuangdi_df_high.flag == 1) & (shuangdi_df_high.tradeDate == '2020-12-30') ]
shuangdi_prem = DataAPI.MktConsBondPerfGet(beginDate=u"20201230",endDate=u"20201230",secID=u"",tickerBond=shuangdi_df_high['tickerBond'].tolist(),tickerEqu=u"",field=u"secShortNameBond,bondPremRatio",pandas="1")
print (shuangdi_prem.head(20))
pnl=(signal*day_return).sum(axis=1)/N
(pnl+1).cumprod().plot(figsize=(10,5),grid=True)
print (pnl+1).cumprod()
赞同来自: Campanella 、nkfish