关灯

[原创] BitMEX挂单策略详解

[复制链接]
admin 发表于 2019-1-20 12:33:59 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
 

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()函数里执行。

  1. var ticker = {price:0, buy:0, sell:0, time:0} //ticker信息,分别为最新价、买一价、卖一价、更新时间
  2. //账户信息,分别有仓位,买卖价格,买卖数量,买卖状态,订单Id
  3. var info = {position:0, buyPrice:0, sellPrice:0, buyAmount:0, sellAmount:0, buyState:0, sellState:0, buyId:0, sellId:0}
  4. var buyListId = []//全局变量,预埋买单id列表,下文有介绍
  5. var sellListId = []
  6. var APIKEY = 'your api id' //这里需要填入BitMEX API ID 注意不是密钥,websocket协议验证时需要
  7. var expires = parseInt(Date.now() / 1000) + 10
  8. var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey}}")//secretkey会在底层自动替换,不需要填入。
  9. var bitmexClient = Dial("wss://www.bitmex.com/realtime", 60)
  10. var auth = JSON.stringify({args: [APIKEY, expires, signature], op: "authKeyExpires"})//认证信息,不然无法订阅账户
  11. bitmexClient.write(auth)
  12. bitmexClient.write('{"op": "subscribe", "args": ["position","execution","trade:XBTUSD"]}')//订阅了仓位、订单执行和永续合约实时成交
  13. while(true){
  14. if(bitmexClient.read()){
  15. bitmexData = JSON.parse(data)
  16. if('table' in bitmexData && bitmexData.table == 'trade'){
  17. data = bitmexData.data
  18. ticker.price = parseFloat(data[data.length-1].price)//最新成交价,一次会推送多条成交,取一个即可
  19. //可根据最新成交的trade方向得出买一卖一,不用订阅深度。
  20. if(data[data.length-1].side == 'Buy'){
  21. ticker.sell = parseFloat(data[data.length-1].price)
  22. ticker.buy = parseFloat(data[data.length-1].price)-0.5
  23. }else{
  24. ticker.buy = parseFloat(data[data.length-1].price)
  25. ticker.sell = parseFloat(data[data.length-1].price)+0.5
  26. }
  27. ticker.time = new Date(data[data.length-1].timestamp);//更新时间,可用于判断延时
  28. }
  29. }else if(bitmexData.table == 'position'){
  30. var position = parseInt(bitmexData.data[0].currentQty)
  31. if(position != info.position){
  32. Log('仓位变化: ', position, info.position, '#FF0000@')//仓位变化Log,并推送到微信,去掉@不推送
  33. info.position = position
  34. }
  35. info.position = parseInt(bitmexData.data[0].currentQty)
  36. }
  37. }
复制代码

 

4.下单技巧

BitMEX官方推荐使用批量下单和修改订单来实现,由于 BitMEX 实时的审计、风险检查、保证金计算、以及委托操作,批量操作可以更快地被执行。因此,批量操作的频率被计算为正常频率的十分之一。因此我们的下单操作要全部使用批量下单和修改订单的方式,尽量减少API的使用,而查询订单状态也需要消耗API次数,可以根据仓位变化或修改订单失败辅助判断订单状态。

批量下单并没有限制订单数量(不能太多),实际上一个订单也能使用批量下单接口。由于修改订单的操作,我们可以在价格偏离很大的地方预下一些订单,它们不会成交,但我们需要下单时,只需要修改已下订单的价格和数量即可。修改订单会出现失败,这也可以作为订单成交的信号。

以下为具体的实现代码:

  1. //撤销所有订单,并重置全局变量
  2. function cancelAll(){
  3. exchange.IO("api","DELETE","/api/v1/order/all","symbol=XBTUSD")//调用IO扩展撤销
  4. info = {position:0, buyPrice:0, sellPrice:0, buyAmount:0, sellAmount:0, buyState:0, sellState:0, buyId:0, sellId:0}
  5. buyListId = []
  6. sellListId = []
  7. }
  8. //下备选订单
  9. function waitOrders(){
  10. var orders = []
  11. if(buyListId.length<4){
  12. //检查数量不足时再下一批
  13. for(var i=0;i<7;i++){
  14. //由于BitMEX限制,价格不能偏离过多,订单量不能太小,execInst参数保证只能做市成交
  15. orders.push({symbol:'XBTUSD', side:'Buy', orderQty:100, price:ticker.buy-400+i, execInst:'ParticipateDoNotInitiate'})
  16. }
  17. }
  18. if(sellListId.length<4){
  19. for(var i=0;i<7;i++){
  20. orders.push({symbol:'XBTUSD', side:'Sell', orderQty:100, price:ticker.buy+400+i, execInst:'ParticipateDoNotInitiate'})
  21. }
  22. }
  23. if(orders.length>0){
  24. var param = "orders=" + JSON.stringify(orders);
  25. var ids = exchange.IO("api", "POST", "/api/v1/order/bulk", param);//批量订单在这里提交
  26. for(var i=0;i<ids.length;i++){
  27. if(ids.side == 'Buy'){
  28. buyListId.push(ids.orderID)
  29. }else{
  30. sellListId.push(ids.orderID)
  31. }
  32. }
  33. }
  34. }
  35. //修改订单函数
  36. function amendOrders(order, direction, price, amount, id){
  37. var param = "orders=" + JSON.stringify(order);
  38. var ret = exchange.IO("api", "PUT", "/api/v1/order/bulk", param);//每次修改一个订单
  39. //修改出现错误
  40. if(!ret){
  41. var err = GetLastError()
  42. //overloaded未修改策成功,需要把订单id回收
  43. if(err.includes('The system is currently overloaded')){
  44. if(id){
  45. if(direction == 'buy'){
  46. buyListId.push(id)
  47. }else{
  48. sellListId.push(id)
  49. }
  50. }
  51. Sleep(1000)
  52. return
  53. }
  54. //非法订单状态,说明待修改的订单已完全成交
  55. else if(err.includes('Invalid ordStatus')){
  56. Log(order, direction)
  57. if(direction == 'buy'){
  58. info.buyId = 0
  59. info.buyState = 0
  60. info.buyAmount = 0
  61. info.buyPrice = 0
  62. }else{
  63. info.sellId = 0
  64. info.sellState = 0
  65. info.sellAmount = 0
  66. info.sellPrice = 0
  67. }
  68. //由于推送不及时,在这里用rest协议更新一下仓位
  69. pos = _C(exchange.GetPosition)
  70. if(pos.length>0){
  71. info.position = pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount
  72. }else{
  73. info.position = 0
  74. }
  75. }
  76. //出现未知错误无法修改,撤销所有订单,重置一次
  77. else if(err.includes('Invalid orderID')){
  78. cancelAll()
  79. Log('Invalid orderID,重置一次')
  80. }
  81. //超过频率限制,休眠一下可继续尝试
  82. else if(err.includes('Rate limit exceeded')){
  83. Sleep(2000)
  84. return
  85. }
  86. //账户被禁,撤销所有订单,休眠较长时间等待恢复
  87. else if(err.includes('403 Forbidden')){
  88. cancelAll()
  89. Log('403,重置一次')
  90. Sleep(5*60*1000)
  91. }
  92. }else{
  93. //修改订单成功
  94. if(direction == 'buy'){
  95. info.buyState = 1
  96. info.buyPrice = price
  97. info.buyAmount = amount
  98. }else{
  99. info.sellState = 1
  100. info.sellPrice = price
  101. info.sellAmount = amount
  102. }
  103. }
  104. }
  105. //0.5价格变化
  106. function fixSize(num){
  107. if(num>=_N(num,0)+0.75){
  108. num = _N(num,0)+1
  109. }else if(num>=_N(num,0)+0.5){
  110. num=_N(num,0)+0.5
  111. }else{
  112. num=_N(num,0)
  113. }
  114. return num
  115. }
  116. //交易函数
  117. function trade(){
  118. waitOrders()//检查是否需要下备选单
  119. var buyPrice = fixSize(ticker.buy-5) //仅作演示用,具体的交易要自己写
  120. var sellPrice = fixSize(ticker.sell+5)
  121. var buyAmount = 500
  122. var sellAmount = 500
  123. //没有订单时,从备选订单修改
  124. if(info.buyState == 0 && buyListId.length > 0){
  125. info.buyId = buyListId.shift()
  126. amendOrders([{orderID:info.buyId, price:buyPrice, orderQty:buyAmount}],'buy', group, buyPrice, buyAmount, info.buyId)
  127. }
  128. if(info.sellState == 0 && sellListId.length > 0){
  129. info.sellId = sellListId.shift()
  130. amendOrders([{orderID: info.sellId, price:sellPrice, orderQty:sellAmount}],'sell', group, sellPrice, sellAmount, info.sellId )
  131. }
  132. //已有订单需要更改价格
  133. if(buyPrice != info.buyPrice && info.buyState == 1){
  134. amendOrders([{orderID:info.buyId, price:buyPrice, orderQty:buyAmount}],'buy', group, buyPrice, buyAmount)
  135. }
  136. if(sellPrice != info.sellPrice && info.sellState == 1){
  137. amendOrders([{orderID:info.sellId, price:sellPrice, orderQty:sellAmount}],'sell', group, sellPrice, sellAmount)
  138. }
  139. }
复制代码

5.其它

BitMEX的服务器在爱尔兰都柏林亚马逊机房,购买此处服务器运行策略ping低于1ms,但推送任有延时,也无法解决overload问题。另外登陆账户时代理不可位于美国等地,时间长了会直接封帐号。

本文代码从我个人策略修改而来,不保证完全正确,供参考。具体使用要把行情代码放在main函数里执行,交易相关的代码放在main函数之前,trade()函数放在推送行情里就可以了。

 

回复

使用道具 举报

 
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


1关注

0粉丝

1603帖子

排行榜

关注我们:微信订阅号

官方微信

APP下载

全国服务热线:

4000-018-018

公司地址:上海市嘉定区银翔路655号B区1068室

运营中心:成都市锦江区东华正街42号广电仕百达国际大厦25楼

邮编:610066 Email:3318850993#qq.com

Copyright   ©2015-2016  比特趋势Powered by©Discuz!技术支持:迪恩网络