双平台对冲套利策略(BotVS—注释版)

2023-02-22 10:34发布

简单的双平台对冲套利策略,适合新手学习入门用,存在优化空间,欢迎来到BotVS 大家一起相互学习!function cancelAll(){ //取
1条回答
1楼 · 2023-02-22 10:47.采纳回答

简单的双平台对冲套利策略,适合新手学习入门用,存在优化空间,欢迎来到BotVS 大家一起相互学习!

function cancelAll(){ //取消所有订单 var ref = false; //记录是否有委托订单 for(var e in exchanges){ //遍历每个交易所 while(true){ var n = 0; var my_orders = _C(exchanges[e].GetOrders); //获得该交易所的未成交委托单 for(var order1 in my_orders){ //遍历委托单数组 ref = true; e.CancelOrder(my_orders[order1].Id); //取消该委托单 n += 1; } if(n==0){ break; } } } return ret } function main(){ if(exchanges.length != 2){ //如果没有设置俩个交易所,则抛出错误 throw("Only two exchanges are supported"); } LogReset(); //清空log LogProfitReset(); cancelAll(); //取消所有订单 var initStocks = 0.0; //初始的数字货币数量 var initBalance = 0.0; //初始的法币数量 var minAmount = 0.1; //最小交易量 var lastTradeTime = 0; //最后一次交易的时间 var lastTradeErrExchange = ''; //最近发生交易错误的交易所 var accountsCache = []; //账户信息数组 var hedgeNum = [0, 0]; // var names = []; //交易所名称数组 var baseCurrency = exchange.GetCurrency(); //交易的目标货币 for(var e in exchanges){ //遍历交易所 if(exchanges[e].GetCurrency() != baseCurrency){ //如果俩个交易所交易的货币不一致 throw("It has to be the same currency to hedge:"+baseCurrency); //抛出错误 } names.push(exchanges[e].GetName()); //将交易所名字加入数组 var account = _C(exchanges[e].GetAccount); //获得交易所账户信息 accountsCache.push(account); //将账户信息加入数组 initStocks += account.Stocks; //计算所有数字货币总额 initBalance += account.Balance; //计算所有法币总额 } if(baseCurrency == "BTC"){ //计算最小交易量 minAmount = 0.01 } else { minAmount = 0.1 } Log("all balance:", _N(initBalance), "all stocks", _N(initStocks)) while(true){ if(accountsCache.length <= 0){ //如果账户数组为空,则初始化 for(var e in exchanges){ var account1 = _C(exchanges[e].GetAccount); accountsCache.push(account1); } } Sleep(LoopInterval); cmd = GetCommand(); //获取外部用户指令 if(cmd){ Log("CMD", cmd); var arr = cmd.split(":"); //分割指令 if(arr[0]=="A->B"){ //设置对冲方向 MinSpreadA = parseFloat(arr[1]); //设置差价 } else if(arr[0]=="B->A"){ MinSpreadB = parseFloat(arr[1]); } } var depthA = exchanges[0].GetDepth(); //获得A交易所委托单簿 if (!depthA){ continue; } var depthB = exchanges[1].GetDepth(); //获得B交易所委托单簿 if (!depthB){ continue; } var time = new Date(); //用来获取时间戳 if(lastTradeTime > 0 && time.getTime() - lastTradeTime > BalanceTime){ //如果时间间隔超过设定的平衡账户间隔时间 var needUpdate = cancelAll(); //取消所有委托单 if (!needUpdate){ //如果没找到有未成交的委托单 for(var account2 in accountsCache){ //检查账户 if(accountsCache[account2].FrozenBalance >= 0.1 || accountsCache[account2].FrozenStocks >= 0.001){//是否有冻结资金 needUpdate = true; break; } } } if (needUpdate){ //如果有未成交的委托单或者有资金被冻结 for(var k in exchanges){ //遍历交易所 account3 = _C(exchanges[k].GetAccount); //获取交易所账户信息 accountsCache.push(account3); //将账户信息放入数组 } } var nowStocks = 0.0; var nowBalance = 0.0; for(var account4 in accountsCache){ //遍历账户信息数组 nowBalance += accountsCache[account4].Balance; //计算法币数量 nowStocks += accountsCache[account4].Stocks; //计算数字货币数量 } var diff = _N(nowStocks-initStocks, 5); //计算现在的数字货币数量和初始数量的差 var isReverse; if(Math.abs(diff) < minAmount){ //如果差大于最小成交量 LogProfit(_N(nowBalance-initBalance, 3), "all balance", _N(nowBalance), "all stocks", _N(nowStocks), "stock offset:", diff); //打印状态 lastTradeTime = 0; } else if(diff > minAmount){ //判断平衡方向 isReverse = depthA.Bids[0].Price < depthB.Bids[0].Price; } else if(-diff > minAmount){ isReverse = depthA.Asks[0].Price > depthB.Asks[0].Price; } if(isReverse != null){ //如果需要平衡 var depths = [depthA, depthB]; //A,B现在的市场订单簿深度 var opAmount; var kk; if(isReverse){ //得出需要平衡的交易所下标 kk = [1, 0]; } else{ kk = [0, 1]; } for(var pos in kk){ if(diff > minAmount){ //如果差距大于最小交易量 opAmount = Math.min(diff, accountsCache[pos].Stocks, depths[pos].Bids[0].Amount + depths[pos].Bids[1].Amount); //计算平衡的货币数量 diff = -opAmount; if(opAmount >= minAmount){ exchanges[pos].Sell(depths[pos].Bids[1].Price, opAmount); //卖出货币 } } else if(-diff >= minAmount){ //同上 opAmount = Math.min(-diff, _N(accountsCache[pos].Balance / depths[pos].Asks[1].Price, 3), depths[pos].Asks[0].Amount + depths[pos].Asks[1].Amount); diff += opAmount; if (opAmount >= minAmount){ exchanges[pos].Buy(depths[pos].Asks[1].Price, opAmount); } } } if (opAmount != undefined){ var time1 = new Date(); lastTradeTime = time1.getTime(); //更新最后平衡时间戳 accountsCache = []; } } continue; } var diffA = _N(depthA.Bids[0].Price - depthB.Asks[0].Price, 3) //计算AB交易所之间的差价 var diffB = _N(depthB.Bids[0].Price - depthA.Asks[0].Price, 3) LogStatus(JSON.stringify({type: 'table', title: 'status', cols: ['name', 'money', 'frozenmoney', 'stock', 'frozenstock', 'buyone', 'sellone', 'threshold', 'offset', 'num of times'], rows: [[names[0], accountsCache[0].Balance, accountsCache[0].FrozenBalance, accountsCache[0].Stocks, accountsCache[0].FrozenStocks, depthA.Bids[0].Price, depthA.Asks[0].Price, MinSpreadA, diffA, hedgeNum[0]], [names[1], accountsCache[1].Balance, accountsCache[1].FrozenBalance, accountsCache[1].Stocks, accountsCache[1].FrozenStocks, depthB.Bids[0].Price, depthB.Asks[0].Price, MinSpreadB, diffB, hedgeNum[0]]]})); HPos = 0; if (diffA >= MinSpreadA){ //如果差价大于设置的最小操作数量 orderH = depthA.Bids[0]; //买一价 orderL = depthB.Asks[0]; //卖一价 exchangeH = 0; //价格高的交易所下标 exchangeL = 1; //价格低的交易所下标 accountH = accountsCache[0]; //价格高的交易所账户信息 accountL = accountsCache[1]; //价格低的交易所账户信息 } else if (diffB >= MinSpreadB){ HPos = 1; orderH = depthB.Bids[0]; orderL = depthA.Asks[0]; exchangeH = 1; exchangeL = 0; accountH = accountsCache[1]; accountL = accountsCache[0]; } else{ continue; } var opPrice = _N((orderH.Price + orderL.Price) / 2.0, 2); //计算平均价钱 var opAmount = Math.min(MaxAmount, orderH.Amount, orderL.Amount, accountH.Stocks, _N(accountL.Balance / opPrice, 3)); //计算交易下单数量 if(opAmount >= minAmount){ //当下单数量大于最小交易量 var tasks = [[exchangeH, "H"], [exchangeL, "L"]]; //准备下单列表 if(lastTradeErrExchange == "L"){ tasks.reverse(); } lastTradeErrExchange = ""; for(var task in tasks){ //执行下单列表 if(tasks[task][1] == "H"){ var id = exchanges[tasks[task][0]].Sell(opPrice, opAmount); if(id == undefined){ lastTradeErrExchange = tasks[task][1]; break; } } if(tasks[task][1] == "L"){ var id = exchanges[tasks[task][0]].Buy(opPrice, opAmount); if(id == undefined){ lastTradeErrExchange = tasks[task][1]; break; } } } var time = new Date(); lastTradeTime = time.getTime(); //更新最后一次下单时间戳 accountsCache = [] hedgeNum[HPos] += 1 } } }