社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【中金 · 固收+】风格轮动:规律、策略与Python实现

中金固定收益研究 • 3 年前 • 634 次点击  


当固收+的投资者们提到“选结构”,风格的倾向几乎是首要的问题。从去年以来,市场至少经历过大盘成长转向小盘(2021年1月末)、小盘转向大盘价值(2021年10月)两次体感较为明显的风格转向。虽然个股、转债层面还有诸多值得讨论的细节,但风格上站错位置,几乎很难拿回好的回报。而在近期,投资者更加集中关注的,是成长是否有抄底价值。在此,我们基于历史情况和数据分析,给出一些可行的思路。


一、A股特征:趋势与边界并存


两个月窗口,正负20%是关键边际值

我们对近10年的风格相对跑赢幅度进行统计,以上证50和中证1000分别代表大\小盘,国证成长和国证价值代表成长\价值。相对稳定的结论是,两个月窗口下,市场整体的轮动偏移幅度基本在正负20%以内(不考虑2015年那一次过大的波动)。这意味着在统计意义上,正负20%是风格偏移的边际值,若触及则后续大概率会出现均值回归。若进一步细看,正负10%已经具备较大约束力。同时我们发现成长价值的分化较大小盘而言更弱,即使不考虑2014-2015年。但值得注意的是,在来到边界值之前,市场倾向于延续原来的方向。


图表1:股市风格轮动情况(40天涨跌幅差值,单位%)

资料来源:万得资讯,中金公司研究部,注:蓝线代表大票跑赢小票幅度,橙线代表价值跑赢成长幅度



轮动从两象限变为四象限

以2020年为节点,我们进一步观测到在此以前,风格相对跑赢幅度的散点具备较明显的正斜率,这意味着市场整体呈现着“小盘≈成长”、“大盘≈价值”的格局,即轮动是一个两象限互动。而随着2020年以来创业板企业体量明显增长,大盘成长股和小盘价值股的结构性机遇,上图的散点的分散度加剧,市场风格轮动的可能性增多。成为典型的四象限运动,也就是大盘不再与价值挂钩,小盘也不一定就意味着成长——而就实际样本来看,“小盘价值”多为一些小周期股,而这些标的可能股票的覆盖度不高,但却常常作为转债的发行人。


图表2:分年度股市风格轮动情况(2012-2019)

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


图表2(续):分年度股市风格轮动情况(2020-2022YTD)


资料来源:万得资讯,中金公司研究部,注:2022年数据截至2022年3月24日



长期趋势偏好成长

而如果我们拉长窗口,能看到成长更容易在长时间窗口占据优势。也即,上述“40交易日20%”的约束关系聚焦于中短期,与更长期限视角下,风格的优劣势具备趋势性不矛盾。但在大小盘中,这样的趋势性则更不明显。


下图则试图说明这个问题:横轴为观测窗口,纵轴为99%的概率下,成长\价值或大盘\小盘的极限跑赢幅度。可以看到,就成长与价值而言,存在时间窗口越大,可能累计的分化越大的情况。而对于大小盘而言,极限分化度不稳定地随窗口扩大而扩大(而图上小盘在100日左右的占优主要来自行情整体强势后,小盘弹性更大,并非严格的风格问题)。


图表3:板块轮动偏离阈值情况(大小盘)


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


图表3(续):板块轮动偏离阈值情况(价值成长)

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



二、转债与风格轮动的适配度


考虑边界和趋势的转债轮动策略


固收+投资者的一个显著特征是可以用转债补足一些“深挖个股”触及不到的风格。而我们经过测算,风格轮动型策略也与转债的策略相适配。延续上述观察,市场的风格是一个趋势与约束并存的变量,我们很容易得到如下的一个策略:当换仓时,风格偏离未接近阈值时,我们遵循市场风格偏移的势能;若突破阈值,则做反向处理。


具体实现方式上,我们需要下面的辅助函数,以得到当前风格偏移的状态,程序如下。这里相当于生成一个1.a中的图表。实现上没有其他需要注意之处,但为避免后续编号混乱,在设定风格与对应代码的字典时选择了OrderedDict。


图表4:观测板块轮动的基础程序


def getDictPairs():
    return OrderedDict({"BigSmall": ["000016.SH", "000852.SH"],
            "ValueGrowth": ["399371.SZ", "399370.SZ"]})

def divergence(dictPairs, start, end, lstDay=None):
    if lstDay is None:
        lstDay = [40]
    
    lstCodes = []
    for v in dictPairs.values():
        lstCodes += v

    _, dfPct = w.wsd(",".join(lstCodes), "Close", dayOffset(start, -max(lstDay) - 1),
                     end, usedf=True)

    dfRet = pd.DataFrame(index=dfPct.index)

    for n in lstDay:
        for k, v in dictPairs.items():
            dfPctRolling = dfPct.rolling(n).apply(lambda x: 100 * (x[-1] / x[0] - 1))
            dfRet[k + ":{_n} days".format(_n=n)] = dfPctRolling[v[0]] - dfPctRolling[v[1]]
            
    dfRet.index = map(lambda  x: x.strftime("%Y/%m/%d"), pd.to_datetime(dfRet.index))
    
    return dfRet.dropna(how="any")

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


在选择风格时,我们首先判定其偏移度距离阈值是否较远,然后判定是否超出阈值,按照下图逻辑:


图表5:板块轮动策略逻辑示意

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


由于已经设定好偏移数据,这里可以直接进入择券程序,择券框架请见《是时候,选出优秀的策略了》[1]。具体实现较为简单,没有值得特别注意之处。


图表6:具体择券程序


def rotation(obj, codes, date, tempCodes, dfAssetBook):
    # 获取市场风格分化情况.
    if not hasattr(obj, 'divergence'):
        obj.divergence = divergence(dictPairs, start, end)

    # 获取转债标的风格数据,得到dictStyle为字典,共有size和style两个字段,各保存一个表格以注明大小盘、成长价值风格
    if not hasattr(obj, 'dictStyle'):
        obj.dictStyle = getStyle(obj)

    sizeScore, styleScore = obj.divergence.loc[date, ["BigSmall:40 days","ValueGrowth:40 days"]]

    # 获取阈值的相关数据,这里我们采取动态把控,而非定量的阈值
    drSize, drStyle = 1 if sizeScore > 0 else  -1,1 if styleScore > 0 else -1
    idx = obj.divergence.index.get_loc(date)
    # theta代表该风格过去1年在40交易日内累计偏移度的95%分位数,epsilon则是这一偏移度绝对值的0.5倍标准差
    # 下面判断当前状态与阈值距离时,则以 score - theta的绝对值是否大于epsilon为准
    thetaSize, thetaStyle = obj.divergence.iloc[idx - 250:idx].abs().quantile(0.95)
    epsilonSize, epsilonStyle = obj.divergence.iloc[idx - 250:idx].abs().std() / 2.
    
    # 经过计算后,下面stylePick的choice将显示为所选风格的字符
    stylePicks = {'size': {'score': sizeScore, 'direction': drSize, 'theta': thetaSize, 'epsilon': epsilonSize,
                           'choice': 0, 'category': ['large', 'small']},
                  'style': {'score': styleScore, 'direction': drStyle, 'theta': thetaStyle, 'epsilon': epsilonStyle,
                            'choice': 0, 'category': ['value', 'growth']}}

    for k, v in stylePicks.items():
        # 判定:当前状态距离阈值是否较远
        if abs(v['score'] - v['theta' ] * v['direction']) >= v['epsilon']:
            
            v["choice"] = v["direction"] if abs(v["score"]) < v["theta"] else -v["direction"]

        else:
            v['choice'] = -v['direction']
            
        v['choice'] = v['category'][0] if v['choice'] == 1 else v['category'][1]
    # 这里选择对应风格的品种,取交集
    srs = obj.dictStyle['size'].loc[date, tempCodes] == stylePicks['size']['choice']
    srs *= obj.dictStyle['style'].loc[date, tempCodes] == stylePicks['style']['choice']

    return srs[srs].index

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



上述策略的效果我们测算如下:


图表7:策略具体情况


资料来源:万得资讯,中金公司研究部,回测数据自2017年12月29日至2022年4月6日



图表8:策略净值情况

资料来源:万得资讯,中金公司研究部,回测数据自2017年12月29日至2022年4月6日


同时这一策略的“生产率”也相对比较稳定,没有出现某一风格长时间占据主导地位的情况:直观角度来看大票仍然占比不低,因此我们认为加权策略的稳定性具备一定保证。


图表9:策略实际选择板块情况

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



三:固收+择股的风格轮动


《再看固收+的绝对收益》[2]中,我们谈到固收+择股普遍具有分散、低估值、近似复制宽基的特性。我们希望借助风格轮动这个话题,再进一步推拟固收+择股还有什么可能性。根据我们测算,按照上述方式直接则股,亦可在250日的滚动窗口下,以接近76%的胜率战胜万得全A。而与债券结合后,在例如简单的股二债八分配下,亦可得到明显的增强效果。投资者可以考虑将股票、转债策略与债券结合,得到更加适合自己的风险回报分布(以下债券市场以债券指数为替代)。


图表10:固收+策略具体情况

资料来源:万得资讯,中金公司研究部,回测数据自2016年12月29日至2022年4月6日



图表11:固收+策略净值情况

资料来源:万得资讯,中金公司研究部,回测数据自2016年12月29日至2022年4月6日




[1] https://research.cicc.com/document/detail?id=221571

[2] https://research.cicc.com/document/detail?id=260562



文章来源

本文摘自:2022年4月8日已经发布的《固收+与风格轮动:规律、策略与Python实现


杨 冰 SAC执业证书编号:S0080515120002;SFC CE Ref: BOM868

房 铎 SAC执业证书编号:S0080519110001

罗 凡 SAC执业证书编号:S0080120070107

陈健恒 SAC执业证书编号:S0080511030011;SFC CE Ref: BBM220




法律声明

向上滑动参见完整法律声明及二维码



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/131436
 
634 次点击