linux007
12/25/2017 - 10:33 AM

套利

双平台对冲台历

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
        }
    }
}