商品期货对冲 + 网格
传统的跨期对冲一般指统计套利, 用线性回归或者其它办法生成一个套利区间, 这样套利机会比较少, 而且有预测性, 未来价差很可能不是预计的那样回归
为了解决这种办法, 进而更频繁的进行套利操作, 我们把两个关联品种或者跨期品种的套利价差定义成一个网格, 每满足一定的价差就开一次仓, 做一次对冲
这样价差来回在我们设置的网格里进行波动,我们就能不断的开仓平仓实现盈利.
不善文案, 不做文字表达了, 具体看策略代码
/*backtest start: 2017-03-07 09:00:00 end: 2017-04-05 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ function Hedge(q, e, positions, symbolA, symbolB, hedgeSpread) { var self = {} self.q = q self.symbolA = symbolA self.symbolB = symbolB self.name = symbolA + " & " + symbolB self.e = e self.isBusy = false self.diffA = 0 self.diffB = 0 self.update = _D() var arr = hedgeSpread.split(';') self.dic = [] var n = 0 var coefficient = 1 for (var i = 0; i < positions.length; i++) { if (positions[i].ContractType == symbolA) { n += positions[i].Amount if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) { coefficient = -1 } } } _.each(arr, function(pair) { var tmp = pair.split(':'); if (tmp.length != 3) { throw "开仓表不正确"; } var st = { open: Number(tmp[0]), cover: Number(tmp[1]), amount: Number(tmp[2]), hold: 0 } if (n > 0) { var m = Math.min(n, st.amount) n -= m st.hold = m * coefficient Log("恢复", self.name, st) } self.dic.push(st) }); if (n > 0) { throw "恢复失败, 有多余仓位 " + n; } self.poll = function() { if (self.isBusy || (!$.IsTrading(self.symbolA))) { return } var insDetailA = exchange.SetContractType(self.symbolA) if (!insDetailA) { return } var tickerA = exchange.GetTicker() if (!tickerA) { return } var insDetailB = exchange.SetContractType(self.symbolB) if (!insDetailB) { return } var tickerB = exchange.GetTicker() if (!tickerB) { return } self.update = _D(tickerA.Time) var action = null var diffA = _N(tickerA.Buy - tickerB.Sell) var diffB = _N(tickerA.Sell - tickerB.Buy) self.diffA = diffA self.diffB = diffB for (var i = 0; i < self.dic.length && !action; i++) { if (self.dic[i].hold == 0) { if (self.dic[i].open <= diffA) { action = [i, "sell", "buy", self.dic[i].amount] } else if (self.dic[i].open <= -diffB) { action = [i, "buy", "sell", -self.dic[i].amount] } } else { if (self.dic[i].hold > 0 && self.dic[i].cover >= diffB) { action = [i, "closesell", "closebuy", self.dic[i].hold] } else if (self.dic[i].hold < 0 && self.dic[i].cover >= -diffA) { action = [i, "closebuy", "closesell", self.dic[i].hold] } } } if (!action) { return } Log("A卖B买: " + _N(diffA) + ", A买B卖: " + _N(diffB), ", Action: " + JSON.stringify(action)) self.isBusy = true self.q.pushTask(self.e, self.symbolA, action[1], self.dic[action[0]].amount, function(task, ret) { if (!ret) { self.isBusy = false return } self.q.pushTask(self.e, self.symbolB, action[2], self.dic[action[0]].amount, function(task, ret) { if (!ret) { throw "开仓失败..." } self.isBusy = false if (task.action != "buy" && task.action != "sell") { self.dic[action[0]].hold = 0; } else { self.dic[action[0]].hold = action[3]; } }) }) } return self } function main() { SetErrorFilter("ready|login|timeout") Log("正在与交易服务器连接...") while (!exchange.IO("status")) Sleep(1000); Log("与交易服务器连接成功") var mode = exchange.IO("mode", 0); if (typeof(mode) !== 'number') { throw "切换模式失败, 请更新到最新托管者!"; } else { Log("已切换到适合多品种价格查询的立即模式"); } if (CoverAll) { Log("开始平掉所有残余仓位..."); $.NewPositionManager().CoverAll(); Log("操作完成"); } LogStatus("尝试获取持仓状态") var positions = _C(exchange.GetPosition) LogStatus("Ready") if (positions.length > 0 && !AutoRestore) { throw "发现持仓, 请勾选自动恢复" } var pairs = [] var q = $.NewTaskQueue(function(task, ret) { Log(task.desc, ret ? "成功" : "失败") }) var arr = HedgeTable.split('('); var tbl = { type: 'table', title: 'Runtime', cols: ['Pair', 'Open', 'Cover', 'Hold', 'DiffA', 'DiffB', 'Time'], rows: [] }; _.each(arr, function(item) { if (item != '') { var tmp = item.split(')'); var pair = tmp[0].replace('(', '').split('&'); var symbolDetail = _C(exchange.SetContractType, pair[0]) Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "份, 最大下单量", symbolDetail.MaxLimitOrderVolume, "保证金率:", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), "交割日期", symbolDetail.StartDelivDate); symbolDetail = _C(exchange.SetContractType, pair[1]) Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "份, 最大下单量", symbolDetail.MaxLimitOrderVolume, "保证金率:", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), "交割日期", symbolDetail.StartDelivDate); pairs.push(Hedge(q, exchanges[0], positions, pair[0], pair[1], tmp[1])) } }); var ts = 0 var lastUpdate = 0 while (true) { if (!exchange.IO("status")) { Sleep(1000) continue } var now = new Date().getTime() if (now - ts > (CalcPeriod * 60000)) { var account = exchange.GetAccount() if (account) { var obj = JSON.parse(exchange.GetRawJSON()) $.PlotLine('账户权益', obj['Balance'] + obj['PositionProfit']); ts = now } } // IO("wait") 会一直等待收到任何一个品种的行情推送信息, 返回收到行情的真实时间 var n = exchange.IO("wait_any") // 计算行情信息传到策略层花费的时间 var idle = UnixNano() - n if (now - lastUpdate > 5000) { tbl.rows = [] _.each(pairs, function(t) { for (var i = 0; i < t.dic.length; i++) { tbl.rows.push([t.name, t.dic[i].open, t.dic[i].cover, t.dic[i].hold, t.diffA, t.diffB, t.update]) } }); LogStatus('`' + JSON.stringify(tbl) + '`\nUpdate: ' + _D() + ', Idle: ' + (idle/1000000) + ' ms') lastUpdate = now } _.each(pairs, function(t) { t.poll() }); q.poll() } }template: strategy.tpl:40:21: executing "strategy.tpl" at <.api.GetStrategyListByName>: wrong number of args for GetStrategyListByName: want 7 got 6