双平台对冲台历
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
}
}
}