BitMEX掛單策略詳解
BitMEX已經成為了虛擬貨幣槓桿交易的首選平臺,但其API交易限制嚴格,讓人十分困擾。本文主要分享在FMZ量化交易平臺實盤中API使用的一些技巧,主要針對做市策略。
-
1.BitMEX的特點
最顯著的優勢是交易活躍,特別是比特幣永續合約,每分鐘交易額常常超過百萬甚至千萬美元;BitMEX掛單交易後有返傭,雖然不高,但任吸引了大批做市交易者,所以買一賣一深度極佳,常常在百萬美元以上;由於買一賣一積累的深度,所以交易價格常常在最小變動單位0.5美元上下波動。
-
2.BitMEX API頻率限制
REST API 的請求頻率限於每5分鐘300次。 相當於1秒一次,這個限制對比其它交易平臺可以說非常嚴格。超出限制後,會提示'Rate limit exceeded',如果繼續超出限制,IP可能被禁用一小時,在短時間內的多個禁用將導致禁用一週。對每個 API 的請求,BitMEX將返回標頭資料,標頭資料來檢視當前的剩餘的請求次數,事實上,如果API使用得當,是不會超出頻率限制,一般不用檢查。
-
3.使用websocket獲取行情
BitMEX REST API 限制比較嚴格,官方推薦多用websocket協議,並且推送的資料型別比一般的交易所要多。具體使用要注意以下幾點:
- 深度資料推送時間長了會出現誤差,和真實深度對應不上,估計是深度變化太多,推送有遺漏,但一般情況下由於流動性極佳,訂閱ticker或trades即可
- 訂單詳情推送遺漏很多,幾乎不可用。
- 賬戶資訊推送會有明顯延時,最好使用REST 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) } }
-
4.下單技巧
BitMEX官方推薦使用批量下單和修改訂單來實現,由於 BitMEX 實時的審計、風險檢查、保證金計算、以及委託操作,批量操作可以更快地被執行。因此,批量操作的頻率被計算為正常頻率的十分之一。因此我們的下單操作要全部使用批量下單和修改訂單的方式,儘量減少API的使用,而查詢訂單狀態也需要消耗API次數,可以根據倉位變化或修改訂單失敗輔助判斷訂單狀態。
批量下單並沒有限制訂單數量(不能太多),實際上一個訂單也能使用批量下單介面。由於修改訂單的操作,我們可以在價格偏離很大的地方預下一些訂單,它們不會成交,但我們需要下單時,只需要修改已下訂單的價格和數量即可。修改訂單會出現失敗,這也可以作為訂單成交的訊號。
以下為具體的實現程式碼:
//撤銷所有訂單,並重置全域性變數 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) } }
-
5.其它
BitMEX的伺服器在愛爾蘭都柏林亞馬遜機房,購買此處伺服器執行策略ping低於1ms,但推送任有延時,也無法解決overload問題。另外登陸賬戶時代理不可位於美國等地,時間長了會直接封帳號。
本文程式碼從我個人策略修改而來,不保證完全正確,供參考。具體使用要把行情程式碼放在main函式裡執行,交易相關的程式碼放在main函式之前,trade()函式放在推送行情裡就可以了。
轉載自 :https://quant.la/Article/View/2029/BitMEX 掛單策略詳解.html
作者 : 小草