原标题:【中金固收·量化】债市多因子探究——基于Python实践

长久以来,固收+的投资者往往将超额回报的重点放在权益和转债上——毕竟这两部分更容易得到差异化的结果。但前提是权益市场收益风险比高,尤其是可以更为灵活调整的转债,能提供一些“进可攻退可守”:显然这些条件不一定时刻满足。但毕竟即便是固收+投资者,债券的仓位也相对更重。此时我们不妨也着眼于纯债本身的因子回报,这里我们进行了一系列尝试。

报告逻辑示意图

资料来源:中金公司研究部

显然,相比于股票、转债而言,债券数据的标准化程度不高、因子的说服力也可能相对弱。但我们的目标并不太高,我们希望因子模型能够帮助我们:

1)更定量化地理解市场超额回报的来源,并能帮我们也更好刻画相关产品,如普通债基;

2)在此基础上观察是否有可行的因子策略。

一、基于指数表现的因子遴选

虽然个券数据与股票相比标准性欠佳,但借助债券市场丰富的指数体系,我们可以比较简单直观地观察到债券市场的因子特征 。我们将2017年以来93组中债净价指数的周度收益率进行聚类计算。聚类图除了能直观展现各指数的相关性,还能依据其相关性进行多层分类。从结果来看:

1、 期限、信用和流动性 是较显著的因子,如下聚类图依据此也做了各级分类;

2、期限因子的重要性较高,在关键期限如1、5、7年,都对指数的回报有明显影响;

图表1:中债各净价指数聚类图

资料来源:万得资讯,中金公司研究部

3、信用因子相较期限因子影响并没有那么大, 信用债与利率债整体上会有一定差异,同时利率债内部金融债和国债(以及地方债)有明显区分; 而信用债指数内部,评级的影响存疑;

4、流动性因子,相对影响微弱,实用性一般。 聚类图中,农发与口行债指数往往在最后还是会与国开债指数被区分开。

图表2:各期限净价曲线(左)与金融债换手率(右)

资料来源:万得资讯,中金公司研究部

二、因子编写的具体方式

不难看出期限、信用的影响力较强,同时,我们还加入动量因子。 在这个基础框架下,参照Fama-French-Carhart四因子的构建方式,以期限、信用和动量作为非市场因子,构建一个债市多因子模型。

1、期限因子LMS(LongMinusShort):我们参照 平均待偿期(年) 。我们先将长期限各评级加总的收益率,与短期限各评级加总的收益率相减得到【期限和信用的交叉项】;再将长期限各动量加总的收益率,与短期限各动量加总的收益率相减得到【期限和动量的交叉项】,最后将两个交叉项平均值认定为期限因子。

2、信用因子GMP(GoodMinusPoor):我们直接参照平均派息率(%)而非隐含评级,以此能够区分利率债和信用债的情况,具体算法与期限类似。

3、动量因子HMW(HighMinusWeek):我们参照指数前一个月的涨跌幅作为的动量划分。

此外,对于因子的计算,我们标的为 中债各财富指数 ,并采用 指数总市值加权

具体计算逻辑如下:

图表3:债市因子构建方式(示意图)

资料来源:中金公司研究部

图表4:债市因子构建方式(程序示意图)

def computerBondFactor (obj, startDate, momwindow= 20 , window= 20 ) :

# 储存固定周期的涨跌幅数据并计算动量,以及后续我们因子构建是按照市值加权的

startDate = pd.to_datetime(startDate).strftime( '%Y/%#m/%#d' )

ret = obj.DB[ 'CLOSE' ].pct_change(window).loc[startDate::window]

wgt = obj.DB[ 'AMT' ].loc[startDate::window]

obj.DB[ '1Mmom' ] = obj.DB[ 'CLOSE' ].pct_change(momwindow).shift(window + 1 )

dictRawdf = {

'ptm' : { 'dfRank' : obj.DB[ 'MATURITY' ].loc[startDate::window].rank(pct= True , axis= 1 ), 'mat' : [ None , None , None ]},

'credit' : { 'dfRank' : obj.DB[ 'DIVIDEND' ].loc[startDate::window].rank(pct= True , axis= 1 ),

'mat' : [ None , None , None ]},

'mom' : { 'dfRank' : obj.DB[ '1Mmom' ].loc[startDate::window].rank(pct= True , axis= 1 ), 'mat' : [ None , None , None ]}}

# 将每个周期依照期限、信用和动量进行三分(以30%与70%为界)

for k, v in dictRawdf.items:

dfRaw = v[ 'dfRank' ]

v[ 'mat' ][ 0 ] = dfRaw.applymap( lambda x: 1 if x > 0.7 else np.nan)

v[ 'mat' ][ 1 ] = dfRaw.applymap( lambda x: 1 if (x <= 0.7 ) & (x >= 0.3 ) else np.nan)

v[ 'mat' ][ 2 ] = dfRaw.applymap( lambda x: 1 if x < 0.3 else np.nan)

dictFactors = { 'LMS' : { 'keydf' : 'ptm' , 'comdf' : [ 'credit' , 'mom' ], 'values' : None },

'GMP' : { 'keydf' : 'credit' , 'comdf' : [ 'ptm' , 'mom' ], 'values' : None },

'HMW' : { 'keydf' : 'mom' , 'comdf' : [ 'ptm' , 'credit' ], 'values' : None }}

# 依据类似Carhart四因子方式构建期限、信用和动量三大因子(市值加权计算因子)

for k, v in dictFactors.items:

factorValue = None

for num, com in enumerate(v[ 'comdf' ]):

pctDiff = None

for i in [ 0 , 2 ]:

pct = None

for j in [ 0 , 1 , 2 ]:

mat = dictRawdf[v[ 'keydf' ]][ 'mat' ][i] * dictRawdf[com][ 'mat' ][j]

pctwgtMean = (ret * mat * wgt).sum(axis= 1 ) / (wgt * mat).sum(axis= 1 )

pct = pctwgtMean if j == 0 else pct + pctwgtMean

pct.fillna( 0 , inplace= True )

pctDiff = pct if i == 0 else pctDiff - pct

pctDiff.fillna( 0 , inplace= True )

factorValue = pctDiff if num == 0 else factorValue + pctDiff

v[ 'values' ] = factorValue / 6.

dfRet = pd.DataFrame(columns=dictFactors.keys)

for k in dfRet.columns:

dfRet[k] = dictFactors[k][ 'values' ]

dfRet.index = [pd.to_datetime(d) for d in dfRet.index]

return dfRet

资料来源:万得资讯,中金公司研究部

依据2010年以来的因子收益情况,我们观察到

1) 期限因子是存在一定周期性的, 信用因子长期暴露下优势相对有限, 而动量因子暴露优势比较显著;

2)月度来看信用因子与期限因子存在一定正相关。 而无论周度还是月度,动量因子整体与期限/信用因子存在一定负相关。

图表5:债市各主要因子收益率

资料来源:万得资讯,中金公司研究部;注:数据区间为2009年12月30日至2022年5月18日

图表6:债市各主要因子相关性情况

资料来源:万得资讯,中金公司研究部;注:数据区间为2009年12月30日至2022年5月18日

面对目前已有的因子数据,我们以下将进行两方面的探索:

1)依据债市多因子,我们可以对债基进行风险归因,并以周度或月度的频率,关注债基的行为与丰富对其的评价维度;

2)对于各因子再进行探究,挖掘因子增强的量化策略。

三、基于债市多因子的债基评价

模仿Carhart模型,我们搭建以下债市四因子模型:

其中,我们使用 中债-新综合财富(总值)指数(CBA00101)的收益率,相较于一年期存款利率的超额收益,来构建市场因子。 依据模型,我们对市场中几个典型的纯债产品(简称中不含“信用”),自2016年以来的月度收益,进行了风险拆解。我们观察到

1)模型对于各基金解释能力普遍较强,R-Squared基本维持到85%及以上;

2)债基在alpha层面不太明显;

3)市场因子对基金业绩解释能力较强,beta值能一定程度体现债基对纯债资产的风险暴露水平,基本与杠杆有一定挂钩;

4)债基在期限因子层面暴露相对有限,p-value呈现出来的显著值并不高。此外,基金平均而言,有做陡曲线的倾向。

5)信用因子解释力度较强,beta值均为正;而动量因子的稳定暴露较难,样本中仅有一只产品动量暴露偏显著,其收益率是全样本中最高的。

图表7:纯债基月度收益风险拆解

资料来源:万得资讯,中金公司研究部;注:样本债基选取2016年以来综合规模排名处于前列的中长期纯债基;测算周期为2016年1月4日至2022年5月18日

图表8:纯债基月度滚动(24个月)收益风险拆解

资料来源:万得资讯,中金公司研究部;注:市场/信用beta剔除当期p-value小于2%的例

而动态来看,模型整体解释力度仍不弱,此外我们观察到1)市场beta近年有所走弱;2)信用beta也处于趋缓的节奏,而且产品间差距比较大。

四、有关债市多因子量化策略的探究

以上我们主要就多因子在债基评价方面进行定量分析。而再回顾我们所拆解的多因子收益情况,实际上 若对于个别因子能做好一定程度的择时,那么对于固收产品而言或许会有显著增强效果。 这也是我们开篇所提到的——我们希望在债市因子层面做不同程度的暴露,以此达到另一种维度的“固收+”。以下,我们就期限、信用和动量因子的增强暴露策略做一定的尝试。

1、期限择时策略:

我们将“10年与隔夜利率差”作为基准标量,当其滚动40日均值显著低于滚动120日线,则买入短期限指数;若短均线显著高于长均线,则买入长期限指数。经过测算,策略在收益端的增厚较为显著,2012年迄今年化回报在5.16%(综合指数4.58%,加权平均综合指数4.57%),同时 在子类中,该期限策略仍具备显著增强效果。

图表9:期限策略对组合的增强效果

资料来源:万得资讯,中金公司研究部;注:以上回测结果测算周期为2011年12月29日至2022年5月18日; 期限切换与各平均策略以指数样本券10日结算量加权 ;换仓周期为40日

具体操作上,我们短均线使用40日平均,长均线使用120日平均,显著与否参照的是0.5倍120日标准差。

图表10:期限策略的代码实现

def maturityStrategySimple (obj, codes, date, tempCodes, dfAssetBook) :

# 计算10Y与隔夜利差

srs = (obj.DB[ 'TermStructure' ][ 10 ] - obj.DB[ 'TermStructure' ][ 0 ]).loc[:date]

srsShort = srs.rolling( 40 ).mean.iloc[ -1 ]

srsLong = srs.rolling( 120 ).mean.iloc[ -1 ]

srsLongstd = srs.rolling( 120 ).std.iloc[ -1 ]

tempCodes = obj.DB[ 'MATURITY' ].columns

srsMat = obj.DB[ 'MATURITY' ].loc[date, tempCodes].dropna

# 短均线显著低于长均线,则买入短久期指数,反之则买入长久期指数

if srsShort <= srsLong - srsLongstd * 0.5 :

t = srsMat[srsMat < srsMat.quantile( 0.3 )].index

elif srsShort >= srsLong + srsLongstd * 0.5 :

t = srsMat[srsMat > srsMat.quantile( 0.3 )].index

else :

t = tempCodes

return t

资料来源:中金公司研究部

2、动量因子控制策略:

对于动量因子而言,结果与我们此前报告中提及的“大级别趋势必惩、小趋势顺势可为”基本一致。因此我们在对债市做因子选择时,从进攻性上仍然偏好短期动量,而回撤端我们则考虑市场当前交易量是否处于历史相对高位。

图表11:动量策略的有效性

资料来源:万得资讯,中金公司研究部;注:2012年以来策略区间为2011年12月29日至2022年5月18日;2018年以来策略区间为2017年12月29日至2022年5月18日;策略均为21天换仓,均以各财富指数的市值加权处理

图表12:动量策略的评价结果

资料来源:万得资讯,中金公司研究部;注:2012年以来策略区间为2011年12月29日至2022年5月18日;2018年以来策略区间为2017年12月29日至2022年5月18日;策略均为21天换仓,均以各财富指数的市值加权处理

具体操作上,我们先选择动量处于前1/3的指数,在这个基础上剔除交易量处于250个交易日80%分位数及以上的。

图表13:动量控制策略的程序实现

def momStrategyMax (obj, codes, date, tempCodes, dfAssetBook) :

date = offset(obj, date)

idx = obj.DB[ 'CLOSE' ].index.get_loc(date)

srs = obj.DB[ 'CLOSE' ].iloc[idx - 21 :idx ][tempCodes].pct_change( 20 ).iloc[- 1 ].rank

t = srs[srs > srs.quantile( 0 . 333 )].index

srsVol = obj.DB[ 'VOL' ].iloc[idx - 251 : idx][t].rank(axis= 0 ).iloc[- 1 ] / 252 .

t = srsVol[srsVol < 0 . 8 ].index

return t

资料来源:中金公司研究部

3、信用因子:

在经过期限因子与动量因子调整后, 同等条件下 做多高票息的策略或许是有效的。因此我们在编制信用因子相关策略时,会先基于期限因子的选择,再在其中选择高票息的,最后选剩余指数中具备动量优势的。

图表14:高票息策略的有效尝试

资料来源:万得资讯,中金公司研究部;注:上图中年化回报/波动/最大回撤单位均为%,2012年以来策略区间为2011年12月29日至2022年5月18日;2018年以来策略区间为2017年12月29日至2022年5月18日;策略均为21天换仓,均以各财富指数的市值加权处理;中债信用债指数中期限最长,隐含评级最低的中债-市场隐含评级AA信用债财富(10年以上)指数,2018年以来年化回报6.43%,Calmar为4.94x;2016年8月成立以来年化回报4.34%,Calmar为0.33x。

图表15:高票息策略的程序实现

def creditStrategy (obj, codes, date, tempCodes, dfAssetBook) :

tempCodes = maturityStrategySimple(obj, codes, date, tempCodes, dfAssetBook)

date = offset(obj, date)

srs = obj.DB[ 'DIVIDEND' ].loc[date, tempCodes].dropna

t = srs[srs > srs.quantile( 0.5 )].index

return momStrategyMax(obj, codes, date, t, dfAssetBook)

资料来源:中金公司研究部

五、利用债市多因子构建的组合

以上我们所作的因子增强策略,均是以各财富指数为标的。而落实到实务层面,大部分纯债债券是没法像转债一样有标准的结构数据,以此直接做量化策略。同时个券流动性偏弱,所以个券做量化策略的效果可能也并不理想。

因此对于纯债投资者,或许可以参照以上因子增强策略在久期、信用层面的选择,落到个券上再有动量考虑;而对于能投债基的投资者,例如FOF、保险、理财等,可以找相应的指数债基操作,也可以依据因子增强策略的选择,选择在某因子上有稳定暴露的产品。以下,我们梳理在信用与动量层面有稳定暴露的纯债基产品。

图表16:信用因子暴露稳定的债基

资料来源:万得资讯,中金公司研究部

图表17:动量因子暴露相对稳定的债基

资料来源:万得资讯,中金公司研究部

因子出现较大变动,货币政策出现较大转向,信用债出现超预期风险

本文摘自:2022年5月27日已经发布的《债市多因子探究——基于Python实践

向上滑动参见完整法律声明及二维码 返回搜狐,查看更多

责任编辑:

声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
发布于: 山西省