大宗商品期货市场中除了跨期套利,也可以进行跨品种套利。对于跨品种套利来说螺纹钢和热卷是最好的两个合约品种了。由于rb(螺纹钢)和hc(热卷)每张合约都是代表10吨货物,并且生产成本、原料等因素导致这两个品种价格相关性是很强的。当价格差出现异常时,是可以进行对冲套利的。
那么本篇我们就使用Python语言,在FMZ量化上用很简单、简短的代码来实现一个rb和hc的跨品种对冲模型,可以用于回测或者实盘(实盘需要继续优化)。
套利品种:热卷 (hc), 螺纹 (rb)
套利原理:价差定义:Price(hc) – Price(rb)
正套:价差大于阈值 A 时 , 做空热卷,做多螺纹
反套:价差小于阈值 B 时 , 做多热卷,做空螺纹
止盈:
正套;价差小于阈值 C 时,止盈。
反套;价差大于阈值 D 时,止盈。
止损:
正套;价差大于阈值 E时,止损。
反套;价差小于阈值 F时,止损。
策略代码实现
为了方便策略编写设计,策略使用了两个FMZ的模版类库:
模版引用界面
这些模版类库在FMZ上都是开源的,可以直接在FMZ的策略广场复制完整的模版,获取到。然后在自己的策略编写设计页面的模版栏就可以看到这些复制过来的模版类库了,勾选就可以引用。
'''backtest
start: 2022-09-01 09:00:00
end: 2022-09-09 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
mode: 1
'''
p = ext.NewPositionManager()
def onTick():
global maxDiff, minDiff
# 获取合约A行情数据
infoA = exchange.SetContractType(symbolA)
tickA = exchange.GetTicker()
recordsA = exchange.GetRecords()
# 获取合约B行情数据
infoB = exchange.SetContractType(symbolB)
tickB = exchange.GetTicker()
recordsB = exchange.GetRecords()
if not tickA or not tickB or not recordsA or not recordsB:
return
# 分析持仓
pos = exchange.GetPosition()
if pos is None:
return
longPosOfSymbolA = p.GetPosition(symbolA, PD_LONG)
shortPosOfSymbolA = p.GetPosition(symbolA, PD_SHORT)
longPosOfSymbolB = p.GetPosition(symbolB, PD_LONG)
shortPosOfSymbolB = p.GetPosition(symbolB, PD_SHORT)
# 计算价差
diff = tickA["Last"] - tickB["Last"]
# Log("diff:", diff) # 测试
if not longPosOfSymbolA and not shortPosOfSymbolA and not longPosOfSymbolB and not shortPosOfSymbolB:
if diff > maxDiff:
# 空A合约,多B合约
Log("空A合约:", symbolA, ",多B合约:", symbolB, ", diff:", diff, ", maxDiff:", maxDiff, "#FF0000")
p.OpenShort(symbolA, 1)
p.OpenLong(symbolB, 1)
elif diff < minDiff:
# 多A合约,空B合约
Log("多A合约:", symbolA, ",空B合约:", symbolB, ", diff:", diff, ", minDiff:", minDiff, "#FF0000")
p.OpenLong(symbolA, 1)
p.OpenShort(symbolB, 1)
if longPosOfSymbolA and shortPosOfSymbolB and not shortPosOfSymbolA and not longPosOfSymbolB:
# 持有A多头、B空头
if diff > (longPosOfSymbolA["Price"] - shortPosOfSymbolB["Price"]) + stopProfit:
# 止盈
Log("持有A多头、B空头,止盈。", "diff:", diff, "持有差价:", (longPosOfSymbolA["Price"] - shortPosOfSymbolB["Price"]))
p.Cover(symbolA)
p.Cover(symbolB)
elif diff < (longPosOfSymbolA["Price"] - shortPosOfSymbolB["Price"]) - stopLoss:
# 止损
Log("持有A多头、B空头,止损。", "diff:", diff, "持有差价:", (longPosOfSymbolA["Price"] - shortPosOfSymbolB["Price"]))
p.Cover(symbolA)
p.Cover(symbolB)
elif shortPosOfSymbolA and longPosOfSymbolB and not longPosOfSymbolA and not shortPosOfSymbolB:
# 持有A空头、B多头
if diff < (shortPosOfSymbolA["Price"] - longPosOfSymbolB["Price"]) - stopProfit:
# 止盈
Log("持有A空头、B多头,止盈。", "diff:", diff, "持有差价:", (shortPosOfSymbolA["Price"] - longPosOfSymbolB["Price"]))
p.Cover(symbolA)
p.Cover(symbolB)
elif diff > (shortPosOfSymbolA["Price"] - longPosOfSymbolB["Price"]) + stopLoss:
# 止损
Log("持有A空头、B多头,止损。", "diff:", diff, "持有差价:", (shortPosOfSymbolA["Price"] - longPosOfSymbolB["Price"]))
p.Cover(symbolA)
p.Cover(symbolB)
# 画图
ext.PlotLine("差价", diff)
def main():
while True:
if exchange.IO("status"):
onTick()
LogStatus(_D(), "已连接")
else:
LogStatus(_D(), "未连接")
Sleep(500)
策略参数:

策略参数
回测测试
我们使用tick实盘级别回测。

回测设置

回测参数设置

行情图表

自定义画图的差价图表

日志系统
由于实盘级别回测数据量很大,时间范围有限。并且策略参数对冲阈值触发的差价设置得很大,设置了100时才触发。所以只触发了2次对冲交易,并且成功止盈。跨品种对冲也不失为一种交易思路,主要目的是程序化监控差价,捕捉行情剧烈波动时的异常差价进行套利。具体效果好坏与差价趋势的判断有很大关系,需要长期观察市场行情,了解一定基本面信息,才能做到对差价变动趋势、回归胸有成竹。
发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/77167
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!