输入/搜索内容
0
关注
224
关注者
钢厂利润套利策略
创建于 2021-09-22 15:52:55  更新于 2024-11-27 21:21:51
 5
 1681

img

钢厂利润套利策略

对大宗商品黑色产业链期货的研究,进行钢厂利润套利涉及螺纹钢(代码rb)、铁矿石(代码i)、焦炭(代码j)。在钢厂炼钢生产过程中影响产品成本的最主要因素就是原料。

通常对于基本面的研究我们认为,螺纹钢期货价格 = 1.6 * 铁矿石期货价格 + 0.5 * 焦炭期货价格 + 其它

以上是理想的情况,而期货价格是变化的,有溢价的。所以这个等式在实际的情况中是不相等的。从成品、原料组合的价格差变动来看,等式左右两边的价差可以理解为钢厂炼钢的利润,那么价差的波动就是钢厂利润的波动,因此追随钢厂利润波动的模式就是钢厂利润套利的模式。

公式变动为:

钢厂1吨螺纹钢的利润 = 1吨螺纹钢合约价格 – 1.6吨铁矿石合约价格 – 0.5吨焦炭合约价格 – 其它成本

钢厂炼钢利润波动的逻辑:如果炼钢利润高过一定水平,铁矿石和焦炭价格会跟涨,挤压炼钢利润;炼钢利润低过一定水平,钢材价格回升。我们可以看到钢厂利润波动的逻辑性较强。当钢厂利润达到高位时可以做空利润,即做空螺纹钢做多铁矿石焦炭。当钢厂利润处于底位时,可以做多利润,即做多螺纹钢做空铁矿石焦炭

对于钢厂利润套利策略,该示例策略仅用于示范,实盘时请根据自己的策略/经验进行评估。

头寸计算

需要注意的是铁矿石合约和焦炭合约一手是100吨,螺纹钢合约一手是10吨。那么就需要计算出一个合理的对冲头寸。让一次对冲的比例为:螺纹钢吨数 : 铁矿石吨数 : 焦炭吨数 = 1 : 1.6 : 0.5

策略计算出最小的下单对冲组合手数分别是:

img

rb手数:100 ,换算吨数:1000吨 i手数:16,换算吨数:1600吨 j手数:5,换算吨数:500吨 达成比例1000 :1600 : 500 = 螺纹钢吨数 : 铁矿石吨数 : 焦炭吨数 = 1 : 1.6 : 0.5

可见,100手rb合约,16手i合约,5手j合约是最小的整数组合了。如果允许偏差一点也可以100:16:5比例下减小5倍即:20:3:1。即使这样,这个保证金量也是不小的,看来这个玩法不太适合小散。

回测

使用布林指标捕获较大的偏差,做空钢厂利润。捕捉较小的偏差,做多钢厂利润。

img

img

img

策略源码

javascript
/*backtest start: 2021-03-01 09:00:00 end: 2021-07-28 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ // 参数 var symbol_rb = "rb2201" var symbol_j = "j2201" var symbol_i = "i2201" var bollPeriod = 50 // 全局变量 var q = $.NewTaskQueue() var p = $.NewPositionManager() function calcCommonMultiple(a, b, c) { var divisor = -1 var commonMultiple = a * b * c for (var i = 1 ; i < (commonMultiple / Math.min(a, b, c)) ; i++) { if (commonMultiple / a % i == 0 && commonMultiple / b % i == 0 && commonMultiple / c % i == 0) { divisor = i } } if (divisor == -1) { throw "没有找到" } else { return commonMultiple / divisor } } function main() { var preTS = 0 var isFirst = true var initAccount = null while (true) { Sleep(1000) if (exchange.IO("status")) { if (!$.IsTrading(symbol_rb) || !$.IsTrading(symbol_i) || !$.IsTrading(symbol_j)) { continue } if (isFirst) { initAccount = _C(exchange.GetAccount) isFirst = false } // 订阅螺纹钢合约 var info_rb = exchange.SetContractType(symbol_rb) if (!info_rb) { continue } var records_rb = exchange.GetRecords() // 订阅铁矿石合约 var info_i = exchange.SetContractType(symbol_i) if (!info_i) { continue } var records_i = exchange.GetRecords() // 订阅焦炭合约 var info_j = exchange.SetContractType(symbol_j) if (!info_j) { continue } var records_j = exchange.GetRecords() // 校验records数据 if (!records_rb || !records_i || !records_j || Math.min(records_rb.length, records_i.length, records_j.length) < bollPeriod) { continue } // 更新持仓 var holds = {rb : 0, i : 0, j : 0} var pos = _C(exchange.GetPosition) for (var i = 0 ; i < pos.length ; i++) { if (pos[i].ContractType == symbol_rb) { if (pos[i].Type == PD_LONG || pos[i].Type == PD_LONG_YD) { holds.rb += pos[i].Amount } else { holds.rb -= pos[i].Amount } } else if (pos[i].ContractType == symbol_i) { if (pos[i].Type == PD_LONG || pos[i].Type == PD_LONG_YD) { holds.i += pos[i].Amount } else { holds.i -= pos[i].Amount } } else if (pos[i].ContractType == symbol_j) { if (pos[i].Type == PD_LONG || pos[i].Type == PD_LONG_YD) { holds.j += pos[i].Amount } else { holds.j -= pos[i].Amount } } } // minCommonMultiple var minCommonMultiple = calcCommonMultiple(info_rb.VolumeMultiple, info_i.VolumeMultiple, info_j.VolumeMultiple) var amount_rb = minCommonMultiple / info_rb.VolumeMultiple * 10 var amount_i = minCommonMultiple / info_i.VolumeMultiple * 16 var amount_j = minCommonMultiple / info_j.VolumeMultiple * 5 // 一般情况是1.5-1.6(矿石)+0.4-0.5(焦炭)=1吨粗钢 , 1 : 1.6 : 0.5 = 10 : 16 : 5 var r = [] for (var i = Math.min(records_rb.length, records_i.length, records_j.length) ; i > 0 ; i--) { var bar_rb = records_rb[records_rb.length - i] var bar_i = records_i[records_i.length - i] var bar_j = records_j[records_j.length - i] r.push(bar_rb.Close - 1.6 * bar_i.Close - 0.5 * bar_j.Close) } if (r.length < bollPeriod) { continue } var boll = TA.BOLL(r, bollPeriod, 2) var up = boll[0] var down = boll[2] if (records_rb[records_rb.length - 1].Time == records_i[records_i.length - 1].Time && records_i[records_i.length - 1].Time == records_j[records_j.length - 1].Time && preTS != records_j[records_j.length - 1].Time) { preTS = records_j[records_j.length - 1].Time $.PlotLine("close", r[r.length - 2], preTS) $.PlotLine("up", up[up.length - 2], preTS) $.PlotLine("down", down[down.length - 2], preTS) } // 判断触发条件 if (r[r.length - 1] > up[up.length - 1] && holds.rb == 0 && holds.i == 0 && holds.j == 0) { // 空rb多i,j q.pushTask(exchange, symbol_rb, "sell", amount_rb, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_i, "buy", amount_i, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_j, "buy", amount_j, function(task, ret) { Log(task.desc, ret) $.PlotFlag(new Date().getTime(), '空rb多i,j', 'up') }) } }) } }) } else if (r[r.length - 1] < down[down.length - 1] && holds.rb == 0 && holds.i == 0 && holds.j == 0) { // 多rb空i,j q.pushTask(exchange, symbol_rb, "buy", amount_rb, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_i, "sell", amount_i, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_j, "sell", amount_j, function(task, ret) { Log(task.desc, ret) $.PlotFlag(new Date().getTime(), '多rb空i,j', 'down') }) } }) } }) } else if (r[r.length - 1] > up[up.length - 1] && holds.rb > 0 && holds.i < 0 && holds.j < 0) { // 平rb多,平i,j空 q.pushTask(exchange, symbol_rb, "coverall", -1, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_i, "coverall", -1, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_j, "coverall", -1, function(task, ret) { Log(task.desc, ret) LogProfit(_C(exchange.GetAccount).Balance - initAccount.Balance) $.PlotFlag(new Date().getTime(), '平多rb平空i,j', 'up') }) } }) } }) } else if (r[r.length - 1] < down[down.length - 1] && holds.rb < 0 && holds.i > 0 && holds.j > 0) { // 平rb空,平i,j多 q.pushTask(exchange, symbol_rb, "coverall", -1, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_i, "coverall", -1, function(task, ret) { Log(task.desc, ret) if (ret) { q.pushTask(exchange, symbol_j, "coverall", -1, function(task, ret) { Log(task.desc, ret) LogProfit(_C(exchange.GetAccount).Balance - initAccount.Balance) $.PlotFlag(new Date().getTime(), '平空rb平多i,j', 'down') }) } }) } }) } q.poll() } else { LogStatus(_D()) } } }

策略需要勾选「画线类库」、「商品期货交易类库」

img

策略仅用于研究学习,实盘慎用。

相关推荐
评论
全部评论 (2)

    问下画线类库在哪里没找到

    5 年前

    可以在策略广场里搜索:https://www.fmz.cn/square

    5 年前
  • 1
iPhone 下载
社区
回测系统
© 2015 - ∞ YouQuant 豫ICP备19046564号