合约对冲_下单多线程版本

Author: 醉里挑灯看剑, Date: 2019-01-10 21:53:37
Tags:

老早以前开发的,JavaScript属于便学便开发,不喜忽喷, 有兴趣的可以在二次开发更新。 微信:fzqtdkj


var ContractTypeIdx =[] //合约类型
var MarginLevelIdx  =[] //杠杆
var VarietiesIndx   =[] //交易品种

var PriceCancelRatio =[]; //撤单比例
var Slide =[];  //滑动值

var CloseNarrowSpreadList =[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]; //同一方向平仓值


var  curVarietiesIndex =[0,0,0,0,0,0,0,0,0,0,0,0]; //0 当前执行品种下标 1当前平台执行标记
var  curPlatformIndex  =[]; //用于标记当前哪两个平台在对比价差(比如说下标0、下标1的交易所相互对比)

var  ArbitrageSpreadList =[[],[],[],[],[],[],[],[],[],[],[],[]];   //套利价差
var  ArbitrageAmountList =[[],[],[],[],[],[],[],[],[],[],[],[]];   //交易数量

var OrderInfoArry  =[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]];    //我的订单信息(主要记录订单类型与订单号)

var CanCelOrderCheckTimer =[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];        //撤销订单检测时间

var LastBarTime    =[]; 

var DifferentHistory =[0,0,0,0,0] //价差历史


/////k线行情信息//
var __lastDiff = 0;
 

var cfg = {
			tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
			title : { text : '差价分析图'},
			rangeSelector: {
                buttons:  [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
                selected: 0,
                inputEnabled: false
            },
			xAxis: { type: 'datetime'},
			yAxis : {
				plotLines : [{
					value : 0.0,
					color : 'black',
					dashStyle : 'shortdash',
					width : 3,
				}, {
					value : NormalDiff,
					color : 'green',
					dashStyle : 'shortdash',
					width : 1,
				}, {
					value : HighDiff,
					color : 'red',
					dashStyle : 'shortdash',
					width : 1,
				},{
					value : -NormalDiff,
					color : 'green',
					dashStyle : 'shortdash',
					width : 1,
				}, {
					value : -HighDiff,
					color : 'red',
					dashStyle : 'shortdash',
					width : 1,
				}]
			},
			series : [{
				name : '价差',
				data : [],
				tooltip: {
					valueDecimals: 2
				}
			}]
		};
function _N(v, precision) {
    if (typeof(precision) != 'number') {
        precision = 4;
    }
    var d = parseFloat(v.toFixed(Math.max(10, precision+5)));
    s = d.toString().split(".");
    if (s.length < 2 || s[1].length <= precision) {
        return d;
    }

    var b = Math.pow(10, precision);
    return Math.floor(d*b)/b;
}

function GetTicker(e) {
    if (typeof(e) == 'undefined') {
        e = exchange;
    }
    var ticker;
    while (!(ticker = e.GetTicker())) {
        Sleep(Interval);
    }
    return ticker;
}



var curArbitrageInfo =  //当前信息
   [ {
        SpreadIndex:0,    //价差下标
        UsedAmount:0,   //当前价差,已用数量
        LastArbitrageType:0 //最后一次套利类型,若套利类型发生变种,则套利价差下标也需进行变动.
    },
    {
        SpreadIndex:0,    //价差下标
        UsedAmount:0,   //当前价差,已用数量
        LastArbitrageType:0 //最后一次套利类型,若套利类型发生变种,则套利价差下标也需进行变动.
    },   
    {
        SpreadIndex:0,    //价差下标
        UsedAmount:0,   //当前价差,已用数量
        LastArbitrageType:0 //最后一次套利类型,若套利类型发生变种,则套利价差下标也需进行变动.
    },
    {
        SpreadIndex:0,    //价差下标
        UsedAmount:0,   //当前价差,已用数量
        LastArbitrageType:0 //最后一次套利类型,若套利类型发生变种,则套利价差下标也需进行变动.
    },    
   ];

//数量转换
function CoinToSheetsAmount(curPrice,CoinAmount,ContractPrice)
{
   var value =curPrice *CoinAmount / ContractPrice;
    if(value <1) {
      value =1;
    }
    return _N(value, 0);
}

//张数转换为数量
function SheetsToCoinAmount(curPrice,SheetsAmount,ContractPrice)
{
   var value =SheetsAmount * ContractPrice/curPrice;
// Log("SheetsAmount",SheetsAmount,"ContractPrice",ContractPrice,"curPrice",curPrice);
    return _N(value, 3);
}


//PD_LONG 多头仓位   PD_SHORT 空头仓位
function GetPosition(Index,posType) {
    var positions = _C(exchanges[Index].GetPosition);
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].Type === posType) {
            return [positions[i].Price, positions[i].Amount];
        }
    }
    return [0, 0];
}

//获取交易所基础信息
function getExchangesBaseInfo() {

    var details = [];
    for (var i = 0; i < exchanges.length; i++) {   //只获取当前对比的两个交易所..
       var account  =null;
       var ticker   =null;
       var LongPosition  =null;
       var ShortPosition =null;
       var depth    =null;
       
       if(exchanges[curPlatformIndex[i]].GetName() == 'Futures_OKCoin' ||
          exchanges[curPlatformIndex[i]].GetName() == 'Futures_HuobiDM')
       {//合约则获取持仓信息
          LongPosition  = GetPosition(curPlatformIndex[i],PD_LONG);
          ShortPosition = GetPosition(curPlatformIndex[i],PD_SHORT);
       }
        
       var accountTemp =exchanges[curPlatformIndex[i]].Go("GetAccount"); 
       var tickerTemp  =exchanges[curPlatformIndex[i]].Go("GetTicker");
       var depthTemp   =exchanges[curPlatformIndex[i]].Go("GetDepth");
        
       ticker   =tickerTemp.wait();
       depth    =depthTemp.wait();
       account  =accountTemp.wait();
        if(ticker ==null ||depth ==null ||  account ==null)
        { //若数据获取失败了,则直接pass..
           break;
        }
      details.push({account: account, ticker: ticker,depth: depth,LongPosition:LongPosition,ShortPosition:ShortPosition});
        
    }
    return details;
}

//获取价格差  参数:现货下标,数据集,类型
function GetPriceDifferent(ExchangeInfo)
{
    var Different =0;
    var Type      =0;
    //正向计算获取价差   如果合约价格 >现货价格,则按 正的方式进行计算价差,否则按反的方式计算..
    
    var curSpread =ArbitrageSpreadList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex];
    var curUsedAmount =curArbitrageInfo[curVarietiesIndex[1]].UsedAmount;
    var curMaxArbitrageAmount =ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex] ;
    
    var BuyAveragePrice  =[0,0,0,0,0,0];  //买均价
    var SellAveragePrice =[0,0,0,0,0,0];  //卖均价
    
    for(var n =0;n<=BuyDepthIndex;n++)
    {//买价
        BuyAveragePrice[0] +=ExchangeInfo[0].depth.Bids[n].Price;
        BuyAveragePrice[1] +=ExchangeInfo[1].depth.Bids[n].Price;        
    }

     BuyAveragePrice[0] =BuyAveragePrice[0] /(BuyDepthIndex+1); //均价
     BuyAveragePrice[1] =BuyAveragePrice[1] /(BuyDepthIndex+1); //均价    

    for(var n =0;n<=SellDepthIndex;n++)
    {//卖价
        SellAveragePrice[0] +=ExchangeInfo[0].depth.Asks[n].Price;
        SellAveragePrice[1] +=ExchangeInfo[1].depth.Asks[n].Price;        
    }

    SellAveragePrice[0] =SellAveragePrice[0] /(SellDepthIndex+1); //计算出均价
    SellAveragePrice[1] =SellAveragePrice[1] /(SellDepthIndex+1); //计算出均价        
   
   if(BuyAveragePrice[0] >SellAveragePrice[1] )
   { //交易所0开空 ,交易所1开多
       Different =BuyAveragePrice[0] -SellAveragePrice[1];
       Type =1;
   }
   else if(BuyAveragePrice[1] >SellAveragePrice[0])   
   { //交易所1开空 ,交易所0开多
       Different =BuyAveragePrice[1] -SellAveragePrice[0];
       Type =2;
   }
    
    if(ExchangeInfo[0].depth.Bids[0].Price <ExchangeInfo[0].depth.Bids[1].Price ||
       ExchangeInfo[1].depth.Bids[0].Price <ExchangeInfo[1].depth.Bids[1].Price ||
       ExchangeInfo[0].depth.Asks[0].Price >ExchangeInfo[0].depth.Asks[1].Price ||
       ExchangeInfo[1].depth.Asks[0].Price >ExchangeInfo[1].depth.Asks[1].Price)
    {
       Type =0;
       Different =0;
       Log("平台抽风,价格排序不正确!!!!");
    }
    
   return  {TYPE:Type,DIFFERENT:Different};  //返回值 类型(正向期空现多 ==1 反向期多现空==2) ,价差
}



//动态计算数量,依据盘口挂单数量多少进行最合适的数量
function  CalcAmount(ExchangeInfo,Type)
{
   var  amount =0;
    var BuyTotalAmount  =[0,0,0,0,0,0,0];
    var SellTotalAmount =[0,0,0,0,0,0,0];
    for(var n =0; n<=BuyDepthIndex;n++)
    {
       BuyTotalAmount[0] +=ExchangeInfo[0].depth.Bids[n].Amount;
       BuyTotalAmount[1] +=ExchangeInfo[1].depth.Bids[n].Amount;        
    }
    
    for (var n =0;n <=SellDepthIndex;n++)
    {
       SellTotalAmount[0] +=ExchangeInfo[0].depth.Asks[n].Amount;
       SellTotalAmount[1] +=ExchangeInfo[1].depth.Asks[n].Amount;        
    }
    
   if(Type  ==1)
   { //交易所0开空 ,交易所1开多
        amount =Math.min(BuyTotalAmount[0],SellTotalAmount[1]);
       //计算两个平台买卖深度最小值(取最小值进行下单,防止深度不足坑的很..)
       //取出深度最小的值但是深度最小值不能大于限定的下单张数
        amount =Math.min(amount,ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex]);       
   }
   else if(Type ==2)
   {//交易所0开多,交易所1开空
        amount =Math.min(SellTotalAmount[0],BuyTotalAmount[1]);
       //取出深度最小的值但是深度最小值不能大于限定的下单张数
        amount =Math.min(amount,ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex]);
   }
   else if(Type ==3)
   {// 0 平空 , 1 平多
       amount =Math.min(SellTotalAmount[0],BuyTotalAmount[1]);
       //取出深度最小的值但是深度最小值不能大于限定的下单张数      
       var MinPostion = Math.min(ExchangeInfo[0].ShortPosition[1],ExchangeInfo[1].LongPosition[1]); //平仓,取数量最小一边的数量
       amount =Math.min(amount,MinPostion);
   }
   else if(Type ==4)
   {//平 交易所0平多,交易所1平空
       amount =Math.min(BuyTotalAmount[0],SellTotalAmount[1]);       
       var MinPostion = Math.min(ExchangeInfo[0].LongPosition[1],ExchangeInfo[1].ShortPosition[1]); //平仓,取数量最小一边的数量      
       amount =Math.min(amount,MinPostion);     
   }
    
    if(Type ==1 || Type ==2)
    {//只有是开仓类型时才进行这样的判断...
     //剩余数量(最大可开仓数量)
      var SurplusAmount =ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex] -curArbitrageInfo[curVarietiesIndex[1]].UsedAmount;
      amount =amount >SurplusAmount?SurplusAmount:amount;   
      amount =amount>0?amount:1; //默认为张数(这里加个三目,主要目的在于防止计算出数量为0的值..)
    }
    else if(Type ==3 || Type ==4)
    {//平仓修改后分批平仓后,平仓数量不能大于当前档位最大数量
        //求出上一次开仓下标,若上一次开仓下标小于0,则强制归置为0
       if(curArbitrageInfo[curVarietiesIndex[1]].UsedAmount ==0)
       {//若当前档位没有多余数量则直接取上一次开仓的数量进行平仓计算
         var MinSpreadIndex =curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex -1 <=0?0:curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex -1; //最小价差下标
         amount =Math.min(amount,ArbitrageAmountList[curVarietiesIndex[0]][MinSpreadIndex]);          
       }
       else 
       { //若当前档位有零散数量,则直接取以零散数量为主的数量
          amount =Math.min(amount,curArbitrageInfo[curVarietiesIndex[1]].UsedAmount);             
       }
    }
    
    return amount; //返回张数值..
}

//开始套利函数
function OpenArbitrage(DifferentInfo,ExchangeInfo)
{
   var TreadAmount = CalcAmount(ExchangeInfo,DifferentInfo.TYPE);
    
   var OrderIdA   =null;
   var OrderIdB   =null;
   var OrderAWait =null;
   var OrderBWait =null;
    
   if(DifferentInfo.TYPE ==1)
   { //交易所0开空 ,交易所1开多
       while(OrderIdA ==null &&OrderIdB ==null)
       {         
         if(OrderIdA ==null)
         {
           exchanges[curPlatformIndex[0]].SetDirection("sell");       //设置下单类型为做空
           OrderAWait = exchanges[curPlatformIndex[0]].Go("Sell",MarketState ==true?-1:ExchangeInfo[0].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]],TreadAmount);  //原始数量下单  
         }
         if(OrderIdB ==null)
         {
           exchanges[curPlatformIndex[1]].SetDirection("buy");           //设置下单类型为做多 
           OrderBWait = exchanges[curPlatformIndex[1]].Go("Buy",MarketState ==true?-1:ExchangeInfo[1].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]], TreadAmount); //数量以转换 
         }
          OrderIdA = OrderAWait.wait();
          OrderIdB = OrderBWait.wait();
           
          if(MarketState !=true &&(OrderIdA ==null || OrderIdB ==null)){ 
              ExchangeInfo = getExchangesBaseInfo();       //刷新数据(重新挂单)
          }
       }

       OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdA,Type:2});  //订单信息保存至容器     
       OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdB,Type:1});  //订单信息保存至容器              

   }   
   else if(DifferentInfo.TYPE ==2)
   { //交易所0开多,交易所1开空
       while(OrderIdA ==null &&OrderIdB ==null)
       {
         if(OrderIdA ==null)
         {           
           exchanges[curPlatformIndex[0]].SetDirection("buy");           //设置下单类型为做多 
           OrderAWait =exchanges[curPlatformIndex[0]].Go("Buy",MarketState ==true?-1:ExchangeInfo[0].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]], TreadAmount); //数量以转换     
         }
         if(OrderIdB ==null)
         {          
           exchanges[curPlatformIndex[1]].SetDirection("sell");       //设置下单类型为做空
           OrderBWait =exchanges[curPlatformIndex[1]].Go("Sell",MarketState ==true?-1:ExchangeInfo[1].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]],TreadAmount);  //原始数量下单  
         }
          OrderIdA = OrderAWait.wait();
          OrderIdB = OrderBWait.wait();  
           
          if(MarketState !=true &&(OrderIdA ==null || OrderIdB ==null)){ 
              ExchangeInfo = getExchangesBaseInfo();       //刷新数据(重新挂单)
          }           
       }
       
       OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdA,Type:1});          
       OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdB,Type:2});                 
   }
    
    
          curArbitrageInfo[curVarietiesIndex[1]].UsedAmount +=TreadAmount;
          if(curArbitrageInfo[curVarietiesIndex[1]].UsedAmount >=ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex])
          {
             curArbitrageInfo[curVarietiesIndex[1]].UsedAmount =0;
              //下标最低值为0,小于0则强制归0
             curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex =curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex++ >ArbitrageSpreadList[curVarietiesIndex[0]].length?curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex:curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex++;
          }
    
      //修改当前下标类型
       curArbitrageInfo[curVarietiesIndex[1]].LastArbitrageType =DifferentInfo.TYPE;          
      
}


//平仓 结束一轮套利
function  CloseArbitrage(DifferentInfo,ExchangeInfo)
{
   var TreadAmount = CalcAmount(ExchangeInfo,DifferentInfo.TYPE);
    
   var OrderIdA   =null;
   var OrderIdB   =null;
   var OrderAWait =null;
   var OrderBWait =null;
    
    if(TreadAmount >0)
    {
      if(DifferentInfo.TYPE ==3)
      { //平 交易所0开空 ,交易所1开多
     //         平空 ,    平多
       while(OrderIdA ==null &&OrderIdB ==null) {
           
          if(OrderIdA ==null){ 
             exchanges[curPlatformIndex[0]].SetDirection("closesell"); 
             OrderAWait =exchanges[curPlatformIndex[0]].Go("Buy",MarketState ==true?-1:ExchangeInfo[0].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]],TreadAmount); 
          }
           
           if(OrderIdB ==null){
             exchanges[curPlatformIndex[1]].SetDirection("closebuy");
             OrderBWait =exchanges[curPlatformIndex[1]].Go("Sell",MarketState ==true?-1:ExchangeInfo[1].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]], TreadAmount);  //卖出平多仓  
           }
           
           OrderIdA = OrderAWait.wait();
           OrderIdB = OrderBWait.wait();
          if(MarketState !=true &&(OrderIdA ==null || OrderIdB ==null)){ 
              ExchangeInfo = getExchangesBaseInfo();       //刷新数据(重新挂单)
          }                   
       }
         OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdA,Type:4});       
         OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdB,Type:3});             
      }
        
     else if(DifferentInfo.TYPE ==4)
     {//平 交易所0平多,交易所1平空
        while(OrderIdA ==null &&OrderIdB ==null) {     
          if(OrderIdA ==null)  {  
             exchanges[curPlatformIndex[0]].SetDirection("closebuy");
            OrderAWait =exchanges[curPlatformIndex[0]].Go("Sell",MarketState ==true?-1:ExchangeInfo[0].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]], TreadAmount);  //卖出平多仓    
          }
          if(OrderIdB ==null)  {
             exchanges[curPlatformIndex[1]].SetDirection("closesell"); 
             OrderBWait =exchanges[curPlatformIndex[1]].Go("Buy",MarketState ==true?-1:ExchangeInfo[1].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]],TreadAmount);
          }
            OrderIdA = OrderAWait.wait();
            OrderIdB = OrderBWait.wait();        
          if(MarketState !=true &&(OrderIdA ==null || OrderIdB ==null)){ 
              ExchangeInfo = getExchangesBaseInfo();       //刷新数据(重新挂单)
           }
        }
         OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdA,Type:3});           
         OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderIdB,Type:4});             
      }  
    }
    else 
    {
       Log("平仓失败,有一方可平仓数量小于0张!!!,类型:",DifferentInfo.TYPE);
	   return;
    }
   
   curArbitrageInfo[curVarietiesIndex[1]].UsedAmount -=TreadAmount;
   if(curArbitrageInfo[curVarietiesIndex[1]].UsedAmount <=0)
   {
       curArbitrageInfo[curVarietiesIndex[1]].UsedAmount =0;
       //下标最低值为0,小于0则强制归0
       curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex =curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex--<=0?0:curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex--;   
   }       
}


//合约价格撤单检测
function FuturePriceCancelCheck(Index,ExchangeInfo,CancelValue)
{
    if(IsVirtual() ==true)
    {//若是模拟不用撤单功能....
      return ;
    }
    
    var orders = exchanges[Index].GetOrders();   
    var Type    =0;
    var OrderId =0;
    
    if(orders ==null ||orders.length <=0)   {//没有未成交订单则直接返回....
      return; 
    }
    
    //主要目的用于修正类型(开多、开空、平多、平空)
    for(var l =0;l <OrderInfoArry[curVarietiesIndex[1]].length;l++)    {
       for(var n =0;n <orders.length;n++)   {
           if(orders[n].Id ==OrderInfoArry[curVarietiesIndex[1]][l].OrderId)  {
               orders[n].Type =OrderInfoArry[curVarietiesIndex[1]][l].Type;
               break;
           }
        }
    }
    
    for(var n =0;n <orders.length;n++)
    {
        if(Math.abs(orders[n].Price - ExchangeInfo[Index].ticker.Last) >CancelValue)
        {//若超出指定值,则进行撤单,重新挂单..

            if(exchanges[Index].CancelOrder(orders[n].Id) ==true)
            {
               Sleep(2000);  //延时2秒,主要防止撤销订单后服务器无法及时响应..
               var curOrderInfo  =_C(exchanges[Index].GetOrder,orders[n].Id);
               if(curOrderInfo.Status !=ORDER_STATE_CANCELED || curOrderInfo.Status ==ORDER_STATE_CLOSED)
               {
                   Log("撤销重新挂单,撤单失败,当前订单状态不为已撤单,返回...当前状态:",curOrderInfo.Status,"当前id",orders[n].Id);
                   return false;
               }
               Log("撤单 类型,",orders[n].Type,"数量:",orders[n].Amount -orders[n].DealAmount,"交易所下标:",Index,"订单id:",orders[n].Id);
              if(orders[n].Type ==1)
              {
                  exchanges[Index].SetDirection("buy");       //开多
                  OrderId =_C(exchanges[Index].Buy,MarketState ==true?-1:ExchangeInfo[Index].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]],orders[n].Amount -orders[n].DealAmount);       //原始数量下单
                  Type =1;
                  break;
              }
              else if(orders[n].Type ==2)
              {
                   exchanges[Index].SetDirection("sell");       //开空
                   OrderId =_C(exchanges[Index].Sell,MarketState ==true?-1:ExchangeInfo[Index].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]],orders[n].Amount -orders[n].DealAmount);       //原始数量下单    
                   Type =2;
                   break;
              }            
              else if(orders[n].Type ==3)
              {
                  exchanges[Index].SetDirection("closebuy");
                  OrderId =_C(exchanges[Index].Sell,MarketState ==true?-1:ExchangeInfo[Index].depth.Bids[BuyDepthIndex].Price -Slide[curVarietiesIndex[0]], orders[n].Amount -orders[n].DealAmount);  //卖出平多仓    
                  Type =3;
                  break;
              }
              else if(orders[n].Type ==4)
              {
                  exchanges[Index].SetDirection("closesell"); 
                  OrderId =_C(exchanges[Index].Buy,MarketState ==true?-1:ExchangeInfo[Index].depth.Asks[SellDepthIndex].Price +Slide[curVarietiesIndex[0]],orders[n].Amount -orders[n].DealAmount);       
                  Type =4;
                  break;
              }
            }
        }
    }
    
    if(Type !=0) //若数值不为空则将新订单信息push进去..
    {
       OrderInfoArry[curVarietiesIndex[1]].push({OrderId:OrderId,Type:Type});    //撤单后新的挂单依旧push进去保存..      
    }

    return false;   
}

//保存部分核心变量的值至本地
function SaveVarValue()
{
    _G("curArbitrageInfo"+curVarietiesIndex[1].toString()+"LastArbitrageType", curArbitrageInfo[curVarietiesIndex[1]].LastArbitrageType);
    _G("curArbitrageInfo"+curVarietiesIndex[1].toString()+"SpreadIndex", curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex);
    _G("curArbitrageInfo"+curVarietiesIndex[1].toString()+"UsedAmount", curArbitrageInfo[curVarietiesIndex[1]].UsedAmount);    
 
}
//读取本地内容
function ReadVarValue(n)
{
      curArbitrageInfo[n].LastArbitrageType  =_G("curArbitrageInfo"+n.toString()+"LastArbitrageType");
      curArbitrageInfo[n].SpreadIndex =_G("curArbitrageInfo"+n.toString()+"SpreadIndex");
      curArbitrageInfo[n].UsedAmount  =_G("curArbitrageInfo"+n.toString()+"UsedAmount");       
    if(curArbitrageInfo[n].LastArbitrageType ==null || curArbitrageInfo[n].SpreadIndex ==null || curArbitrageInfo[n].UsedAmount ==null)
    {//若配置内容无数据,则直接将其归0
       curArbitrageInfo[n].LastArbitrageType =0;
       curArbitrageInfo[n].SpreadIndex =0;
       curArbitrageInfo[n].UsedAmount  =0;
    }
    Log("读取本地配置值:",curArbitrageInfo[n].LastArbitrageType,curArbitrageInfo[n].SpreadIndex,curArbitrageInfo[n].UsedAmount);
}

function onTick(exchanges) {
    var records      =_C(exchanges[0].GetRecords,PERIOD_M1);   //默认1小时
    var ExchangeInfo = getExchangesBaseInfo();                 //获取数据
    
    if(ExchangeInfo.length <2 || records ==null || ExchangeInfo[0].account.Stocks ==0 || ExchangeInfo[1].account.Stocks ==0)  { 
       return;//若两个交易所有一个交易所无保证金或数据只有一个货币对,则直接pass
    }    
    
   var DifferentInfo =GetPriceDifferent(ExchangeInfo);
    
   DifferentHistory[curVarietiesIndex[0]] =DifferentInfo.DIFFERENT;  //记录不同品种价差记录,用于日志刷新...   
    
   if(ExchangeInfo[0].ShortPosition[1] !=ExchangeInfo[1].LongPosition[1]||
      ExchangeInfo[1].ShortPosition[1] !=ExchangeInfo[0].LongPosition[1])
   {    //若持仓数量不相等,则不进行任何开平仓操作,防止火币合约抽风,导致都是单边持仓..,这样最多只有一个价位单是单边,风险可控..
        DifferentInfo.TYPE =-1; //没有任何类型,直接屏蔽...
   }
    

    var Bar = records[records.length - 1];    
    if (LastBarTime[curVarietiesIndex[0]] !== Bar.Time) { 
        
      if(IsShowLog ==true)
      {//是否显示日志
          Log("价差信息:",DifferentInfo,"  持仓信息",ExchangeInfo[0].ShortPosition[1],ExchangeInfo[0].LongPosition[1],
          ExchangeInfo[1].ShortPosition[1],ExchangeInfo[1].LongPosition[1],"当前价差下标:",curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex,"当前已用数量:",curArbitrageInfo[curVarietiesIndex[1]].UsedAmount,
         "平台0价格:",ExchangeInfo[0].ticker.Last,"平台1价格:",ExchangeInfo[1].ticker.Last);
      }
        
        if(ExchangeInfo[0].ShortPosition[1] !=ExchangeInfo[1].LongPosition[1]||
           ExchangeInfo[1].ShortPosition[1] !=ExchangeInfo[0].LongPosition[1])
        { //若持仓数量不相等,则不进行任何开平仓操作,防止火币合约抽风,导致都是单边持仓..
           DifferentInfo.TYPE =-1; //没有任何类型,直接屏蔽...
        }
        
    //图表..    
    var diff = ExchangeInfo[0].ticker.Last - ExchangeInfo[1].ticker.Last;
    var strLogStatus ="";
    for(var LogN =0;LogN <VarietiesCount;LogN++)
    {
        var TempLogStatus ="符号 : " +VarietiesIndx[LogN]+"差价:"+ DifferentHistory[LogN].toString()+"\r\n";
        strLogStatus+=TempLogStatus;
    }
     strLogStatus+="\r\n说起爆仓我就想起了对冲,无风险对冲,有需要的老板可以联系,文体两开花,懂得自然懂!!\r\n 租用1888元/一月,3888元/季 微信:fzq250 ";

     LogStatus(strLogStatus);
        
    if (__lastDiff != 0) {
        if (Math.abs(Math.abs(diff) - Math.abs(__lastDiff)) > 200) {
            return;
        }
    }
      
        if(curVarietiesIndex[0] ==0)
        {
          cfg.yAxis.plotLines[0].value=diff;
     //     cfg.subtitle={text:'当前价差:' + diff};
          __chart.update([cfg,cfg]);
          __chart.add([0, [new Date().getTime(), diff]]);           
        }
        else if(curVarietiesIndex[0] ==1)
        {
          cfg.yAxis.plotLines[1].value=diff;
  //        cfg.subtitle={text:'当前价差:' + diff};
        __chart.update([cfg,cfg]);
        __chart.add([1, [new Date().getTime(), diff]]);
        }
        
      SaveVarValue();  //定时保存值至本地

      LastBarTime[curVarietiesIndex[0]] = Bar.Time; 
    }
    
    
    if((curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex <=0 &&curArbitrageInfo[curVarietiesIndex[1]].UsedAmount <=0)||
      (ExchangeInfo[0].ShortPosition[1]==0&&ExchangeInfo[0].LongPosition[1]==0&&
       ExchangeInfo[1].ShortPosition[1]==0&&ExchangeInfo[1].LongPosition[1]==0))   
    {//若当前价差下标与当前已用数量==0则重新初始化..
        var PlatformAOrder =exchanges[curPlatformIndex[0]].GetOrders();
        var PlatformBOrder =exchanges[curPlatformIndex[1]].GetOrders();
        if(PlatformAOrder !=null&&PlatformBOrder !=null &&PlatformAOrder.length <=0 &&PlatformBOrder.length <=0 )
        {//若两个平台都没有未成交订单并且又没有持仓订单,则订单数组可清空...,否则一旦清空有未成交订单则很麻烦..
          curArbitrageInfo[curVarietiesIndex[1]].LastArbitrageType =0;
          curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex =0;
          curArbitrageInfo[curVarietiesIndex[1]].UsedAmount  =0;
          OrderInfoArry[curVarietiesIndex[1]] =[];  //清空订单数组
        }
    }
    
    
    //正向计算获取价差   如果合约价格 >现货价格,则按 正的方式进行计算价差,否则按反的方式计算..
    
    var curOpenSpread =ArbitrageSpreadList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex];
    var curUsedAmount =curArbitrageInfo[curVarietiesIndex[1]].UsedAmount;
    var curMaxArbitrageAmount =ArbitrageAmountList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex] ;
    
    var LastArbitrageType =curArbitrageInfo[curVarietiesIndex[1]].LastArbitrageType;
    
    //当前平仓价差值    
     var  curCloseSpread  =CloseNarrowSpreadList[curVarietiesIndex[0]][curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex]; 

     if(curArbitrageInfo[curVarietiesIndex[1]].UsedAmount ==0)
     {//若当前档位没有多余数量则直接取上一次开仓的数量进行平仓计算
         var MinSpreadIndex  =curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex -1 <=0?0:curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex -1; //最小价差下标
         curCloseSpread  =CloseNarrowSpreadList[curVarietiesIndex[0]][MinSpreadIndex];
     } 
    
    
    if(curArbitrageInfo[curVarietiesIndex[1]].SpreadIndex <ArbitrageSpreadList[curVarietiesIndex[0]].length)
    {
       if(DifferentInfo.TYPE ==1 &&DifferentInfo.DIFFERENT >curOpenSpread &&curMaxArbitrageAmount >curUsedAmount)
       { //交易所0开空 ,交易所1开多
        DifferentInfo.TYPE =1;        
         OpenArbitrage(DifferentInfo,ExchangeInfo);
       }
       else if(DifferentInfo.TYPE ==2 &&DifferentInfo.DIFFERENT >curOpenSpread &&curMaxArbitrageAmount >curUsedAmount)
       {//交易所0开多,交易所1开空  
         DifferentInfo.TYPE =2;        
         OpenArbitrage(DifferentInfo,ExchangeInfo);      
       }
    }
    //平仓检测_剥离出去..
    if(LastArbitrageType ==1 &&DifferentInfo.TYPE ==1 &&DifferentInfo.DIFFERENT <=curCloseSpread)
    {//平仓 平0空 平1多
        DifferentInfo.TYPE =3;
        CloseArbitrage(DifferentInfo,ExchangeInfo);
    }
    else if(LastArbitrageType ==2 &&DifferentInfo.TYPE ==2 &&DifferentInfo.DIFFERENT <=curCloseSpread)
    {//平仓 平0多 平1空
        DifferentInfo.TYPE =4;
        CloseArbitrage(DifferentInfo,ExchangeInfo);       
    }
    
    
    //这里为交易所价差反转情况下平仓逻辑(A >B 现在为B<A 那么之前的开仓应平掉.)
    //这里不判断下标,只判断价差是否反转,反转则平..
    if(DifferentInfo.TYPE ==1 && DifferentInfo.DIFFERENT >0 &&
       ExchangeInfo[0].LongPosition[1]!=0 &&ExchangeInfo[1].ShortPosition[1]!=0)
    {//若类型==1并有相反方向开仓,则平掉 交易所0开多,交易所1开空  
        DifferentInfo.TYPE =4;
        CloseArbitrage(DifferentInfo,ExchangeInfo);    
        Log("价差发生反转,平仓...",DifferentInfo.TYPE);        
    }
    else if(DifferentInfo.TYPE ==2 && DifferentInfo.DIFFERENT >0 &&
            ExchangeInfo[0].ShortPosition[1]!=0 &&ExchangeInfo[1].LongPosition[1]!=0)
    {//若类型==2 并有相反方向开仓,则平掉 交易所0开空 ,交易所1开多
        DifferentInfo.TYPE =3;
        CloseArbitrage(DifferentInfo,ExchangeInfo);
        Log("价差发生反转,平仓...",DifferentInfo.TYPE);
    }
    
    
    if(CanCelOrderCheckTimer[curVarietiesIndex[1]]++ >3)
    { //每隔5秒检测一次撤单
       FuturePriceCancelCheck(curPlatformIndex[0],ExchangeInfo,PriceCancelRatio[curVarietiesIndex[0]]/100*ExchangeInfo[0].ticker.Last);
       FuturePriceCancelCheck(curPlatformIndex[1],ExchangeInfo,PriceCancelRatio[curVarietiesIndex[0]]/100*ExchangeInfo[1].ticker.Last);
       CanCelOrderCheckTimer[curVarietiesIndex[1]] =0;   //归0,重新计数
    }
    
    
     set_command();   //设置交互信息
}

function main() {
    Log("交易所0:",exchanges[0].GetName(),"符号:",exchanges[0].GetCurrency(),exchanges[0].GetAccount());
    Log("交易所1:",exchanges[1].GetName(),"符号:",exchanges[1].GetCurrency(),exchanges[1].GetAccount());    
    
    __chart = Chart(cfg);
    
    var strContractTypeIdxArray  =ContractTypeIdxString.split("|");    
    var strMarginLevelIdxArray   =MarginLevelIdxString.split("|");
    var strVarietiesArray        =VarietiesString.split("|");
    var strPriceCancelRatioArray =PriceCancelRatioString.split("|");    
    var strSlideArray            =SlideString.split("|");        
//    var strCloseNarrowSpreadArray =CloseNarrowSpreadString.split("|");
    
    var strArbitrageSpreadArrayA      =ArbitrageSpreadString.split("#");       
    var strArbitrageAmountA           =ArbitrageAmountString.split("#");   //#号拆分符号分割
 
    for(var n =0;n<strArbitrageSpreadArrayA.length;n++)   {  //拆分套利价差
        var strArbitrageSpreadArrayB      =strArbitrageSpreadArrayA[n].split("|");       
        for(var l=0;l <strArbitrageSpreadArrayB.length;l++)    {
           ArbitrageSpreadList[n][l] =parseFloat(strArbitrageSpreadArrayB[l]);        
        }
    }
    
      for(var n =0;n<strArbitrageAmountA.length;n++)   {   //拆分交易数量
         var strArbitrageAmountB      =strArbitrageAmountA[n].split("|");     //|号拆分具体数据         
        for(var l=0;l <strArbitrageAmountB.length;l++)    {
           ArbitrageAmountList[n][l] =parseFloat(strArbitrageAmountB[l]);        
        }
    }  

   var strCloseNarrowSpreadArray       =CloseNarrowSpreadString.split("#");    
    
   for(var n =0;n<strCloseNarrowSpreadArray.length;n++)   {   //拆分平仓价差
         var strCloseNarrowSpreadB   =strCloseNarrowSpreadArray[n].split("|");     //|号拆分具体数据         
        for(var l=0;l <strCloseNarrowSpreadB.length;l++)    {
           CloseNarrowSpreadList[n][l] =parseFloat(strCloseNarrowSpreadB[l]);
            Log("平仓价差:", CloseNarrowSpreadList[n][l] );
        }
    }  
    
    
    for(var n =0;n<2;n++)  //最大数量为交易品种的数量
    {
       ContractTypeIdx[n]       =strContractTypeIdxArray[n];
       MarginLevelIdx[n]        =parseInt(strMarginLevelIdxArray[n]);
       VarietiesIndx[n]         =strVarietiesArray[n];
       PriceCancelRatio[n]      =parseFloat(strPriceCancelRatioArray[n]);         
       Slide[n]                 =parseFloat(strSlideArray[n]);
 
    }
    
    for(var n =0;n<exchanges.length;n++)
    {
       exchanges[n].SetPrecision(3, 1);
       exchanges[n].SetRate(1);
       exchanges[n].SetContractType(ContractTypeIdx[n]);
       exchanges[n].SetMarginLevel(MarginLevelIdx[n]);   
        Log("交易所:",n,"当前合约类型:",ContractTypeIdx[n],"杠杆:",MarginLevelIdx[n]);
    }

    
    for(var a =0;a <exchanges.length;a++)   {
        for(var b =a+1;b <exchanges.length;b++)   {
         for(var n =0; n <VarietiesCount;n++)  {    
           ReadVarValue(a+b+n);  //读取本地配置信息                
        }
     }
   }
    
    if(IsDeleteGloablConfig ==true)
    { //删除_G保留的配置信息
       _G(null); // 删除所有全局变量
    }
       
    while(true)
    {
        for(var a =0;a <exchanges.length;a++)   {
            for(var b =a+1;b <exchanges.length;b++)   {
              curPlatformIndex[0] =a; //交易所0
              curPlatformIndex[1] =b; //交易所1                 
                
              for(var n =0; n <VarietiesCount;n++)  {
                curVarietiesIndex[0]  =n;   //当前执行下标,用于更换品种
                curVarietiesIndex[1]  =a+b+n;   //当前交易所所与交易对标记0\1\2\3\4\5以此类推..
                if(IsVirtual() ==false)
                {//实盘则进行符号切换
                  exchanges[curPlatformIndex[0]].IO("currency", VarietiesIndx[n]+"_USD");  //合约符号更改
                  exchanges[curPlatformIndex[1]].IO("currency", VarietiesIndx[n]+"_USD");  //现货符号更改            
                }     

                onTick(exchanges);
                Sleep(1000); //延时1秒
             }              
           }            
        }   
      Sleep(LoopInterval * 1000);     
    }   
    
}



//获取动态参数(策略交互内容)
 function set_command() {

     var get_command = GetCommand();//  GetCommand方法是获取参数方法,获取的参数是字符串形式 格式为 "参数名:参数值" 参见BotVS API文档
     if (get_command != null) {
         if (get_command.indexOf("ModifyArbitrageSpread:") == 0) {  //如果传入的参数名为A3(以“A3:”打头,即表明是A3参数)

            var  AAA = (get_command.replace("ModifyArbitrageSpread:", "")); //赋值给策略里面的AAA(将打头字符串替换为空,剩下就是我们的参数值)
            var strArbitrageSpreadArrayA  =AAA.split("#");      
             for(var n =0;n<strArbitrageSpreadArrayA.length;n++)   {  //拆分套利价差
               var strArbitrageSpreadArrayB      =strArbitrageSpreadArrayA[n].split("|");       
                 for(var l=0;l <strArbitrageSpreadArrayB.length;l++)    {
                  ArbitrageSpreadList[n][l] =parseFloat(strArbitrageSpreadArrayB[l]);     
                  Log("新的值:",ArbitrageSpreadList[n][l]);
               }
             }
         }
         
          if (get_command.indexOf("ModifyArbitrageAmount:") == 0) {  //如果传入的参数名为B3(以“B3:”打头,即表明是B3参数)

            var BBB = (get_command.replace("ModifyArbitrageAmount:", "")); //赋值给策略里面的BBB(将打头字符串替换为空,剩下就是我们的参数值)
             var strArbitrageAmountA  =BBB.split("#");   //#号拆分符号分割
             for(var n =0;n<strArbitrageAmountA.length;n++)   {   //拆分交易数量
                 var strArbitrageAmountB      =strArbitrageAmountA[n].split("|");     //|号拆分具体数据         
                 for(var l=0;l <strArbitrageAmountB.length;l++)    {
                    ArbitrageAmountList[n][l] =parseFloat(strArbitrageAmountB[l]);     
                     Log("新的值:",ArbitrageAmountList[n][l]);
                 }
              }                
         }

          if (get_command.indexOf("ModifyCloseNarrowSpread:") == 0) {  //如果传入的参数名为B3(以“B3:”打头,即表明是B3参数)

            var CCC = (get_command.replace("ModifyCloseNarrowSpread:", "")); //赋值给策略里面的BBB(将打头字符串替换为空,剩下就是我们的参数值)
            var strCloseNarrowSpreadArray  =CCC.split("#");    
    
            for(var n =0;n<strCloseNarrowSpreadArray.length;n++)   {   //拆分平仓价差
               var strCloseNarrowSpreadB   =strCloseNarrowSpreadArray[n].split("|");     //|号拆分具体数据         
               for(var l=0;l <strCloseNarrowSpreadB.length;l++)    {
                 CloseNarrowSpreadList[n][l] =parseFloat(strCloseNarrowSpreadB[l]);
                Log("新的值:", CloseNarrowSpreadList[n][l] );
               }
            }  
         }         
         
     }
 }



More