期限结构与carry大致定义
我们有没有注意到:同一商品期货合约,由于交割时间不同,价格有很大差异?这种差异如果可以被利用,我们即可获得一种特殊的收益——展期收益。
展期收益基本定义
每个期货合约都有自己固定的生命周期,这个周期在交割结算日结束。如果你持有到期,就需要持有这些市场头寸的交易者退出(平仓)这些合约,并将其展期至下一个季度。这种期货合约展期动作,是期货市场所特有的。
这种收益也被称为carry(持续持有)收益,一般意义上的carry是指票息所得和资金成本之间的差。
很多科班的金融学生都了解,利率期限结构(Term Structure of Interest Rates),是指在某一时点上,不同期限资金的收益率(Yield)与到期期限(Maturity)之间的关系。而到了商品期货市场,期限结构就是指远期和近期合约,由于交割时间不同,市场需求不同,产生了价差,价差又产生了展期收益(carry收益)。
通过上图看出,螺纹的远期合约价格更低
carry展期收益是一种重要的收益来源,你可以按照字面理解就是持有它,在很多市场carry都会产生收益。在固定收益领域有句话:Carry is the king。Total Return = Carry Mark to Market P&L(总回报=Carry 市值),其中Carry = 票息 – 融资成本。
公式来自:Carry Ralph S.J. Koijen Tobias J.Moskowitz Lasse Heje Pedersen Evert B. Vrugt November 2016
在商品期货领域期货和现货无风险套利的定义是:远期合约和近期合约,存在套利机会,期货合约到期日之前,期货与现货除受供需影响外,亦受持有成本(cost of carry)的影响。由此可以理解carry收益,由期货的两个特性决定——期限性和收敛性。
今天的文章在聚宽平台上,我们就简单实现这个策略的多品种对冲效果。虽然之前有很多人推荐继续写时间序列策略,但是考虑到聚宽大神太多,我写一两个害怕被看出来水平太菜,所以留个诸位自行发挥吧,我们搞点别的。
期限结构——和动量不同源的有趣因子
期货合约的期限性(期货合约的生命周期有限)使期货市场定价必须及时解决问题。随着期货合约所剩交易日的减少,合约价格代表性、市场流动性、影响价格变化的因素等也都将发生相应的变化。
期限结构因子作为期货合约的特色因子,与现货的库存水平、持有成本(包括交通、仓储以及保险费用等)、市场利率(购买现货的融资成本)以及持有现货的便利收益(convenience yield)等因素相关,而且它和动量几乎完全无关(因为计算期限结构考虑的是两个不同时间的合约价格,而不是一个合约价格),所以在CTA量化因子倍受关注。
讲完了期限性,再说说收敛性。价格收敛是指越是临近期货合约交割日,期货价格越是趋近于现货价格。这个过程实质上就是期货基差趋向于最小化的过程,也可以说是期货价格逼近现货价格、二者逐渐收敛的过程。我们可以用近月合约近似替代现货价格,则可以计算出合约升水Contango、贴水backward幅度。
远期和近期合约由于交割时间不同,市场需求不同,产生了价差。远期升水Contango的情况较多,因为有资金利息和仓储成本的影响。期货贴水backward也会发生,由于短期供需缺口和产期经济前景看淡,会造成远期合约价格较低。
比如我在2018年12月28日,以3868元平仓1手螺纹钢1901合约多单,然后以3399元买入1手1905合约多单,我的持仓量始终都是1手,但是我降低了持有成本3868 - 3399元,此时可以认为我的展期收益 = 01新合约 - 05老合约 = 469元,全段展期收益率 = 14.04%,折合每月展期收益率 = 14.04% / 4(两个合约之间的相隔月数) = 3.51%。这种展期收益为正的结果叫做远月合约贴水,如果远月比近月贵,则叫做升水。
今天简单介绍一个标准的商品期货市场展期收益率模型,它通过做多贴水最多的合约,做空升水最多的合约,赌升贴水收敛,获得较为稳定的carry收益。
远期和近期合约由于交割时间不同,市场需求不同,产生了价差。远期升水Contango的情况较多,因为有资金利息和仓储成本的影响。期货贴水backward也会发生,由于短期供需缺口和产期经济前景看淡,会造成远期合约价格较低。
比如我在2018年12月28日,以3868元平仓1手螺纹钢1901合约多单,然后以3399元买入1手1905合约多单,我的持仓量始终都是1手,但是我降低了持有成本3868 - 3399元,此时可以认为我的展期收益 = 01新合约 - 05老合约 = 469元,全段展期收益率 = 14.04%,折合每月展期收益率 = 14.04% / 4(两个合约之间的相隔月数) = 3.51%。这种展期收益为正的结果叫做远月合约贴水,如果远月比近月贵,则叫做升水。
今天简单介绍一个标准的商品期货市场展期收益率模型,它通过做多贴水最多的合约,做空升水最多的合约,赌升贴水收敛,获得较为稳定的carry收益。
展期收益率观察与计算
展期收益策略是商品期货特有的策略类型。逻辑上,展期收益策略与传统的趋势类策略相关性较差,有较好的互补性,因为他们从根源上不同源。
对于资产管理,商品配置一般不投资于实物商品,业内流行的方式是通过商品期货,然后滚动移仓,达到配置商品的目的。理论上,投资商品期货的收益可以分解成:
Futuresreturn = Spotreturn Rollreturn Collateralreturn
即期货投资收益由3部分构成:对应现货的收益、展期收益和现金收益。这里roll收益和carry收益相对应。
在商品期货市场,同一标的品种对应着不同交割日的期货合约,如螺纹钢每年有1月,5月,10月,这3个主力合约。
商品期货的现货价格(这里常用近月合约代替现货价格)与不同交割月期货合约交易价格之间的价格差,即商品期货的期限结构。若现货价格大于期货价格,或近月合约的期货价格大于远月合约的期货价格,称为期货贴水backward,或现货升水;若现货价格小于期货价格,或近月合约的期货价格小于远月合约的期货价格,称为期货升水Contango,或现货贴水。
我们一般说升贴水,都是针对远期合约。
展期收益率特指两个合约之间的收益差额,它属于期限结构的研究范围内,期限结构是某一个特定品种的期货所有合约的价格按时间排列构成的曲线,如下图:
AL铝期货期限结构(多期合约价格)
螺纹钢期限结构(多期合约价格)
在聚宽研究平台获取价格和绘图代码如下:
import matplotlib.pyplot as plt
import pandas as pd
symbolList = get_future_contracts('RB')
PriceList = []
SymList = []
for i in symbolList:
SymList.append(i)
PriceList.append(attribute_history(i,1,'1d',['close'])['close'][-1])
s_1 = pd.Series(SymList, name='Symbol')
s_2 = pd.Series(PriceList, name="Price")
df = pd.concat([s_1, s_2], axis=1)
df=df.set_index("Symbol")
plt.figure(figsize=(15, 7))
plt.plot(df)
plt.show()
我们可以通过计算不同标的物的展期收益率来度量不同标的的现货溢价幅度,并根据不同标的之间展期收益率的高低来构建交易策略。
假设 Pt,n 是 t 时刻近月合约的价格,Pt,d 是 t 时刻远月合约的价格,Nt,n 是近月合约在 t 时刻距离交割日的天数,Nt,n 是远月合约在 t 时刻距离交割日的天数。则展期收益率 Rt为:
当 Rt 为正,即近月合约价格大于远月合约价格,当 Rt 为负,即近月合约价格小于远月合约价格。对同一横截面的所有品种来说,最大的 Rt,即对所有品种来说,该标的的近月合约对远月合约涨幅最大,最小的 Rt,即对所有品种来说,该标的的近月合约对远月合约跌幅最大。
按照渤海证券的交易逻辑:若不同合约的展期收益率排名可在一定程度上体现“多强空弱”中的“强”、“弱”概念,即可根据展期收益率排名构建交易策略,从而在展期收益率的角度做到“顺势而为”。
绘制展期收益率的代码如下:
# 以螺纹钢1901 1905合约为例
PriceList1901 = attribute_history('RB1901.XSGE',120,'1d',['close'])
PriceList1905 = attribute_history('RB1905.XSGE',120,'1d',['close'])
df = pd.concat([PriceList1901, PriceList1905], axis=1)
df.columns= ['RB1901.XSGE','RB1905.XSGE']
# 计算展期收益率
df['RY:RB'] = df.apply(lambda x: (x[0] / x[1]-1)*3, axis=1)
plt.figure(figsize=(15, 7))
plt.plot(df['RY:RB'])
plt.legend(['RY:RB'])
plt.show()
螺纹展期收益率
沪铝展期收益率
由于展期收益率的计算不涉及到指数合约,都是在两个相邻的真实合约之间进行,所以我们首先人工写出了历史上的大部分期货品种的主力合约,如这样。
# 历史上的每个合约主力月份
g.domMonth = {
'MA': ['01', '05', '09'],
'IC':['01','02','03','04','05','06','07','08','09','10','11','12'],
'IF':['01','02','03','04','05','06','07','08','09','10','11','12'],
'IH':['01','02','03','04','05','06','07','08','09','10','11','12'],
'TF':['03','06','09','12'],
'T':['03','06','09','12'],
'CU':['01','02','03','04','05','06','07','08','09','10','11','12'],
'AL':['01','02','03','04','05','06','07','08','09','10','11','12'],
'ZN':['01','02','03','04','05','06','07','08','09','10','11','12'],
'PB':['01','02','03','04','05','06','07','08','09','10','11','12'],
'NI':['01', '05', '09'],
'SN':['01', '05', '09'],
'AU':['06', '12'],
'AG':['06', '12'],
'RB':['01', '05', '10'],
'HC':['01', '05', '10'],
'BU':['06', '09', '12'],
'RU':['01', '05', '09'],
'M':['01', '05', '09'],
'Y':['01', '05', '09'],
'A':['01', '05', '09'],
'P':['01', '05', '09'],
'C':['01', '05', '09'],
'CS':['01', '05', '09'],
'JD':['01', '05', '09'],
'L':['01', '05', '09'],
'V':['01', '05', '09'],
'PP':['01', '05', '09'],
'J':['01', '05', '09'],
'JM':['01', '05', '09'],
'I':['01', '05', '09'],
'SR':['01', '05', '09'],
'CF':['01', '05', '09'],
'ZC':['01', '05', '09'],
'FG':['01', '05', '09'],
'TA':['01', '05', '09'],
'MA':['01', '05', '09'],
'OI':['01', '05', '09'],
'RM':['01', '05', '09'],
'SF':['01', '05', '09'],
'SM':['01', '05', '09'],
'AP':['01', '05', '10'],
}
然后我们撰写了一个函数,来获取当时的远月合约名称,该函数大家可以直接使用:
# 获得当前远月合约
def get_nextDom(context,ins):
# 获取当月主力合约
dom = get_dominant_future(ins)
if dom !='':
# 获取合约交割月份时间,如['1601']
YD = re.findall(r"\d \d*",dom)
try:
# 获取远月合约——读取“历史上的每个合约主力月份”,读取月份后两位,往后移一位
nextDomMonth = g.domMonth[ins][g.domMonth[ins].index(YD[0][-2:]) 1]
# 替换远月合约日期,合成远月合约名,如['RB1601.XSGE']
g.MappingNext[ins]=dom.replace(YD[0][-2:],nextDomMonth)
except:
nextDomMonth = g.domMonth[ins][0]
nextDom = dom.replace(YD[0][:2],str(int(YD[0][:2]) 1))
g.MappingNext[ins] = nextDom.replace(YD[0][-2:],g.domMonth[ins][0])
else:
pass
交易规则方面,我们考虑一定要尽可能简单,我们计算商品池内所有商品的ts(展期收益率)因子,将因子值大于0的商品归为远期贴水backward组,将因子小于0的商品归为远期升水contango组;
因为我们约定常用远期合约描述升水贴水,所以近期比远期高,是远期贴水,近期升水,属于contango。反之,属于backward。
这里的升和贴,和展期收益率的大小,是相反的,这里是比较绕的一点。
# 展期收益率
g.RY[ins] = (g.PriceCurrent-g.PriceNext)/g.PriceNext/g.DM
# 分为远期升水RYS,远期贴水RYB,两组
if g.RY[ins] < 0:
g.RYS[ins] = g.RY[ins]
elif g.RY[ins] > 0:
g.RYB[ins] = g.RY[ins]
按照“多强空弱”中的理念,我们做多展期收益率最高的品种,做空展期收益率最低的品种。这里你可以按照某些研报上所说,在两组内再做一个强度区分,比如只做每一组的前50%的品种。当然也可以全部做,我建议在测试模型时,全部交易。
## 交易模块
def Trade(context):
# 多头部分的品种,展期收益率大于0
a = sorted(g.RYB.items(), key=lambda x: x[1],reverse = True)
# 空头部分的品种,展期收益率小于0
b = sorted(g.RYS.items(), key=lambda x: x[1],reverse = False)
大家可以看到我们在初始设置里,对于手续费的设定是比较严格的,这个手续费做中长期模型是肯定够用了,如果你担心手续费低,就再加上一倍的冲击成本我也没意见:
# 期货类每笔交易时的手续费是:买入时万分之1,卖出时万分之1,平今仓为万分之1
set_order_cost(OrderCost(open_commission=0.0001, close_commission=0.0001,close_today_commission=0.0001), type='index_futures')
# 设定保证金比例
set_option('futures_margin_rate', 0.15)
# 设置滑点(单边万5,双边千1)
set_slippage(PriceRelatedSlippage(0.001),type='future')
期限结构Carry收益性能测试和分析
模型我们最初采用了月度调仓方案,获得如下绩效:
然后我们尝试,对所做的合约进行筛选,比如说,我只做多展期收益率为正的且最高的前50%,做空为负且最低的后50%,在代码这里做调整:
# 将字典进行排序,各买入50%,或者全部交易
for i in range(int(len(a)*1)):
BuyList.append(a[i][0])
for i in range(int(len(b)*1)):
SellList.append(b[i][0])
我们分别测试了25% 50%和75%,得到这样的绩效:
我感觉这样的测试不利于得到稳定的测试结论,所以还是选择全做了。
在全部交易的时候,我截取了一天的持仓,大家可以看细节:
可以看到,持仓是相对均衡的,也就是说,它基本上实现了多品种多空对冲。这就是Carry收益的策略。
再次总结,它获取的是和趋势完全不相关的收益,通过远期近期合约的收益率差异,计算升贴水,然后全市场分析升贴水率(展期收益率)。做多展期收益率更高(近期比远期更强)的品种,做空展期收益率更低(近期比远期更弱)的品种。
Carry展期收益是有一定风险的,要做好风险控制,在做期货趋势的同时搭配这套模型,综合曲线可能会更好。
最后,我们还测试了周度调仓上图,结果较为理想,回撤控制确实满意。
分享代码给各位,如果高兴,觉得有收获,可以打赏,谢谢各位!
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程