股指期货跨期套利策略示例
模型简介
套利是期货交易中一种常见的投资方式,其原理是在标的产品间的市场价格关系处于“异常”状态时进行双边交易,以获取低风险价差。相比来说,投机交易的风险太大,套期保则值是为了规避市场风险,无法获取最大收益。而套利交易的收益往往独立于市场,无需关注市场行情涨跌,都可以在价差中获利,且一般波动较小,风险较低。
期货套利交易种类繁多,按品种可分为股指期货套利和商品期货套利;按套利方式又可以分为期现套利、跨期套利、跨市套利、跨品种套利等。在这里,我们选取比较有代表性的股指期货跨期套利向大家介绍,并在之后向大家展示如何在云宽客投研平台加以实现。
跨期套利是指投资者以赚取期货合约价差为目的,在同一期货品种的不同月份合约上建立数量相等、方向相反的交易头寸,最后以对冲或交割方式结束交易、获得收益的方式。最简单的跨期套利就是买入近期的期货品种,卖出远期的期货品种。因此跨期套利所关注的是合约之间的价差,能够一定程度上避免单个期货合约价格剧烈变动所带来的风险。
对于同一个投资标的,市场上一般有若干张不同到期月份的期货合约。以沪深300指数为例,同一时刻,市场上存在当月,下月,当季,下季四个股指期货。这些合约均有同一现货标的,受相同因素的影响,所以合约价格表现出一定的相关性。在市场预期一致的情况下,它们之间的价差应该是稳定的,一旦发生了偏离,那么就出现了套利的机会。
相比期现套利来说,跨期套利并不是无风险套利。期现套利的价差会在合约到期时收敛,但是跨期套利的价差可能会随着股指的上升而上升,实际中也与投资者对中期或短期股指的预期相关,如果价差在一个新的水平上达到平衡,那么我们的策略便面临着风险。
因此,如何判断价差走势,是股指期货跨期套利的关键所在。
制定策略
本篇文章中,我们选取了沪深300股指期货作为投资品种,选择当月主力合约及下月合约(如果当月合约到期,则取下月合约及下个季月合约)作为投资标的。对于价差的,我们采用考察其均值和标准差的方法选择进场时点。
我们的策略具体分为以下几个步骤:
- 选择品种、标的;
- 获取历史数据,计算标的价差(合约B-合约A),以及其均值、标准差,设定上界为均值+标准差_进场参数,下界为均值-标准差_离场参数,这里进场、离场参数都设为1;
- 根据价格、保证金率、合约乘数设定交易量,留存部分现金防止爆仓;
- 当价差上穿上界时,卖出组合,即开多合约A,开空合约B,并在价差下穿均线时平仓;当价差下穿下界时,买入组合,即开空合约A,开多合约B;,并在价差上穿均线时平仓;
收益曲线
收益归因
小结
我们可以看到,对沪深300股指期货的跨期套利策略,在近一年的回测期中表现十分优异,有着稳健且高额的收益率,较低的波动和很小的回撤。这证明了期货套利策略在实际应用上的可行性。
当然,本文着重介绍的只是改策略的思想以及具体的实现过程。在实际应用中,更困难的问题是发掘和验证不同合约直接的相关性与稳定性。传统的跨期套利中投资者需要预期价差(spread)的走势来建立套利头寸,在主观性的影响下这种方法局限性很大。现代投资体系中往往采用统计套利(Statistical Arbitrage)的方法发现价差的稳定性以及变量间的长期均衡关系,用实际的价格与数量模型所预测的价值进行对比,制定统计方法下相对客观的跨期套利策略。这点就留给读者继续探究了。
Code:
12:
year+=1
month-=12
return str(year*100+month)
def initial(sdk):
flag=0 # 标记状态
sdk.setGlobal('flag',flag)
def initPerDay(sdk):
选择标的,当月主力及下月合约
delivery_date = sdk.getFactorData("LZ_CN_FF_LASTTRADE_DATE")
delivery_date_dict = dict(line for line in delivery_date)
todayDate = sdk.getNowDate()
this_month = str(todayDate)[2:6]
year = int(this_month[0:2])
month = int(this_month[2:4])
next_month = dateAdd(year, month, 1)
next_quarter_month = dateAdd(year, month, (3 - month % 3))
contract_this_month = MARKET + str(this_month)
contract_next_month =MARKET + str(next_month)
contract_next_quarter_month=MARKET+str(next_quarter_month)
delivery_date_this_month = delivery_date_dict[contract_this_month + '.CFE']
如果当月合约到期,选择下月即下个季月
if todayDate =WINDOW and len(hist[contract_b])>=WINDOW:
hist_a=[0.0]WINDOW
hist_b=[0.0]WINDOW
for i in range(WINDOW):
hist_a[i]=hist[contract_a][i].close
hist_b[i]=hist[contract_b][i].close
hist_a=np.array(hist_a)
hist_b=np.array(hist_b)
spread=hist_b-hist_a
mean=np.nanmean(spread)
std=np.std(spread)
获取标的报价
q=sdk.getQuotes(contract,2)
price_a=q[contract_a].current
price_b=q[contract_b].current
设定保证金率
margin_a=40
margin_b=40
计算持仓量,投入60%资金,留存40%防止行情震荡爆仓
volume_a= (sdk.getAccountInfo(tsInd=2).availableCash0.6) //((price_amargin_a0.01+price_bmargin_b0.01)300)
volume_b=volume_a
pos=sdk.getPositions(tsInd=2)
if pos:
pos_a=pos[0].optPosition
if len(pos)>1:
pos_b=pos[1].optPosition
else:
pos_b=0
else:
pos_a=0
pos_b=0
if spread[-1] 0: # 下穿均线,平仓
sdk.makeOrder(contract_a, price_a, pos_a, -1, tsInd=2)
sdk.makeOrder(contract_b, price_b, pos_b, -2, tsInd=2)
sdk.sdklog("------------------------------------------")
sdk.sdklog(sdk.getNowDate(), "DATE")
sdk.sdklog([contract_a, price_a, pos_a, -1], 'CLOSE_LONG')
sdk.sdklog([contract_b, price_b, pos_b, -2], 'CLOSE_SHORT')
if spread[-1]>mean and flag-1: # 下穿下界,买入
sdk.makeOrder(contract_a, price_a, volume_a, 2, tsInd=2)
sdk.makeOrder(contract_b, price_b, volume_b, 1, tsInd=2)
sdk.sdklog("------------------------------------------")
sdk.sdklog(sdk.getNowDate(), "DATE")
sdk.sdklog([contract_a, price_a, volume_a, 2], 'OPEN_SHORT')
sdk.sdklog([contract_b, price_b, volume_b, 1], 'OPEN_LONG')
if spread[-1]>(mean+EXIT_SCOREstd) and flag(mean+EXIT_SCOREstd):
flag=1.5
elif spread[-1]>mean:
flag=0.5
elif spread[-1]>(mean-ENTRY_SCORE*std):
flag=-0.5
else:
flag=-1.5
sdk.setGlobal("flag",flag)
def main():
将策略函数加入
config['initial'] = initial
config['strategy'] = strategy
config['preparePerDay'] = initPerDay
启动SDK
SDKCoreEngine(**config).run()
if name == "main":
main()
">
-- coding:utf-8 --
from CloudQuant import SDKCoreEngine # 导入量子金服SDK
from CloudQuant import AssetType
from CloudQuant import QuoteCycle
from CloudQuant import OrderType
import numpy as np # 使用numpy
np.seterr(invalid='ignore')
MARKET=
-- coding:utf-8 --
from CloudQuant import SDKCoreEngine # 导入量子金服SDK
from CloudQuant import AssetType
from CloudQuant import QuoteCycle
from CloudQuant import OrderType
import numpy as np # 使用numpy
np.seterr(invalid='ignore')
MARKET="IF" # 设定交易品种
WINDOW=10 # 设定观察窗口
ENTRY_SCORE=1 # 设定入场参数
EXIT_SCORE=1 # 设定离场参数
config = {
'username': 'pengkun',
'password': '111111',
'rootpath': 'c:/cStrategy', # 客户端所在路径
'assetType': AssetType.Future,
'initCapitalFuture': 10000000, # 初始资金
'startDate': 20160101, # 交易开始日期
'endDate': 20170110, # 交易结束日期
'cycle': QuoteCycle.D, # 回放粒度为1分钟线
'feeRate': 0.001,
'feeLimit': 5,
'strategyName': 'arbitrage', # 策略名
"logfile": "ma.log",
'dealByVolume': False
}
def dateAdd(year,month,add): # 自定义月份加法
month=month+add
if month>12:
year+=1
month-=12
return str(year*100+month)
def initial(sdk):
flag=0 # 标记状态
sdk.setGlobal('flag',flag)
def initPerDay(sdk):
选择标的,当月主力及下月合约
delivery_date = sdk.getFactorData("LZ_CN_FF_LASTTRADE_DATE")
delivery_date_dict = dict(line for line in delivery_date)
todayDate = sdk.getNowDate()
this_month = str(todayDate)[2:6]
year = int(this_month[0:2])
month = int(this_month[2:4])
next_month = dateAdd(year, month, 1)
next_quarter_month = dateAdd(year, month, (3 - month % 3))
contract_this_month = MARKET + str(this_month)
contract_next_month =MARKET + str(next_month)
contract_next_quarter_month=MARKET+str(next_quarter_month)
delivery_date_this_month = delivery_date_dict[contract_this_month + '.CFE']
如果当月合约到期,选择下月即下个季月
if todayDate =WINDOW and len(hist[contract_b])>=WINDOW:
hist_a=[0.0]WINDOW
hist_b=[0.0]WINDOW
for i in range(WINDOW):
hist_a[i]=hist[contract_a][i].close
hist_b[i]=hist[contract_b][i].close
hist_a=np.array(hist_a)
hist_b=np.array(hist_b)
spread=hist_b-hist_a
mean=np.nanmean(spread)
std=np.std(spread)
获取标的报价
q=sdk.getQuotes(contract,2)
price_a=q[contract_a].current
price_b=q[contract_b].current
设定保证金率
margin_a=40
margin_b=40
计算持仓量,投入60%资金,留存40%防止行情震荡爆仓
volume_a= (sdk.getAccountInfo(tsInd=2).availableCash0.6) //((price_amargin_a0.01+price_bmargin_b0.01)300)
volume_b=volume_a
pos=sdk.getPositions(tsInd=2)
if pos:
pos_a=pos[0].optPosition
if len(pos)>1:
pos_b=pos[1].optPosition
else:
pos_b=0
else:
pos_a=0
pos_b=0
if spread[-1] 0: # 下穿均线,平仓
sdk.makeOrder(contract_a, price_a, pos_a, -1, tsInd=2)
sdk.makeOrder(contract_b, price_b, pos_b, -2, tsInd=2)
sdk.sdklog("------------------------------------------")
sdk.sdklog(sdk.getNowDate(), "DATE")
sdk.sdklog([contract_a, price_a, pos_a, -1], 'CLOSE_LONG')
sdk.sdklog([contract_b, price_b, pos_b, -2], 'CLOSE_SHORT')
if spread[-1]>mean and flag-1: # 下穿下界,买入
sdk.makeOrder(contract_a, price_a, volume_a, 2, tsInd=2)
sdk.makeOrder(contract_b, price_b, volume_b, 1, tsInd=2)
sdk.sdklog("------------------------------------------")
sdk.sdklog(sdk.getNowDate(), "DATE")
sdk.sdklog([contract_a, price_a, volume_a, 2], 'OPEN_SHORT')
sdk.sdklog([contract_b, price_b, volume_b, 1], 'OPEN_LONG')
if spread[-1]>(mean+EXIT_SCOREstd) and flag(mean+EXIT_SCOREstd):
flag=1.5
elif spread[-1]>mean:
flag=0.5
elif spread[-1]>(mean-ENTRY_SCORE*std):
flag=-0.5
else:
flag=-1.5
sdk.setGlobal("flag",flag)
def main():
将策略函数加入
config['initial'] = initial
config['strategy'] = strategy
config['preparePerDay'] = initPerDay
启动SDK
SDKCoreEngine(**config).run()
if name == "main":
main()
-- coding:utf-8 --
from CloudQuant import SDKCoreEngine # 导入量子金服SDK
from CloudQuant import AssetType
from CloudQuant import QuoteCycle
from CloudQuant import OrderType
import numpy as np # 使用numpy
np.seterr(invalid='ignore')
MARKET=
关键字:交易策略, 金融工程, 算法交易, 理论基础
风险提示及免责条款
市场有风险,投资需谨慎。本文不构成个人投资建议,也未考虑到个别用户特殊的投资目标、财务状况或需要。用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!