vn.py量化社区
By Traders, For Traders.
Member
avatar
加入于:
帖子: 9
声望: 0

vnpy版本为1.9.2
用系统自带的海龟策略交易BITMEX的XBTUSD,经常出现拒单的委托状态,请教是什么原因?数据如下:

gatewayName,orderID,symbol,direction,price,totalVolume,tradedVolume,status,orderTime
BITMEX,190220140629000014,XBTUSD,多,4108.0,20,,拒单,
BITMEX,190220140629000015,XBTUSD,多,4123.0,20,,拒单,
BITMEX,190220140629000016,XBTUSD,多,4132,20,20,全部成交,20190223
BITMEX,190220140629000017,XBTUSD,多,4146,20,20,全部成交,20190223
BITMEX,190220140629000018,XBTUSD,多,4158.0,20,,拒单,
BITMEX,190220140629000019,XBTUSD,空,4096.5,40,40,全部成交,20190223
BITMEX,190220140629000020,XBTUSD,多,4159.0,20,,拒单,
BITMEX,190220140629000021,XBTUSD,多,4168.5,20,20,全部成交,20190224
BITMEX,190220140629000022,XBTUSD,多,4183.0,20,,拒单,
BITMEX,190220140629000023,XBTUSD,空,4116.0,20,,拒单,
BITMEX,190220140629000024,XBTUSD,空,4106.5,20,20,全部成交,20190224
BITMEX,190220140629000025,XBTUSD,空,3884.5,20,,拒单,
BITMEX,190220140629000026,XBTUSD,空,3874.5,20,,拒单,
BITMEX,190220140629000027,XBTUSD,空,3862.0,20,,拒单,
BITMEX,190220140629000028,XBTUSD,空,3848.0,20,,拒单,

Administrator
avatar
加入于:
帖子: 1408
声望: 63

简单来说,就是你发单的时候BitMEX服务器正好处于过载中(the system is overload),然后拒绝了你的请求。

可以在主界面看到错误输出,解决办法是多使用BitMEX自身的服务器条件单。

Member
avatar
加入于:
帖子: 9
声望: 0

请教群主:主界面指的是哪个主界面?vnpy主界面没有看到拒单的错误输出信息,bitmex的交易历史记录里面也没有看到拒单的信息。另外,BitMEX自身的服务器条件单相关资料哪里有?非常感谢!

Administrator
avatar
加入于:
帖子: 1408
声望: 63

主界面就是你一启动VN Trader的时候看到的界面,如果没有看到错误输出,那么就是因为报单参数问题造成的服务器拒单了,这种情况在bitmex网页是看不到该笔委托记录的。

BITMEX API:https://www.bitmex.com/app/apiOverview

Member
avatar
加入于:
帖子: 9
声望: 0

好的,多谢

Member
avatar
加入于:
帖子: 5
声望: 0

我在bitmex交易很久了,bitmex经常在行情波动大的时候抛出 503: {"error":{"message":"The system is currently overloaded. Please try again later.","name":"HTTPError"}}或者502,400等错误,其实很简单,重新下单就可以了。
@用Python的交易员
如果有空的话可以优化一下这部分,参考Bitmex官方GitHub里面MarketMaker这个示例里面的错误处理即可

Member
avatar
加入于:
帖子: 5
声望: 0

code text here
def _curl_bitmex(self, path, query=None, postdict=None, timeout=None, verb=None, rethrow_errors=False,
max_retries=300):
"""Send a request to BitMEX Servers."""

    # Handle URL
    url = self.base_url + path

    if timeout is None:
        timeout = self.timeout

    # Default to POST if data is attached, GET otherwise
    if not verb:
        verb = 'POST' if postdict else 'GET'

    # By default don't retry POST or PUT. Retrying GET/DELETE is okay because they are idempotent.
    # In the future we could allow retrying PUT, so long as 'leavesQty' is not used (not idempotent),
    # or you could change the clOrdID (set {"clOrdID": "new", "origClOrdID": "old"}) so that an amend
    # can't erroneously be applied twice.
    if max_retries is None:
        max_retries = 0 if verb in ['POST', 'PUT'] else 3

    # Auth: API Key/Secret
    auth = APIKeyAuthWithExpires(self.apiKey, self.apiSecret)

    def exit_or_throw(e):
        if rethrow_errors:
            raise e
        else:
            exit(1)

    def retry():
        self.retries += 1
        if self.retries > max_retries:
            raise Exception("Max retries on %s (%s) hit, raising." % (path, json.dumps(postdict or '')))
        return self._curl_bitmex(path, query, postdict, timeout, verb, rethrow_errors, max_retries)

    # Make the request
    response = None
    proxies = {'http': 'http://127.0.0.1:1080', 'https': 'https://127.0.0.1:1080'}
    try:
        self.logger.debug("sending req to %s: %s" % (url, json.dumps(postdict or query or '')))
        req = requests.Request(verb, url, json=postdict, auth=auth, params=query)
        prepped = self.session.prepare_request(req)
        #response = self.session.send(prepped, proxies=proxies, timeout=timeout)
        response = self.session.send(prepped, timeout=timeout)
        # Make non-200s throw
        response.raise_for_status()

    except requests.exceptions.HTTPError as e:
        if response is None:
            raise e

        # 401 - Auth error. This is fatal.
        if response.status_code == 401:
            self.logger.error("API Key or Secret incorrect, please check and restart.")
            self.logger.error("Error: " + response.text)
            if postdict:
                self.logger.error(postdict)
            # Always exit, even if rethrow_errors, because this is fatal
            exit(1)

        # 404, can be thrown if order canceled or does not exist.
        elif response.status_code == 404:
            if verb == 'DELETE':
                self.logger.error("Order not found: %s" % postdict['orderID'])
                return
            self.logger.error("Unable to contact the BitMEX API (404). " +
                              "Request: %s \n %s" % (url, json.dumps(postdict)))
            exit_or_throw(e)

        # 429, ratelimit; cancel orders & wait until X-RateLimit-Reset
        elif response.status_code == 429:
            self.logger.error("Ratelimited on current request. Sleeping, then trying again. Try fewer " +
                              "order pairs or contact support@bitmex.com to raise your limits. " +
                              "Request: %s \n %s" % (url, json.dumps(postdict)))

            # Figure out how long we need to wait.
            ratelimit_reset = response.headers['X-RateLimit-Reset']
            to_sleep = int(ratelimit_reset) - int(time.time())
            reset_str = datetime.datetime.fromtimestamp(int(ratelimit_reset)).strftime('%X')

            # We're ratelimited, and we may be waiting for a long time. Cancel orders.
            self.logger.warning("Canceling all known orders in the meantime.")
            self.cancel([o['orderID'] for o in self.open_orders()])

            self.logger.error("Your ratelimit will reset at %s. Sleeping for %d seconds." % (reset_str, to_sleep))
            time.sleep(to_sleep)

            # Retry the request.
            return retry()

        # 502 - Bad gateway, likely due to a deploy. Try again
        elif response.status_code == 502:
            self.logger.warning("Unable to contact the BitMEX API (502), retrying. " +
                                "Request: %s \n %s" % (url, json.dumps(postdict)))
            time.sleep(2)
            return retry()

        # 503 - BitMEX temporary downtime, likely due to a deploy. Try again
        elif response.status_code == 503:
            self.logger.warning("Unable to contact the BitMEX API (503), retrying. " +
                                "Request: %s \n %s" % (url, json.dumps(postdict)))
            time.sleep(2)
            return retry()

        elif response.status_code == 400:
            error = response.json()['error']
            message = error['message'].lower() if error else ''

            if error['name'] == 'HTTPError':
                time.sleep(1)
                return retry()

            # Duplicate clOrdID: that's fine, probably a deploy, go get the order(s) and return it
            if 'duplicate clordid' in message:
                orders = postdict['orders'] if 'orders' in postdict else postdict

                IDs = json.dumps({'clOrdID': [order['clOrdID'] for order in orders]})
                orderResults = self._curl_bitmex('/order', query={'filter': IDs}, verb='GET')

                for i, order in enumerate(orderResults):
                    if order.get('orderQty') is not None and postdict.get('orderQty') is not None:
                        if (
                                order.get('orderQty') != abs(postdict.get('orderQty')) or
                                order.get('side') != ('Buy' if postdict.get('orderQty') > 0 else 'Sell') or
                                order.get('price') != postdict.get('price') or
                                order.get('symbol') != postdict.get('symbol')):
                            raise Exception(
                                'Attempted to recover from duplicate clOrdID, but order returned from API ' +
                                'did not match POST.\nPOST data: %s\nReturned order: %s' % (
                                    json.dumps(orders[i]), json.dumps(order)))
                # All good
                return orderResults

            elif 'insufficient available balance' in message:
                self.logger.error('Account out of funds. The message: %s' % error['message'])
                exit_or_throw(Exception('Insufficient Funds'))

        # If we haven't returned or re-raised yet, we get here.
        self.logger.error("Unhandled Error: %s: %s" % (e, response.text))
        self.logger.error("Endpoint was: %s %s: %s" % (verb, path, json.dumps(postdict)))
        exit_or_throw(e)

    except requests.exceptions.Timeout as e:
        # Timeout, re-run this request
        self.logger.warning("Timed out on request: %s (%s), retrying..." % (path, json.dumps(postdict or '')))
        return retry()

    except requests.exceptions.ConnectionError as e:
        self.logger.warning("Unable to contact the BitMEX API (%s). Please check the URL. Retrying. " +
                            "Request: %s %s \n %s" % (e, url, json.dumps(postdict)))
        time.sleep(1)
        return retry()

    # Reset retry counter on success
    self.retries = 0

    return response.json()
Member
avatar
加入于:
帖子: 5
声望: 0

以上是我自己修改过的关于BitMEX 使用HTTP请求时遇到错误的处理

© 2015-2019 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号-3