钢厂利润套利逻辑
目前国内商品期货套利模式主要包括产业链套利、跨期套利、内外盘套利和期现套利。我们针对黑色产业链期货进行研究,利用产业链关系进行钢厂利润套利,涉及螺纹钢、铁矿、焦炭品种,炼钢工艺中影响总成本的主要因素是原料成本,即铁矿石、焦炭成本。根据研报内容,我们获知钢材的成本 可以通过如下方式进行计算:
螺纹钢期货价格 = 1.6 铁矿石期货价格 + 0.5 焦炭期货价格 + 其他成本(东方证券《衍生品系列研究之五-商品期货套利策略实证》)
上述等式是无套利的情形,而市场上的期货价格是波动的,上述等式在实际的市场中是不等的。如果从价差的变动来看,上述等式左右两边的价差可以理解为钢厂炼钢的利润,那么价差的波动就是钢厂利润的波动,因此追随钢厂利润波动的模式就是钢厂利润套利的模式,在实际操作中,我们用指数合约代替实际价格
钢厂利润 = 1 螺纹钢指数合约价格 – 1.6 铁矿石指数合约价格 – 0.5 * 焦炭指数合约价格 – 其他成本
def cal_spread(klines_rb, klines_i, klines_j): index_spread = klines_rb.close - 1.6 * klines_i.close - 0.5 * klines_j.close ma_spread = ma(index_spread, 10) spread_std = np.std(index_spread) klines_rb["index_spread"] = index_spread klines_rb["index_spread.board"] = "index_spread" return index_spread, ma_spread, spread_std
关于钢厂炼钢利润波动的逻辑,参考研报内容:如果炼钢利润过高,铁矿和 焦炭价格会跟涨,挤压炼钢利润;炼钢利润过低,钢材价格回升。我们可以看到钢厂利润波动的逻辑性较强,基于此,当钢厂利润达到高位时,可以做空利润,即做空螺纹钢做多铁矿石焦炭,当钢厂利润处于底位时,可以做多利润,即做多螺纹钢做空铁矿石焦炭。
一般的套利做法是设置固定的价差值进行套利,在价格偏离价差平均水平时进行多空操作,下面是通过期货指数绘制的钢厂利润曲线
从上面的图中我们发现价格并没有一个稳定的回复价格,即价差的分布并不对称,这样的序列显然不适合用传统的回复套利方法,在本报告中我们采用类似布林通道的策略思想,比如当价差超越长期或者 短期均值一个标准差之时,可以认为此时的价差水平偏高,因此我们做空价格相对高的期货,做多 价格相对低的期货,而当二者的价差回归到一个长期或短期均值的时候同时对二者进行平仓。这样 策略获得价差回复的收益
- 研报中模型具体设置为
- 开仓条件:价差在10日均值加1倍标准差和1.2倍标准差之间,有回归趋势开仓。
- 平仓条件:回归到10日均值进行平仓。
策略原理
在该示例中,在回测过程中我们设置不同的开仓标准以及止损 等条件,发现以下设置更为合适
-
- 开仓条件:价差在15日均值加0.5倍标准差之间,有回归趋势开仓。
- 开仓条件:
- 1.价差序列下穿上轨,利润冲高回落进行回复,策略空螺纹钢、多焦煤焦炭
- 2.价差序列上穿下轨,利润过低回复上升,策略多螺纹钢、空焦煤焦炭
if index_spread.iloc[-1] > 0.5 * spread_std + ma_spread.iloc[-1]: target_pos_rb.set_target_volume(-100) target_pos_i.set_target_volume(100) target_pos_j.set_target_volume(100) elif index_spread.iloc[-1] < ma_spread.iloc[-1] - 0.5 * spread_std: target_pos_rb.set_target_volume(100) target_pos_i.set_target_volume(-100) target_pos_j.set_target_volume(-100)
策略回测
初始账户资金:1000万
回测日期:2019.06.04-2019.08.11
多、空头开仓手数:100手
合约:SHFE.rb2001,DCE.i2001,DCE.j2001
钢厂套利策略回测结果 | |||||
合约代码 | 合约品种 | 收益率 | 风险度 | 最大回撤 | 年化夏普率 |
SHFE.rb2001,DCE.i2001,DCE.j2001 | 31.49% | 18.60% | 10.01% | 2.649 |
天勤量化源码
""" 钢厂利润套利策略 注: 该示例策略仅用于功能示范, 实盘时请根据自己的策略/经验进行修改 """ from tqsdk import TqApi, TargetPosTask from tqsdk.tafunc import ma import numpy as np api = TqApi() SYMBOL_rb = "SHFE.rb2001" SYMBOL_i = "DCE.i2001" SYMBOL_j = "DCE.j2001" klines_rb = api.get_kline_serial(SYMBOL_rb, 86400) klines_i = api.get_kline_serial(SYMBOL_i, 86400) klines_j = api.get_kline_serial(SYMBOL_j, 86400) target_pos_rb = TargetPosTask(api, SYMBOL_rb) target_pos_i = TargetPosTask(api, SYMBOL_i) target_pos_j = TargetPosTask(api, SYMBOL_j) # 计算钢厂利润线,并将利润线画到副图 def cal_spread(klines_rb, klines_i, klines_j): index_spread = klines_rb.close - 1.6 * klines_i.close - 0.5 * klines_j.close ma_spread = ma(index_spread, 10) spread_std = np.std(index_spread) klines_rb["index_spread"] = index_spread klines_rb["index_spread.board"] = "index_spread" return index_spread, ma_spread, spread_std index_spread, ma_spread, spread_std = cal_spread(klines_rb, klines_i, klines_j) print("ma_spread是%.2f,index_spread是%.2f,spread_std是%.2f" % (ma_spread.iloc[-1], index_spread.iloc[-1], spread_std)) while True: api.wait_update() # 每次有新日线生成时重新计算利润线 if api.is_changing(klines_j.iloc[-1], "datetime"): index_spread, ma_spread, spread_std = cal_spread(klines_rb, klines_i, klines_j) print("ma_spread是%.2f,index_spread是%.2f,spread_std是%.2f" % ( ma_spread.iloc[-1], index_spread.iloc[-1], spread_std)) # 价差序列下穿上轨,利润冲高回落进行回复,策略空螺纹钢、多焦煤焦炭 if index_spread.iloc[-1] > 0.5 * spread_std + ma_spread.iloc[-1]: target_pos_rb.set_target_volume(-100) target_pos_i.set_target_volume(100) target_pos_j.set_target_volume(100) # 价差序列上穿下轨,利润过低回复上升,策略多螺纹钢、空焦煤焦炭 elif index_spread.iloc[-1] < ma_spread.iloc[-1] - 0.5 * spread_std: target_pos_rb.set_target_volume(100) target_pos_i.set_target_volume(-100) target_pos_j.set_target_volume(-100)