程序员的量化世界投资理财程序员

BitMEX挂单策略详解

2019-01-26  本文已影响5人  发明者量化FMZ

BitMEX已经成为了虚拟货币杠杆交易的首选平台,但其API交易限制严格,让人十分困扰。本文主要分享在FMZ量化交易平台实盘中API使用的一些技巧,主要针对做市策略。

以下代码为使用websocket协议,实时获取行情和账户信息,主要针对做市策略编写。具体使用需要在main()函数里执行。

var ticker  = {price:0, buy:0, sell:0, time:0} //ticker信息,分别为最新价、买一价、卖一价、更新时间
//账户信息,分别有仓位,买卖价格,买卖数量,买卖状态,订单Id
var info = {position:0, buyPrice:0, sellPrice:0, buyAmount:0, sellAmount:0, buyState:0, sellState:0, buyId:0, sellId:0}
var buyListId = []//全局变量,预埋买单id列表,下文有介绍
var sellListId = []
var APIKEY = 'your api id' //这里需要填入BitMEX API ID 注意不是密钥,websocket协议验证时需要
var expires = parseInt(Date.now() / 1000) + 10
var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey}}")//secretkey会在底层自动替换,不需要填入。
var bitmexClient = Dial("wss://www.bitmex.com/realtime", 60)
var auth = JSON.stringify({args: [APIKEY, expires, signature], op: "authKeyExpires"})//认证信息,不然无法订阅账户
bitmexClient.write(auth)
bitmexClient.write('{"op": "subscribe", "args": ["position","execution","trade:XBTUSD"]}')//订阅了仓位、订单执行和永续合约实时成交
while(true){
    if(bitmexClient.read()){
        bitmexData = JSON.parse(data)
        if('table' in bitmexData && bitmexData.table == 'trade'){
            data = bitmexData.data
            ticker.price = parseFloat(data[data.length-1].price)//最新成交价,一次会推送多条成交,取一个即可
            //可根据最新成交的trade方向得出买一卖一,不用订阅深度。
            if(data[data.length-1].side == 'Buy'){
                ticker.sell = parseFloat(data[data.length-1].price)
                ticker.buy = parseFloat(data[data.length-1].price)-0.5
            }else{
                ticker.buy = parseFloat(data[data.length-1].price)
                ticker.sell = parseFloat(data[data.length-1].price)+0.5
            }
            ticker.time =  new Date(data[data.length-1].timestamp);//更新时间,可用于判断延时
        }
    }else if(bitmexData.table == 'position'){
        var position = parseInt(bitmexData.data[0].currentQty)  
        if(position != info.position){
            Log('仓位变化: ', position, info.position, '#FF0000@')//仓位变化Log,并推送到微信,去掉@不推送
            info.position = position  
        }
        info.position  = parseInt(bitmexData.data[0].currentQty)  
    }
}
  //撤销所有订单,并重置全局变量
function cancelAll(){
    exchange.IO("api","DELETE","/api/v1/order/all","symbol=XBTUSD")//调用IO扩展撤销
    info = {position:0, buyPrice:0, sellPrice:0, buyAmount:0, sellAmount:0, buyState:0, sellState:0, buyId:0, sellId:0}
    buyListId = []
    sellListId = []
}
//下备选订单
function waitOrders(){
    var orders = []
    if(buyListId.length<4){
        //检查数量不足时再下一批
        for(var i=0;i<7;i++){
            //由于BitMEX限制,价格不能偏离过多,订单量不能太小,execInst参数保证只能做市成交
            orders.push({symbol:'XBTUSD', side:'Buy', orderQty:100, price:ticker.buy-400+i, execInst:'ParticipateDoNotInitiate'})
        }
    }
    if(sellListId.length<4){
        for(var i=0;i<7;i++){
            orders.push({symbol:'XBTUSD', side:'Sell', orderQty:100, price:ticker.buy+400+i, execInst:'ParticipateDoNotInitiate'})
        }
    }
    if(orders.length>0){
        var param = "orders=" + JSON.stringify(orders);
        var ids = exchange.IO("api", "POST", "/api/v1/order/bulk", param);//批量订单在这里提交
        for(var i=0;i<ids.length;i++){
            if(ids.side == 'Buy'){
                buyListId.push(ids.orderID)
            }else{
                sellListId.push(ids.orderID)
            }
        }
    }
}
//修改订单函数
function amendOrders(order, direction, price, amount, id){
    var param = "orders=" + JSON.stringify(order);
    var ret = exchange.IO("api", "PUT", "/api/v1/order/bulk", param);//每次修改一个订单
    //修改出现错误
    if(!ret){
        var err = GetLastError()
        //overloaded未修改策成功,需要把订单id回收
        if(err.includes('The system is currently overloaded')){
            if(id){
                if(direction == 'buy'){
                    buyListId.push(id)
                }else{
                    sellListId.push(id)
                }
            }
            Sleep(1000)
            return
        }
        //非法订单状态,说明待修改的订单已完全成交
        else if(err.includes('Invalid ordStatus')){
            Log(order, direction)
            if(direction == 'buy'){
                info.buyId = 0
                info.buyState = 0
                info.buyAmount = 0
                info.buyPrice = 0
            }else{
                info.sellId = 0
                info.sellState = 0
                info.sellAmount = 0
                info.sellPrice = 0
            }
            //由于推送不及时,在这里用rest协议更新一下仓位
            pos = _C(exchange.GetPosition)
            if(pos.length>0){
                info.position = pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount
            }else{
                info.position = 0
            }
        }
        //出现未知错误无法修改,撤销所有订单,重置一次
        else if(err.includes('Invalid orderID')){
            cancelAll()
            Log('Invalid orderID,重置一次')
        }
        //超过频率限制,休眠一下可继续尝试
        else if(err.includes('Rate limit exceeded')){
            Sleep(2000)
            return
        }
        //账户被禁,撤销所有订单,休眠较长时间等待恢复
        else if(err.includes('403 Forbidden')){
            cancelAll()
            Log('403,重置一次')
            Sleep(5*60*1000)
        }
    }else{
        //修改订单成功
        if(direction == 'buy'){
            info.buyState = 1
            info.buyPrice = price
            info.buyAmount = amount
        }else{
            info.sellState = 1
            info.sellPrice = price
            info.sellAmount = amount
        }
    }
}
//0.5价格变化
function fixSize(num){
    if(num>=_N(num,0)+0.75){
        num = _N(num,0)+1
    }else if(num>=_N(num,0)+0.5){
        num=_N(num,0)+0.5
    }else{
        num=_N(num,0)
    }
    return num
}
//交易函数
function trade(){
    waitOrders()//检查是否需要下备选单
    var buyPrice = fixSize(ticker.buy-5) //仅作演示用,具体的交易要自己写
    var sellPrice = fixSize(ticker.sell+5)
    var buyAmount =  500
    var sellAmount = 500
    //没有订单时,从备选订单修改
    if(info.buyState == 0  && buyListId.length > 0){
        info.buyId = buyListId.shift()
        amendOrders([{orderID:info.buyId, price:buyPrice, orderQty:buyAmount}],'buy', group, buyPrice, buyAmount, info.buyId)
    }
    if(info.sellState == 0 && sellListId.length > 0){
        info.sellId = sellListId.shift()
        amendOrders([{orderID: info.sellId, price:sellPrice, orderQty:sellAmount}],'sell', group, sellPrice, sellAmount, info.sellId )
    }
    //已有订单需要更改价格
    if(buyPrice !=  info.buyPrice && info.buyState == 1){
        amendOrders([{orderID:info.buyId, price:buyPrice, orderQty:buyAmount}],'buy', group, buyPrice, buyAmount)
    }
    if(sellPrice != info.sellPrice && info.sellState == 1){
        amendOrders([{orderID:info.sellId, price:sellPrice, orderQty:sellAmount}],'sell', group, sellPrice, sellAmount)
    }
}

转载自 : https://quant.la/Article/View/2029/BitMEX挂单策略详解.html
作者 : 小草

上一篇下一篇

猜你喜欢

热点阅读