Article détaillé:https://www.fmz.com/digest-topic/7666
var lastKeepBalanceTS = 0 function hedge(buyEx, sellEx, priceBuy, priceSell, amount) { var buyRoutine = buyEx.Go("Buy", priceBuy, amount) var sellRoutine = sellEx.Go("Sell", priceSell, amount) Sleep(500) buyRoutine.wait() sellRoutine.wait() } function getDepthPrice(depth, side, amount) { var arr = depth[side] var sum = 0 var price = null for (var i = 0 ; i < arr.length ; i++) { var ele = arr[i] sum += ele.Amount if (sum >= amount) { price = ele.Price break } } return price } function keepBalance(initAccs, nowAccs, depths) { var initSumStocks = 0 var nowSumStocks = 0 _.each(initAccs, function(acc) { initSumStocks += acc.Stocks + acc.FrozenStocks }) _.each(nowAccs, function(acc) { nowSumStocks += acc.Stocks + acc.FrozenStocks }) var diff = nowSumStocks - initSumStocks // 计算币差 if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) { Log("触发平衡操作,平衡量:", Math.abs(diff)) var index = -1 var available = [] var side = diff > 0 ? "Bids" : "Asks" for (var i = 0 ; i < nowAccs.length ; i++) { var price = getDepthPrice(depths[i], side, Math.abs(diff)) if (side == "Bids" && nowAccs[i].Stocks * 0.9 > Math.abs(diff)) { available.push(i) } else if (side == "Asks" && price && nowAccs[i].Balance / price * 0.9 > Math.abs(diff)) { available.push(i) } } for (var i = 0 ; i < available.length ; i++) { if (index == -1) { index = available[i] } else { var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff)) var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff)) if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) { index = available[i] } else if (side == "Asks" && priceIndex && priceI && priceI < priceIndex) { index = available[i] } } } if (index == -1) { Log("无法平衡") } else { // 平衡下单 var price = getDepthPrice(depths[index], side, Math.abs(diff)) if (price) { var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy tradeFunc(price, Math.abs(diff)) } else { Log("价格无效", price) } } return false } else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) { Log("错误:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length) return true } else { return true } } function cancelAll() { _.each(exchanges, function(ex) { while (true) { var orders = _C(ex.GetOrders) if (orders.length == 0) { break } for (var i = 0 ; i < orders.length ; i++) { ex.CancelOrder(orders[i].Id, orders[i]) Sleep(500) } } }) } function updateAccs(arrEx) { var ret = [] for (var i = 0 ; i < arrEx.length ; i++) { var acc = arrEx[i].GetAccount() if (!acc) { return null } ret.push(acc) } return ret } function main() { var exA = exchanges[0] var exB = exchanges[1] // 精度,汇率设置 if (rateA != 1) { // 设置汇率A exA.SetRate(rateA) Log("交易所A设置汇率:", rateA, "#FF0000") } if (rateB != 1) { // 设置汇率B exB.SetRate(rateB) Log("交易所B设置汇率:", rateB, "#FF0000") } exA.SetPrecision(pricePrecisionA, amountPrecisionA) exB.SetPrecision(pricePrecisionB, amountPrecisionB) // 切换杠杆模式 for (var i = 0 ; i < exchanges.length ; i++) { if (exchanges[i].GetName() == "Binance" && marginType != 0) { if (marginType == 1) { Log(exchanges[i].GetName(), "设置为杠杆逐仓") exchanges[i].IO("trade_margin") } else if (marginType == 2) { Log(exchanges[i].GetName(), "设置为杠杆全仓") exchanges[i].IO("trade_super_margin") } } } if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("重置所有数据", "#FF0000") } var nowAccs = _C(updateAccs, exchanges) var initAccs = _G("initAccs") if (!initAccs) { initAccs = nowAccs _G("initAccs", initAccs) } var isTrade = false while (true) { var ts = new Date().getTime() var depthARoutine = exA.Go("GetDepth") var depthBRoutine = exB.Go("GetDepth") var depthA = depthARoutine.wait() var depthB = depthBRoutine.wait() if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) { Sleep(500) continue } var targetDiffPriceA2B = hedgeDiffPriceA2B var targetDiffPriceB2A = hedgeDiffPriceB2A if (diffAsPercentage) { targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * (hedgeDiffPercentageA2B / 100) targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * (hedgeDiffPercentageB2A / 100) } // 画图 $.PlotHLine(targetDiffPriceA2B, "A->B") $.PlotHLine(targetDiffPriceB2A, "B->A") if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足 var priceSell = depthA.Bids[0].Price - slidePrice var priceBuy = depthB.Asks[0].Price + slidePrice // 处理负精度参数 if (pricePrecisionA < 0) { // priceSell = _N(priceSell, pricePrecisionA) - slidePrice priceSell = _N(priceSell - slidePrice, pricePrecisionA) // Log("priceSell:", priceSell, "priceSell - slidePrice:", priceSell - slidePrice, "pricePrecisionA:", pricePrecisionA, typeof(pricePrecisionA)) // 测试 } if (pricePrecisionB < 0) { // priceBuy = _N(priceBuy, pricePrecisionB) + slidePrice priceBuy = _N(priceBuy + slidePrice, pricePrecisionB) // Log("priceBuy:", priceBuy, "priceBuy + slidePrice:", priceBuy + slidePrice, "pricePrecisionB:", pricePrecisionB, typeof(pricePrecisionB)) // 测试 } var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) { amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount) Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // 提示信息 hedge(exB, exA, priceBuy, priceSell, amount) cancelAll() lastKeepBalanceTS = 0 isTrade = true } } else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足 var priceBuy = depthA.Asks[0].Price + slidePrice var priceSell = depthB.Bids[0].Price - slidePrice // 处理负精度参数 if (pricePrecisionA < 0) { // priceBuy = _N(priceBuy, pricePrecisionA) + slidePrice priceBuy = _N(priceBuy + slidePrice, pricePrecisionA) // Log("priceBuy:", priceBuy, "priceBuy + slidePrice:", priceBuy + slidePrice, "pricePrecisionA:", pricePrecisionA, typeof(pricePrecisionA)) // 测试 } if (pricePrecisionB < 0) { // priceSell = _N(priceSell, pricePrecisionB) - slidePrice priceSell = _N(priceSell - slidePrice, pricePrecisionB) // Log("priceSell:", priceSell, "priceSell - slidePrice:", priceSell - slidePrice, "pricePrecisionB:", pricePrecisionB, typeof(pricePrecisionB)) // 测试 } var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) { amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount) Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // 提示信息 hedge(exA, exB, priceBuy, priceSell, amount) cancelAll() lastKeepBalanceTS = 0 isTrade = true } } if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) { nowAccs = _C(updateAccs, exchanges) var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB]) cancelAll() if (isBalance) { lastKeepBalanceTS = ts if (isTrade) { var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0) var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0) LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs) isTrade = false } } $.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price) $.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) } // 交互 var cmd = GetCommand() if (cmd) { Log("接收到命令:", cmd) var arr = cmd.split(":") if (arr[0] == "A2B") { Log("修改A2B的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1]) if (diffAsPercentage) { hedgeDiffPercentageB2A = parseFloat(arr[1]) } else { hedgeDiffPriceA2B = parseFloat(arr[1]) } } else if (arr[0] == "B2A") { Log("修改B2A的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1]) if (diffAsPercentage) { hedgeDiffPercentageA2B = parseFloat(arr[1]) } else { hedgeDiffPriceB2A = parseFloat(arr[1]) } } } var tbl = { "type" : "table", "title" : "数据", "cols" : ["交易所", "币", "冻结币", "计价币", "冻结计价币", "触发差价", "当前差价"], "rows" : [], } tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)]) tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)]) LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`") Sleep(1000) } }