def handle_errors(self, code, reason, url, method, headers, body): if code >= 400: if code == 418: raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) if body.find('MIN_NOTIONAL') >= 0: raise InvalidOrder(self.id + ' order cost = amount * price should be >(0.001 BTC or 0.01 ETH or 1 BNB or 1 USDT)' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size, use self.amount_to_lots(symbol, amount) ' + body) if body.find('PRICE_FILTER') >= 0: raise InvalidOrder(self.id + ' order price exceeds allowed price precision or invalid, use self.price_to_precision(symbol, amount) ' + body) if body.find('Order does not exist') >= 0: raise OrderNotFound(self.id + ' ' + body) if body[0] == "{": response = json.loads(body) error = self.safe_value(response, 'code') if error == -2010: raise InsufficientFunds(self.id + ' ' + self.json(response)) elif error == -2011: raise OrderNotFound(self.id + ' ' + self.json(response)) elif error < 0: raise ExchangeError(self.id + ' ' + self.json(response))
def handle_rest_response(self, response, url, method='GET', headers=None, body=None): try: self.last_json_response = json.loads(response) if len(response) > 1 else None return self.last_json_response except Exception as e: ddos_protection = re.search('(cloudflare|incapsula)', response, flags=re.IGNORECASE) exchange_not_available = re.search('(offline|busy|retry|wait|unavailable|maintain|maintenance|maintenancing)', response, flags=re.IGNORECASE) if ddos_protection: raise DDoSProtection(' '.join([self.id, method, url, response])) if exchange_not_available: message = 'exchange downtime, exchange closed for maintenance or offline, DDoS protection or rate-limiting in effect' raise ExchangeNotAvailable(' '.join([ self.id, method, url, response, message, ])) if isinstance(e, ValueError): raise ExchangeError(' '.join([self.id, method, url, response, str(e)])) raise
def handle_errors(self, code, reason, url, method, headers, body, response): if code == 429: raise DDoSProtection(self.id + ' ' + body) if code >= 400: if body: if body[0] == '{': response = json.loads(body) error = self.safe_value(response, 'error', {}) message = self.safe_string(error, 'message') feedback = self.id + ' ' + body exact = self.exceptions['exact'] if message in exact: raise exact[message](feedback) broad = self.exceptions['broad'] broadKey = self.findBroadlyMatchedKey(broad, message) if broadKey is not None: raise broad[broadKey](feedback) if code == 400: raise BadRequest(feedback) raise ExchangeError(feedback) # unknown message
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody): if response is None: return # fallback to default error handler # # {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} # error = self.safe_string(response, 'error') feedback = self.id + ' ' + body if error is not None: self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback) self.throw_broadly_matched_exception(self.exceptions['broad'], error, feedback) if code == 401 or code == 403: raise AuthenticationError(feedback) elif code == 429: raise DDoSProtection(feedback) if code < 400: return raise ExchangeError(feedback)
def request(self, path, api='public', method='GET', params={}, headers=None, body=None): response = self.fetch2(path, api, method, params, headers, body) if not isinstance(response, basestring): if 'error' in response: numErrors = len(response['error']) if numErrors: for i in range(0, len(response['error'])): if response['error'][i] == 'EService:Unavailable': raise ExchangeNotAvailable(self.id + ' ' + self.json(response)) if response['error'][i] == 'EService:Busy': raise DDoSProtection(self.id + ' ' + self.json(response)) raise ExchangeError(self.id + ' ' + self.json(response)) return response
def throw_exception_on_error(self, response): if 'message' in response: if response['message'] == 'APISIGN_NOT_PROVIDED': raise AuthenticationError(self.id + ' ' + self.json(response)) if response['message'] == 'INVALID_SIGNATURE': raise AuthenticationError(self.id + ' ' + self.json(response)) if response['message'] == 'INSUFFICIENT_FUNDS': raise InsufficientFunds(self.id + ' ' + self.json(response)) if response['message'] == 'MIN_TRADE_REQUIREMENT_NOT_MET': raise InvalidOrder(self.id + ' ' + self.json(response)) if response['message'] == 'APIKEY_INVALID': if self.hasAlreadyAuthenticatedSuccessfully: raise DDoSProtection(self.id + ' ' + self.json(response)) else: raise AuthenticationError(self.id + ' ' + self.json(response)) if response[ 'message'] == 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT': raise InvalidOrder(self.id + ' order cost should be over 50k satoshi ' + self.json(response))
def handle_errors(self, code, reason, url, method, headers, body): if code >= 400: if (code == 418) or (code == 429): raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) if body.find('Price * QTY is zero or less') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price is zero or less ' + body) if body.find('MIN_NOTIONAL') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price is too small ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder( self.id + ' order amount should be evenly divisible by lot size, use self.amount_to_lots(symbol, amount) ' + body) if body.find('PRICE_FILTER') >= 0: raise InvalidOrder( self.id + ' order price exceeds allowed price precision or invalid, use self.price_to_precision(symbol, amount) ' + body) if body.find('Order does not exist') >= 0: raise OrderNotFound(self.id + ' ' + body) if isinstance(body, basestring): if len(body) > 0: if body[0] == '{': response = json.loads(body) error = self.safe_value(response, 'code') if error is not None: if error == -2010: raise InsufficientFunds(self.id + ' ' + self.json(response)) elif error == -2011: raise OrderNotFound(self.id + ' ' + self.json(response)) elif error == -1013: # Invalid quantity raise InvalidOrder(self.id + ' ' + self.json(response))
def handle_errors(self, code, reason, url, method, headers, body, response): if code >= 200 and code < 300: return exceptions = self.exceptions if code == 401: # expected non-json response if body in exceptions: raise exceptions[body](self.id + ' ' + body) else: return if code == 429: raise DDoSProtection(self.id + ' ' + body) if response is None: return feedback = self.id + ' ' + body message = self.safe_string(response, 'message') errors = self.safe_value(response, 'errors') if message is not None: # # {"message": "Order not found"} # if message in exceptions: raise exceptions[message](feedback) elif errors is not None: # # {"errors": {"user": ["not_enough_free_balance"]}} # {"errors": {"quantity": ["less_than_order_size"]}} # {"errors": {"order": ["Can not update partially filled order"]}} # types = list(errors.keys()) for i in range(0, len(types)): type = types[i] errorMessages = errors[type] for j in range(0, len(errorMessages)): message = errorMessages[j] if message in exceptions: raise exceptions[message](feedback) else: raise ExchangeError(feedback)
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody): if (code == 418) or (code == 429): raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) if code == 404: raise ExchangeError(self.id + ' ' + str(code) + ' endpoint not found') if response is not None: success = self.safe_value(response, 'success') if not success: feedback = self.id + ' ' + body message = self.safe_value(response, 'message') if isinstance(message, basestring): exact = self.safe_value(self.exceptions, 'exact', {}) if message in exact: raise exact[message](feedback) broad = self.safe_value(self.exceptions, 'broad', {}) broadKey = self.findBroadlyMatchedKey(broad, body) if broadKey is not None: raise broad[broadKey](feedback) raise ExchangeError(feedback)
def handle_errors(self, code, reason, url, method, headers, body, response=None): if code < 400: return if not isinstance(body, basestring): return # fallback to default error handler if len(body) < 2: return # fallback to default error handler if (body[0] == '{') or (body[0] == '['): response = json.loads(body) message = self.id + ' ' + self.safe_value(response, 'detail', body) if code == 401 or code == 403: raise AuthenticationError(message) elif code == 429: raise DDoSProtection(message) raise ExchangeError(message)
def request(self, path, api='public', method='GET', params={}, headers=None, body=None): response = self.fetch2(path, api, method, params, headers, body) message = self.id + ' ' + self.json(response) if 'error' in response: if 'code' in response['error']: code = response['error']['code'] if code == '2033': # \u64cd\u4f5c\u5931\u8d25\uff01\u8ba2\u5355\u5df2\u5b8c\u6210\u6216\u5df2\u64a4\u9500 # operation failednot Orders have been completed or revoked # e.g. trying to cancel a filled order raise OrderNotFound(message) elif code == '2068': # \u4e0b\u5355\u6570\u91cf\u4e0d\u80fd\u4f4e\u4e8e # The number of orders can not be less than raise InvalidOrder(message) elif code == '3012': raise AuthenticationError(message) # invalid api key elif code == '3025': raise AuthenticationError(message) # signature failed elif code == '4000': # \u5f53\u524d\u7f51\u7edc\u8fde\u63a5\u4e0d\u7a33\u5b9a\uff0c\u8bf7\u7a0d\u5019\u91cd\u8bd5 # The current network connection is unstable. Please try again later raise ExchangeNotAvailable(message) elif code == '4003': raise DDoSProtection( message) # server is busy, try again later raise ExchangeError(message) if not ('result' in list(response.keys())): raise ExchangeError(message) if method == 'GET': return response else: return response['result'][0]
def handle_errors(self, code, reason, url, method, headers, body): if code >= 400: if code == 418: raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) if body.find('MIN_NOTIONAL') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price should be > 0.001 BTC ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder( self.id + ' order amount should be evenly divisible by lot size, use self.amount_to_lots(symbol, amount) ' + body) if body.find('PRICE_FILTER') >= 0: raise InvalidOrder( self.id + ' order price exceeds allowed price precision or invalid, use self.price_to_precision(symbol, amount) ' + body) if body.find('Order does not exist') >= 0: raise OrderNotFound(self.id + ' ' + body)
def request(self, path, api='public', method='GET', params={}, headers=None, body=None): response = self.fetch2(path, api, method, params, headers, body) message = self.id + ' ' + self.json(response) if 'error' in response: if 'code' in response['error']: code = response['error']['code'] if code == '3012': raise AuthenticationError(message) # invalid api key elif code == '3025': raise AuthenticationError(message) # signature failed elif code == '4003': raise DDoSProtection( message) # server is busy, try again later raise ExchangeError(message) if not ('result' in list(response.keys())): raise ExchangeError(message) return response['result'][0]
async def request(self, path, api='public', method='GET', params={}, headers=None, body=None): response = await self.fetch2(path, api, method, params, headers, body) message = self.id + ' ' + self.json(response) if 'error' in response: if 'code' in response['error']: code = response['error']['code'] if code == '2068': # \u4e0b\u5355\u6570\u91cf\u4e0d\u80fd\u4f4e\u4e8e # The number of orders can not be less than raise InvalidOrder(message) elif code == '3012': raise AuthenticationError(message) # invalid api key elif code == '3025': raise AuthenticationError(message) # signature failed elif code == '4003': raise DDoSProtection(message) # server is busy, try again later raise ExchangeError(message) if not('result' in list(response.keys())): raise ExchangeError(message) if method == 'GET': return response else: return response['result'][0]
def handle_errors(self, code, reason, url, method, headers, body, response=None): if not self.is_json_encoded_object(body): return # fallback to default error handler if (code >= 200) and (code < 300): return # no error response = json.loads(body) message = self.safe_string(response, 'errorDescription') feedback = self.id + ' ' + self.json(response) exact = self.exceptions['exact'] if message in exact: raise exact[message](feedback) broad = self.exceptions['broad'] broadKey = self.findBroadlyMatchedKey(broad, message) if broadKey is not None: raise broad[broadKey](feedback) if code == 400: raise BadRequest(feedback) elif code == 401: raise AuthenticationError(feedback) elif code == 403: raise AuthenticationError(feedback) elif code == 429: raise DDoSProtection(feedback) elif code == 500: raise ExchangeError(feedback) elif code == 503: raise ExchangeNotAvailable(feedback) elif code == 504: raise RequestTimeout(feedback) raise ExchangeError(feedback) # unknown message
def handle_errors(self, code, reason, url, method, headers, body): response = None try: response = json.loads(body) except Exception as e: # syntax error, resort to default error handler return if 'error' in response: message = response['error'] feedback = self.id + ' ' + self.json(response) if message == 'Invalid order number, or you are not the person who placed the order.': raise OrderNotFound(feedback) elif message == 'Connection timed out. Please try again.': raise RequestTimeout(feedback) elif message == 'Internal error. Please try again.': raise ExchangeNotAvailable(feedback) elif message == 'Order not found, or you are not the person who placed it.': raise OrderNotFound(feedback) elif message == 'Invalid API key/secret pair.': raise AuthenticationError(feedback) elif message == 'Please do not make more than 8 API calls per second.': raise DDoSProtection(feedback) elif message.find('Total must be at least') >= 0: raise InvalidOrder(feedback) elif message.find('This account is frozen.') >= 0: raise AccountSuspended(feedback) elif message.find('Not enough') >= 0: raise InsufficientFunds(feedback) elif message.find('Nonce must be greater') >= 0: raise InvalidNonce(feedback) elif message.find( 'You have already called cancelOrder or moveOrder on self order.' ) >= 0: raise CancelPending(feedback) else: raise ExchangeError(self.id + ' unknown error ' + self.json(response))
def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody): if (httpCode == 418) or (httpCode == 429): raise DDoSProtection(self.id + ' ' + str(httpCode) + ' ' + reason + ' ' + body) # error response in a form: {"code": -1013, "msg": "Invalid quantity."} # following block cointains legacy checks against message patterns in "msg" property # will switch "code" checks eventually, when we know all of them if httpCode >= 400: if body.find('Price * QTY is zero or less') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price is zero or less ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder( self.id + ' order amount should be evenly divisible by lot size ' + body) if body.find('PRICE_FILTER') >= 0: 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) if response is None: return # fallback to default error handler # # {"code":-1128,"msg":"Combination of optional parameters invalid."} # errorCode = self.safe_string(response, 'code') if (errorCode is not None) and (errorCode != '0'): feedback = self.id + ' ' + self.json(response) self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback) message = self.safe_string(response, 'msg') self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback) raise ExchangeError(feedback)
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody): if (code == 418) or (code == 429): raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) # error response in a form: {"code": -1013, "msg": "Invalid quantity."} # following block cointains legacy checks against message patterns in "msg" property # will switch "code" checks eventually, when we know all of them if code >= 400: if body.find('Price * QTY is zero or less') >= 0: raise InvalidOrder(self.id + ' order cost = amount * price is zero or less ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size ' + body) if body.find('PRICE_FILTER') >= 0: 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) if response is None: return # fallback to default error handler # check success value for wapi endpoints # response in format {'msg': 'The coin does not exist.', 'success': True/false} success = self.safe_value(response, 'success', True) if not success: message = self.safe_string(response, 'msg') parsedMessage = None if message is not None: try: parsedMessage = json.loads(message) except Exception as e: # do nothing parsedMessage = None if parsedMessage is not None: response = parsedMessage exceptions = self.exceptions message = self.safe_string(response, 'msg') if message in exceptions: ExceptionClass = exceptions[message] raise ExceptionClass(self.id + ' ' + message) if not success: raise ExchangeError(self.id + ' ' + body)
def handle_errors(self, code, reason, url, method, headers, body): if code >= 300: if body[0] == '{': response = json.loads(body) if 'errorCode' in response: error = response['errorCode'] # todo: rework for error-maps, like in liqui or okcoinusd if error == 1: raise ExchangeError(self.id + ' ' + self.json(response)) elif error == 2: if 'errorMessage' in response: if response['errorMessage'] == 'User not found': raise AuthenticationError(self.id + ' ' + response['errorMessage']) else: raise ExchangeError(self.id + ' ' + self.json(response)) elif (error == 10) or (error == 11) or (error == 12) or (error == 20) or (error == 30) or (error == 101) or (error == 102): raise AuthenticationError(self.id + ' ' + self.json(response)) elif error == 31: raise NotSupported(self.id + ' ' + self.json(response)) elif error == 32: raise ExchangeError(self.id + ' ' + self.json(response)) elif error == 100: raise ExchangeError(self.id + ': Invalid parameters ' + self.json(response)) elif error == 103: raise InvalidOrder(self.id + ': Invalid currency ' + self.json(response)) elif error == 104: raise InvalidOrder(self.id + ': Invalid amount ' + self.json(response)) elif error == 105: raise InvalidOrder(self.id + ': Unable to block funds ' + self.json(response)) elif error == 503: raise ExchangeNotAvailable(self.id + ': Exchange is not available ' + self.json(response)) elif error == 429: raise DDoSProtection(self.id + ': Too many requests' + self.json(response)) else: raise ExchangeError(self.id + ' ' + self.json(response)) raise ExchangeError(self.id + ' ' + body)
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody): if not self.is_json_encoded_object(body): return # fallback to default error handler if (code >= 200) and (code < 300): return # no error message = self.safe_string(response, 'errorDescription') feedback = self.id + ' ' + body self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback) self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback) if code == 400: raise BadRequest(feedback) elif code == 401: raise AuthenticationError(feedback) elif code == 403: raise AuthenticationError(feedback) elif code == 429: raise DDoSProtection(feedback) elif code == 500: raise ExchangeError(feedback) elif code == 503: raise ExchangeNotAvailable(feedback) elif code == 504: raise RequestTimeout(feedback) raise ExchangeError(feedback) # unknown message
def handle_errors(self, code, reason, url, method, headers, body): if body[0] == '{': response = json.loads(body) # {success: False, message: "message"} success = self.safe_value(response, 'success') if success is None: raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) if isinstance(success, basestring): # bleutrade uses string instead of boolean success = True if (success == 'true') else False if not success: message = self.safe_string(response, 'message') feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if message in exceptions: raise exceptions[message](feedback) if message == 'APIKEY_INVALID': if self.hasAlreadyAuthenticatedSuccessfully: raise DDoSProtection(feedback) else: raise AuthenticationError(feedback) if message == 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT': raise InvalidOrder(self.id + ' order cost should be over 50k satoshi ' + self.json(response)) raise ExchangeError(self.id + ' ' + self.json(response))
def handle_errors(self, code, reason, url, method, headers, body, response=None): if body[0] == '{': response = json.loads(body) # {success: False, message: "message"} success = self.safe_value(response, 'success') if success is None: raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) if isinstance(success, basestring): # bleutrade uses string instead of boolean success = True if (success == 'true') else False if not success: message = self.safe_string(response, 'message') feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if message == 'APIKEY_INVALID': if self.options['hasAlreadyAuthenticatedSuccessfully']: raise DDoSProtection(feedback) else: raise AuthenticationError(feedback) if message == 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT': raise InvalidOrder( self.id + ' order cost should be over 50k satoshi ' + self.json(response)) if message == 'INVALID_ORDER': # Bittrex will return an ambiguous INVALID_ORDER message # upon canceling already-canceled and closed orders # therefore self special case for cancelOrder # url = 'https://bittrex.com/api/v1.1/market/cancel?apikey=API_KEY&uuid=ORDER_UUID' cancel = 'cancel' indexOfCancel = url.find(cancel) if indexOfCancel >= 0: parts = url.split('&') orderId = None for i in range(0, len(parts)): part = parts[i] keyValue = part.split('=') if keyValue[0] == 'uuid': orderId = keyValue[1] break if orderId is not None: raise OrderNotFound(self.id + ' cancelOrder ' + orderId + ' ' + self.json(response)) else: raise OrderNotFound(self.id + ' cancelOrder ' + self.json(response)) if message in exceptions: raise exceptions[message](feedback) if message is not None: if message.find('throttled. Try again') >= 0: raise DDoSProtection(feedback) if message.find('problem') >= 0: raise ExchangeNotAvailable( feedback ) # 'There was a problem processing your request. If self problem persists, please contact...') raise ExchangeError(feedback)
def handle_errors(self, httpCode, reason, url, method, headers, body): if not isinstance(body, basestring): return # fallback to default error handler if len(body) < 2: return # fallback to default error handler if (body[0] == '{') or (body[0] == '['): response = json.loads(body) if 'success' in response: # # 1 - Liqui only returns the integer 'success' key from their private API # # {"success": 1, ...} httpCode == 200 # {"success": 0, ...} httpCode == 200 # # 2 - However, exchanges derived from Liqui, can return non-integers # # It can be a numeric string # {"sucesss": "1", ...} # {"sucesss": "0", ...}, httpCode >= 200(can be 403, 502, etc) # # Or just a string # {"success": "true", ...} # {"success": "false", ...}, httpCode >= 200 # # Or a boolean # {"success": True, ...} # {"success": False, ...}, httpCode >= 200 # # 3 - Oversimplified, Python PEP8 forbids comparison operator(==) of different types # # 4 - We do not want to copy-paste and duplicate the code of self handler to other exchanges derived from Liqui # # To cover points 1, 2, 3 and 4 combined self handler should work like self: # success = self.safe_value(response, 'success', False) if isinstance(success, basestring): if (success == 'true') or (success == '1'): success = True else: success = False if not success: code = self.safe_string(response, 'code') message = self.safe_string(response, 'error') feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if code in exceptions: raise exceptions[code](feedback) # need a second error map for these messages, apparently... # in fact, we can use the same .exceptions with string-keys to save some loc here if message == 'invalid api key': raise AuthenticationError(feedback) elif message == 'api key dont have trade permission': raise AuthenticationError(feedback) elif message.find( 'invalid parameter' ) >= 0: # errorCode 0, returned on buy(symbol, 0, 0) raise InvalidOrder(feedback) elif message == 'invalid order': raise InvalidOrder(feedback) elif message == 'Requests too often': raise DDoSProtection(feedback) elif message == 'not available': raise DDoSProtection(feedback) elif message == 'data unavailable': raise DDoSProtection(feedback) elif message == 'external service unavailable': raise DDoSProtection(feedback) else: raise ExchangeError(self.id + ' unknown "error" value: ' + self.json(response))
def handle_errors(self, code, reason, url, method, headers, body): if (code == 418) or (code == 429): raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) # error response in a form: {"code": -1013, "msg": "Invalid quantity."} # following block cointains legacy checks against message patterns in "msg" property # will switch "code" checks eventually, when we know all of them if code >= 400: if body.find('Price * QTY is zero or less') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price is zero or less ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder( self.id + ' order amount should be evenly divisible by lot size, use self.amount_to_lots(symbol, amount) ' + body) if body.find('PRICE_FILTER') >= 0: raise InvalidOrder( self.id + ' order price exceeds allowed price precision or invalid, use self.price_to_precision(symbol, amount) ' + body) if len(body) > 0: if body[0] == '{': response = json.loads(body) # check success value for wapi endpoints # response in format {'msg': 'The coin does not exist.', 'success': True/false} success = self.safe_value(response, 'success', True) if not success: if 'msg' in response: try: response = json.loads(response['msg']) except Exception as e: response = {} # checks against error codes error = self.safe_string(response, 'code') if error is not None: exceptions = self.exceptions if error in exceptions: # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} # despite that their message is very confusing, it is raised by Binance # on a temporary ban(the API key is valid, but disabled for a while) if (error == '-2015') and self.options[ 'hasAlreadyAuthenticatedSuccessfully']: raise DDoSProtection(self.id + ' temporary banned: ' + body) message = self.safe_string(response, 'msg') if message == 'Order would trigger immediately.': raise InvalidOrder(self.id + ' ' + body) elif message == 'Account has insufficient balance for requested action.': raise InsufficientFunds(self.id + ' ' + body) elif message == 'Rest API trading is not enabled.': raise InsufficientFunds(self.id + ' ' + body) raise exceptions[error](self.id + ' ' + body) else: raise ExchangeError(self.id + ': unknown error code: ' + body + ' ' + error) if not success: raise ExchangeError(self.id + ': success value False: ' + body)