我们尚不会对近期的反弹过多解读,但过程仍值得观察。
虽然转债指数截至周四已经回升了1.95%,但于趋势而言当周的反弹改变不大。经历过这一段的波折洗礼,在结构上,哪些品种能够经受考验——或者说,依然能在较弱的情绪环境下,保持住不错的趋势,是更值得关注的。但当下足有400只转债的情况下,即便是只关注价格走势的投资者,也难以较高效率完成对“趋势”的分类。在此我们介绍一类基于降噪处理的技术,从而高效地选出当下比较值得关注的品种。
图表1:转债指数一度跌破55日线,仍可维持趋势的品种值得注意
资料来源:万得资讯,中金公司研究部
降噪本身是较为成熟的技术,其中小波离散变换也是较为常用(Discrete Wavelet Transform)。
其从傅立叶变换(将原始信号分解为不同频次的波,即所谓基函数,投资者可以将其理解为不同周期长度的cos函数,较长周期的对应趋势信号)逐步发展而来,最主要的进步是在原始基函数的基础上加入衰减函数。使原本只能覆盖于全时间轴的基函数(类似cos一样的全局函数),可以只在某个局部有影响力(以应对少见的突变)。当然,正是由于技术本身成熟,高效简易的实现方式,投资者仅了解原理即可。
图表2:变换的基本逻辑示意图
资料来源:万得资讯,中金公司研究部
总之我们可以把价格走势信号拆解为低频的趋势项,以及在此基础上的高频噪声项,并进一步将趋势信号加入转债策略。DWT向下还分有haar,db, sym, coif 等各类细分方法。我们在此选用了变换原理相对较为简单的haar法,并进行迭代滤波来确定趋势,需要注意的是haar变换一次数据量就会相应减少一半,举例说64日的数据量经过4次变化将只有4个变量。
图表3:使用DWT降噪的原理示意图
资料来源:万得资讯,中金公司研究部
图表4:通过DWT haar算法多次迭代计算后的降噪结果
资料来源:万得资讯,中金公司研究部
实现方式非常简单,但为了方便后续使用,尤其是批量化应用到不同的转债标的上,我们在这里对价格的原始序列进行标准化,使其落在[0,1]范围内。
程序1:DWT的python实现
def
getDwt
(srs, rec=
4
)
:
srs = srs.reset_index(drop=
True
)
retCa = dict.fromkeys((range(rec +
1
)))
retCd = dict.fromkeys((range(rec +
1
)))
retCa[
0
] = rescale(srs)
# pywt.wavedec可以直接输出迭代结果,单其输出值为[cA_n, cD_n, cD_n - 1, …, cD2, cD1],
# 但由于其中途不能再做数值变换,同时为了保证我们实现多种迭代方式,因而我们这里仍采用for loop
# 下方cA代表approximation signal(趋势), cD代表detail signal(噪音)
for
i, key
in
enumerate(list(retCa.keys)[
1
:]):
cA, cD = pywt.dwt(retCa[i],
'haar'
)
retCa[key] = pd.Series(rescale(cA))
retCd[key] = pd.Series(cD)
return
retCa, retCd
资料来源:中金公司研究部
我们将转债的正股前128个交易日的数据进行5次降噪处理,这样将保留4个关键点,排列组合可得到一共24种可能性。结合技术上的经验,我们可以简易地将这些可能性分为上行趋势、下行趋势以及横盘震荡。
图表5:简易的形态分类
资料来源:中金公司研究部
基于这样的处理,相应地策略也比较简单。这里我们不以转债指数为对比,而是在简易EasyBall的基础上做策略强化,然后直接以EasyBall作为基准进行对比。
图表6:策略逻辑示意图
资料来源:万得资讯,中金公司研究部
实现方式则较为简单,实操中注意数据提取数量是否超限等问题。策略测试框架在我们此前发布的《简易的转债策略测试框架以及Python实践方法》中,供参考。
def
getData2Show
(codes, date, lag=
128
)
:
#用来取正股走势数据的辅助函数
if
not
w.isconnected: w.start
_, dfUnderlying = w.wss(
','
.join(codes),
'underlyingcode'
, usedf=
True
)
start_date = w.tdaysoffset(-lag, date,
""
).Data[
0
][
0
].strftime(
'%Y/%m/%d'
)
uniStock = set(dfUnderlying.UNDERLYINGCODE)
_, dfRaw = w.wsd(
","
.join(uniStock),
"close"
,
start_date, date,
"Priceadj=True"
, usedf=
True
)
retDict = {}
for
code
in
codes:
stock = dfUnderlying.loc[code,
"underlyingcode"
]
retDict[code] = dfRaw[stock]
return
retDict
def
close2premRank5
(obj, codes, date, tempCodes, dfAssetBook)
:
#简单双低
srs = obj.DB[
'Close'
].loc[date, tempCodes].rank(ascending=
True
)
srs += obj.DB[
'ConvPrem'
].loc[date, tempCodes].rank(ascending=
True
)
t = srs < srs.quantile(
0.5
)
return
t[t].index
def
dwtSecUp2
(obj, codes, date, tempCodes, dfAssetBook)
:
# 假设我们将tplUp中的几种模式定为上行趋势
date = _offset(obj, date)
t = close2premRank5(obj, codes, date, tempCodes, dfAssetBook)
rawData = getData(t, date)
tplUp = ([
1
,
2
,
3
,
4
], [
1
,
2
,
4
,
3
], [
1
,
3
,
2
,
4
], [
1
,
4
,
2
,
3
], [
2
,
1
,
3
,
4
], [
3
,
1
,
2
,
4
])
ret = []
for
code, df
in
rawData.iteritems:
dwt, _ = getDwt(df.CLOSE, rec=
5
)
if
dwt[
5
].rank.astype(int).tolist
in
tplUp: ret.append(code)
return
ret
dfTest = frameStrategy(obj, start=
"2017/12/29"
, selMethod=dwtSecUp2, roundMethod=
21
)
资料来源:中金公司研究部
测试效果如下,可以明显看到上行趋势对EasyBall的加成效果,以及下行趋势的品种即便是在EasyBall的样本下,依然会给组合带来损害。
资料来源:万得资讯,中金公司研究部
最后,行情与调整之前最大的不同是什么?
具体来说,差异在于:在9月转债市场调整之前,转债溢价率、股市情绪指标都在较高的位置,但近期后者明显下滑——即便投资者没有自己编制的情绪指标监控,也能看到市场总成交额已经相比此前明显下滑。至少这个位置,没有包含很多“情绪过热”的风险。因此,转债市场的问题是溢价率仍高,但正股层面的风险解除了不少,我们上述介绍的策略,也是希望投资者能关注到变化,从而更多注意那些估值不高(尤其是溢价率)、形态完好的品种。毕竟,这些品种,已经经历过9月的试验了。
转债市场跟踪
本周市场小幅回升,截止周四(10月21日收盘),万得全A上涨0.69%,创业板指上涨0.1%,上证50下跌0.49%,两市合计成交3.96万亿元,日均成交水平较上周小幅提升。
行业层面,本周化工、公用事业、有色金属、电气设备涨幅领先,休闲服务、食品饮料、通信、医药板块领跌,市场热点轮动较快。
转债指数本周上涨2.18%,个券中清水、蒙电、金诺涨幅领先,众兴、旗滨、利德转债领跌。
市场平均平价溢价率抬升,但大规模品种整体估值下降。
走势切割与Python实践
至少有错位:“低估”策略优化与Python实现
挑战:EasyBall可以更稳吗?——转债退市风险测算与Python实现
简易的转债策略测试框架以及python实现方法
本文摘自:2021年10月22日已经发布的《低噪声下的转债强化策略及Python实现》
向上滑动参见完整法律声明及二维码
返回搜狐,查看更多
责任编辑: