(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody)
| 3131 | return {'url': url, 'method': method, 'body': body, 'headers': headers} |
| 3132 | |
| 3133 | def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): |
| 3134 | if (code == 418) or (code == 429): |
| 3135 | raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) |
| 3136 | # error response in a form: {"code": -1013, "msg": "Invalid quantity."} |
| 3137 | # following block cointains legacy checks against message patterns in "msg" property |
| 3138 | # will switch "code" checks eventually, when we know all of them |
| 3139 | if code >= 400: |
| 3140 | if body.find('Price * QTY is zero or less') >= 0: |
| 3141 | raise InvalidOrder(self.id + ' order cost = amount * price is zero or less ' + body) |
| 3142 | if body.find('LOT_SIZE') >= 0: |
| 3143 | raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size ' + body) |
| 3144 | if body.find('PRICE_FILTER') >= 0: |
| 3145 | raise InvalidOrder(self.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use self.price_to_precision(symbol, amount) ' + body) |
| 3146 | if response is None: |
| 3147 | return None # fallback to default error handler |
| 3148 | # check success value for wapi endpoints |
| 3149 | # response in format {'msg': 'The coin does not exist.', 'success': True/false} |
| 3150 | success = self.safe_bool(response, 'success', True) |
| 3151 | if not success: |
| 3152 | messageInner = self.safe_string(response, 'msg') |
| 3153 | parsedMessage = None |
| 3154 | if messageInner is not None: |
| 3155 | try: |
| 3156 | parsedMessage = json.loads(messageInner) |
| 3157 | except Exception as e: |
| 3158 | # do nothing |
| 3159 | parsedMessage = None |
| 3160 | if parsedMessage is not None: |
| 3161 | response = parsedMessage |
| 3162 | message = self.safe_string(response, 'msg') |
| 3163 | if message is not None: |
| 3164 | self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + message) |
| 3165 | self.throw_broadly_matched_exception(self.exceptions['broad'], message, self.id + ' ' + message) |
| 3166 | # checks against error codes |
| 3167 | error = self.safe_string(response, 'code') |
| 3168 | if error is not None: |
| 3169 | # https://github.com/ccxt/ccxt/issues/6501 |
| 3170 | # https://github.com/ccxt/ccxt/issues/7742 |
| 3171 | if (error == '200') or Precise.string_equals(error, '0'): |
| 3172 | return None |
| 3173 | # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} |
| 3174 | # despite that their message is very confusing, it is raised by Binance |
| 3175 | # on a temporary ban, the API key is valid, but disabled for a while |
| 3176 | if (error == '-2015') and self.options['hasAlreadyAuthenticatedSuccessfully']: |
| 3177 | raise DDoSProtection(self.id + ' temporary banned: ' + body) |
| 3178 | feedback = self.id + ' ' + body |
| 3179 | self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback) |
| 3180 | raise ExchangeError(feedback) |
| 3181 | if not success: |
| 3182 | raise ExchangeError(self.id + ' ' + body) |
| 3183 | return None |
| 3184 | |
| 3185 | def calculate_rate_limiter_cost(self, api, method, path, params, config={}): |
| 3186 | if ('noSymbol' in config) and not ('symbol' in params): |
nothing calls this directly
no test coverage detected