传统模型跨期套利策略(传统商品期货)
什么是跨期套利
跨期套利是套利交易中最普遍的一种,是利用同一商品但不同交割月份之间正常价格差距出现异常变化时进行对冲而获利的,又可分为牛市套利(bull spread)和熊市套利(bear spread)两种形式。例如在进行金属牛市套利时,交易所买入近期交割月份的金属合约,同时卖出远期交割月份的金属合约,希望近期合约价格上涨幅度大于远期合约价格的上涨幅度;而熊市套利则相反,即卖出近期交割月份合约,买入远期交割月份合约,并期望远期合约价格下跌幅度小于近期合约的价格下跌幅度。
怎么判断不合理价差
合理价差的运用在正向市场中有效,在正向市场中,以相邻月份为例,远月比近月的价格高出的部分主要是持有成本-持有收益,此处为简单的传统跨期套利模拟,我们假设所有的期货处于正向市场中(大部分情况下,期货市场呈现正向市场)。由于是传统的商品期货,持有期间很少会出现持有收益的情况,因为基本上只需要考虑持有期间的成本即可。持有成本又可以大致分为交割成本(包括运输费持仓费等)和时间成本(持有商品时候损失的现金流收益),交割成本以仓储费和交易费为主,我们可以把他们看作一个常量整体(需要投资者自己调研和设置,本策略中的常量并不代表市场上可供参考和使用的数值),而时间成本就是资金流的时间价值。如果同一个标的资产的不同交割月份的两个期货合约之间的价差,和我们计算的理论值偏离过大,我们可以认为出现了不合理价差,也同时出现了交易机会。
如何利用不合理价差进行套利交易
我们计算的理论值价差代表的是我们目标期货合约由于交割月份不同应该具有的合理价值,而实际的价差代表的是市场中投资者对于期货涨跌走势的态度以及市场中供求关系的表现。假如市场是理性的,那么在理论值的价差和市场中实际的价差应相差不大。因此当理论值偏离当时的实际价差过高或者过低时,说明市场中的非理性冲击导致价差偏离理论值,我们可以进入空头或者多头头寸,预期价差会回归一个合理价值,这是均值回归概念的应用也是跨期套利的理论基础。确定合理价值变成了最重要的一环,在本策略中我们使用的最简单的持有成本传统模型。此模型的优势是在简单易操作,缺点是由于模型本身简单和理论假设较多,可能会忽略现实中的其他因素的影响。
合理价差策略原理
远期合约价值的计算公式
- Flater = Fearly*(1+Rf*month/12)
其中Flater为远期合约合理价值,Fearly为近期合约最新价,Rf为无风险利率,month为远近期货合约之间的时间差,month/12用来转化年化利率为持有期间利率。
之后用近期价格减去远期理论价格即可得到理论价差的值,而用近期价格减去远期价格则可以得到实际的价差代码如下:
- interest_rate = 1 + 0.0404*3/12 # 远近合约月份之间的利率系数理论值
- theory_price = quote1.last_price * interest_rate # 根据利率系数计算出的远期合约理论价格
- theory = quote1.last_price – theory_price # 近期实际价格和远期理论价格的价差
- real = quote1.last_price – quote2.last_price # 近期实际价格和远期实际价格的价差
策略交易逻辑
将实际基差和理论价差进行对比,我们可以得到他们之间的差值。假设实际价差最终会回归到理性价差,那么当实际价差高于或低于理论价值一个特定值的时候(我们自己设定),可以进行开仓操作;同理当实际价差和理论价差相差的绝对值小于某个特定值的时候(我们自己设定),可以进行平仓操作。
当实际价差高出理论价差特定值时,我们认为实际价差被高估,可以多头价差,实际操作为卖出近期合约同时买入相同手数的远期合约。
当实际价差低于理论价差特定值时,我们认为实际价差被低估,可以空头价差,实际操作为买入近期合约同时卖出相同手数的远期合约。
当实际价差接近理论价差时,我们认为实际价差是理性价差,可以平仓操作,实际操作为同时平仓近期和远期合约。
由于同时进入两种合约的相反交易方向,且同种标的物的两个合约相关性较高,盈利并不会被个别合约的涨跌影响过多,所以风险敞口只需要看整体价差即可,开仓和平仓条件也只需要看远近合约价差。
回测
注意:以上参数皆是默认数值,并不严谨。在真正交易的过程中,我们要根据对应合约品种和行情的变化,调试这个区间以适应当下的行情状态。
策略代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Lisiheng"
*from tqsdk import TqApi, TqAuth, TargetPosTask, TqSim*
*# 设置初始资金,信易账号,模拟模式,回测模式和时间,web\_gui开启*
*api = TqApi(TqSim(init\_balance=10000000), auth=TqAuth("信易账户", "账号密码"), web\_gui=True)*
*# 参数和合约的相关初始设置*
*symbol1 = "SHFE.cu2006" # 近期合约*
*symbol2 = "SHFE.cu2010" # 远期合约*
*target\_pos1 = TargetPosTask(api, symbol1) # 近期合约TargetPosTask设置*
*target\_pos2 = TargetPosTask(api, symbol2) # 远期合约TargetPosTask设置*
*quote1 = api.get\_quote(symbol1) # 取得近期合约最新行情*
*quote2 = api.get\_quote(symbol2) # 取得远期合约最新行情*
*pos1 = api.get\_position(symbol1) # 取得近期合约持仓情况*
*pos2 = api.get\_position(symbol2) # 取得远期合约持仓情况*
*interest\_rate = 1 + 0.0404\*3/12 # 远近合约月份之间的利率系数理论值*
*# 定义价差相关参数*
*def difference():*
*theory\_price = quote1.last\_price \* interest\_rate # 根据利率系数计算出的远期合约理论价格*
*theory = quote1.last\_price – theory\_price # 近期实际价格和远期理论价格的价差*
*real = quote1.last\_price – quote2.last\_price # 近期实际价格和远期实际价格的价差*
*floor = theory – 200 # -200为自己设置的参数,扩大利润区间下界*
*cap = theory + 200 # +200为自己设置的参数,扩大利润区间上界*
*close\_low = theory – 50 # -50为自己设置的参数,扩大平仓区间上界*
*close\_high = theory + 50 # +50为自己设置的参数,扩大平仓区间下界*
*return theory\_price, theory, real, floor, cap, close\_low, close\_high*
*while True:*
*api.wait\_update()*
*# 如果近期远期合约最新价变动,相关理论价格和实际价格涉及到的价差更新*
*if api.is\_changing(quote1, "last\_price") or api.is\_changing(quote2, "last\_price"):*
*dif\_theory\_price, dif\_theory, dif\_real, dif\_floor, dif\_cap, dif\_closelow, dif\_closehigh = difference()*
*# 判定目前近期和远期仓位是否为空仓,以及判定价差关系*
*if (pos1.pos\_long == 0 and pos1.pos\_short == 0) and (pos2.pos\_long == 0 and pos2.pos\_short == 0)*
*and (dif\_real < dif\_floor):*
*print("可以买近卖远交易开仓", dif\_real, dif\_theory)*
*target\_pos1.set\_target\_volume(30) # 实际价差被低估的情况时,近期合约cu2006净持仓调整为30手*
*target\_pos2.set\_target\_volume(-30) # 实际价差被低估的情况时,同时远期合约cu2008净持仓调整为-30手*
*# 判定目前近期和远期仓位是否为空仓,以及判定价差关系*
*elif (pos1.pos\_long == 0 and pos1.pos\_short == 0) and (pos2.pos\_long == 0 and pos2.pos\_short == 0)*
*and (dif\_real > dif\_cap): # +200为自己设置的参数,扩大利润区间*
*print("可以卖近买远开仓", dif\_real, dif\_theory)*
*target\_pos1.set\_target\_volume(-30) # 实际价差被高估的情况时,近期合约cu2006净持仓调整为-30手*
*target\_pos2.set\_target\_volume(30) # 实际价差被高估的情况时,同时远期合约cu2008净持仓调整为30手*
*# 判定目前近期和远期仓位是否都有仓位,以及判定价差关系*
*elif (pos1.pos\_long != 0 or pos1.pos\_short != 0 or pos2.pos\_long != 0 or pos2.pos\_short != 0)*
*and (dif\_closelow < dif\_real < dif\_closehigh):*
*target\_pos1.set\_target\_volume(0) # 实际价差接近理论值的情况时,近期合约cu2006的净持仓调整为0手*
*target\_pos2.set\_target\_volume(0) # 实际价差接近理论值的情况时,远期合约cu2008的净持仓调整为0手*
*print("平仓", dif\_real, dif\_theory)*