def handle_errors(self, code, reason, url, method, headers, body, response=None): if code == 520: raise ExchangeNotAvailable(self.id + ' ' + body) if body.find('Invalid order') >= 0: raise InvalidOrder(self.id + ' ' + body) if body.find('Invalid nonce') >= 0: raise InvalidNonce(self.id + ' ' + body) if body.find('Insufficient funds') >= 0: raise InsufficientFunds(self.id + ' ' + body) if body.find('Cancel pending') >= 0: raise CancelPending(self.id + ' ' + body) if body.find('Invalid arguments:volume') >= 0: raise InvalidOrder(self.id + ' ' + body) if body[0] == '{': response = json.loads(body) if not isinstance(response, basestring): if 'error' in response: numErrors = len(response['error']) if numErrors: message = self.id + ' ' + self.json(response) for i in range(0, len(response['error'])): if response['error'][i] in self.exceptions: raise self.exceptions[response['error'][i]]( message) raise ExchangeError(message)
def handle_errors(self, code, 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 fixedJSONString = self.sanitize_broken_json_string(body) if fixedJSONString[0] == '{': response = json.loads(fixedJSONString) if 'Success' in response: success = self.safe_value(response, 'Success') if success is not None: if not success: error = self.safe_string(response, 'Error') feedback = self.id if isinstance(error, basestring): feedback = feedback + ' ' + error if error.find('Invalid trade amount') >= 0: raise InvalidOrder(feedback) if error.find('No matching trades found') >= 0: raise OrderNotFound(feedback) if error.find('does not exist') >= 0: raise OrderNotFound(feedback) if error.find('Insufficient Funds') >= 0: raise InsufficientFunds(feedback) if error.find('Nonce has already been used') >= 0: raise InvalidNonce(feedback) else: feedback = feedback + ' ' + fixedJSONString raise ExchangeError(feedback)
def handle_errors(self, code, reason, url, method, headers, body): if body.find('Invalid nonce') >= 0: raise InvalidNonce(self.id + ' ' + body) if body.find('Insufficient funds') >= 0: raise InsufficientFunds(self.id + ' ' + body) if body.find('Cancel pending') >= 0: raise CancelPending(self.id + ' ' + body)
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: error = response['error'] feedback = self.id + ' ' + self.json(response) if error == 'Invalid order number, or you are not the person who placed the order.': raise OrderNotFound(feedback) elif error == 'Order not found, or you are not the person who placed it.': raise OrderNotFound(feedback) elif error == 'Invalid API key/secret pair.': raise AuthenticationError(feedback) elif error == 'Please do not make more than 8 API calls per second.': raise DDoSProtection(feedback) elif error.find('Total must be at least') >= 0: raise InvalidOrder(feedback) elif error.find('Not enough') >= 0: raise InsufficientFunds(feedback) elif error.find('Nonce must be greater') >= 0: raise InvalidNonce(feedback) elif error.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, code, reason, url, method, headers, body, response, requestHeaders, requestBody): if not response: return # fallback to default error handler # # {"usOut":1535877098645376,"usIn":1535877098643364,"usDiff":2012,"testnet":false, # "success":false,"message":"order_not_found","error":10004} # error = self.safe_string(response, 'error') if error is not None and error != '0': error_dict = literal_eval(error) error_code = self.safe_string(error_dict, 'code') message = self.safe_string(error_dict, 'message') message = message.lower() if message else message feedback = self.id + ' ' + body if 'order_not_found' == message: raise OrderNotFound(feedback) elif ('invalid' in message and 'nonce' in message) or ('invalid' in feedback and 'nonce' in feedback): raise InvalidNonce(feedback) # elif 'invalid_credentials' in message: # raise AuthenticationError(feedback) exceptions = self.exceptions if error_code in exceptions: raise exceptions[error_code](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 # {"error":"Permission denied."} if 'error' in response: message = response['error'] feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if message in exceptions: raise exceptions[message](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 throw_exception_on_error(self, response): # {success: False, code: "ERROR", msg: "Min price:100.0"} # {success: True, code: "OK", msg: "Operation succeeded."} if not('success' in list(response.keys())): raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) if response['success'] is True: return # not an error if not('code' in list(response.keys())) or not('msg' in list(response.keys())): raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) code = self.safe_string(response, 'code') message = self.safe_string(response, 'msg') feedback = self.id + ' ' + self.json(response) if code == 'UNAUTH': if message == 'Invalid nonce': raise InvalidNonce(feedback) raise AuthenticationError(feedback) elif code == 'ERROR': if message.find('The precision of amount') >= 0: raise InvalidOrder(feedback) # amount violates precision.amount if message.find('Min amount each order') >= 0: raise InvalidOrder(feedback) # amount < limits.amount.min if message.find('Min price:') >= 0: raise InvalidOrder(feedback) # price < limits.price.min if message.find('The precision of price') >= 0: raise InvalidOrder(feedback) # price violates precision.price elif code == 'NO_BALANCE': if message.find('Insufficient balance') >= 0: raise InsufficientFunds(feedback) raise ExchangeError(self.id + ': unknown response: ' + self.json(response))
async def fetch_order_book_snapshot(self, client, symbol, params={}): orderbook = self.orderbooks[symbol] market = self.market(symbol) messageHash = 'l2orderbook' + ':' + market['id'] subscription = client.subscriptions[messageHash] if not subscription['fetchingOrderBookSnapshot']: subscription['startTime'] = self.milliseconds() subscription['fetchingOrderBookSnapshot'] = True maxAttempts = self.safe_integer(self.options, 'fetchOrderBookSnapshotMaxAttempts', 10) maxDelay = self.safe_integer(self.options, 'fetchOrderBookSnapshotMaxDelay', 10000) try: limit = self.safe_integer(subscription, 'limit', 0) # 3. Request a level-2 order book snapshot for the market from the REST API Order Books endpoint with limit set to 0. snapshot = await self.fetch_order_book(symbol, limit) firstBuffered = self.safe_value(orderbook.cache, 0) firstData = self.safe_value(firstBuffered, 'data') firstNonce = self.safe_integer(firstData, 'u') length = len(orderbook.cache) lastBuffered = self.safe_value(orderbook.cache, length - 1) lastData = self.safe_value(lastBuffered, 'data') lastNonce = self.safe_integer(lastData, 'u') bothExist = (firstNonce is not None) and (lastNonce is not None) # ensure the snapshot is inside the range of our cached messages # for example if the snapshot nonce is 100 # the first nonce must be less than or equal to 101 and the last nonce must be greater than 101 if bothExist and (firstNonce <= snapshot['nonce'] + 1) and (lastNonce > snapshot['nonce']): orderbook.reset(snapshot) for i in range(0, len(orderbook.cache)): message = orderbook.cache[i] data = self.safe_value(message, 'data') u = self.safe_integer(data, 'u') if u > orderbook['nonce']: # 5. Discard all order book update messages with sequence numbers less than or equal to the snapshot sequence number. # 6. Apply the remaining buffered order book update messages and any incoming order book update messages to the order book snapshot. self.handle_order_book_message(client, message, orderbook) subscription['fetchingOrderBookSnapshot'] = False client.resolve(orderbook, messageHash) else: # 4. If the sequence in the order book snapshot is less than the sequence of the # first buffered order book update message, discard the order book snapshot and retry step 3. # self will continue to recurse until we have a buffered message # since updates the order book endpoint depend on order events # so it will eventually raise if there are no orders on a pair subscription['numAttempts'] = subscription['numAttempts'] + 1 timeElapsed = self.milliseconds() - subscription['startTime'] maxAttemptsValid = subscription['numAttempts'] < maxAttempts timeElapsedValid = timeElapsed < maxDelay if maxAttemptsValid and timeElapsedValid: self.delay(self.rateLimit, self.fetch_order_book_snapshot, client, symbol) else: endpart = ' in ' + str(maxAttempts) + ' attempts' if (not maxAttemptsValid) else ' after ' + str(maxDelay) + ' milliseconds' raise InvalidNonce(self.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + endpart) except Exception as e: subscription['fetchingOrderBookSnapshot'] = False client.reject(e, messageHash)
async def fetch_order_book_snapshot(self, client, message, subscription): symbol = self.safe_string(subscription, 'symbol') limit = self.safe_integer(subscription, 'limit') messageHash = self.safe_string(subscription, 'messageHash') try: # 2. Initiate a REST request to get the snapshot data of Level 2 order book. # todo: self is a synch blocking call in ccxt.php - make it async snapshot = await self.fetch_order_book(symbol, limit) orderbook = self.orderbooks[symbol] messages = orderbook.cache # make sure we have at least one delta before fetching the snapshot # otherwise we cannot synchronize the feed with the snapshot # and that will lead to a bidask cross as reported here # https://github.com/ccxt/ccxt/issues/6762 firstMessage = self.safe_value(messages, 0, {}) data = self.safe_value(firstMessage, 'data', {}) sequenceStart = self.safe_integer(data, 'sequenceStart') nonce = self.safe_integer(snapshot, 'nonce') previousSequence = sequenceStart - 1 # if the received snapshot is earlier than the first cached delta # then we cannot align it with the cached deltas and we need to # retry synchronizing in maxAttempts if nonce < previousSequence: options = self.safe_value(self.options, 'fetchOrderBookSnapshot', {}) maxAttempts = self.safe_integer(options, 'maxAttempts', 3) numAttempts = self.safe_integer(subscription, 'numAttempts', 0) # retry to syncrhonize if we haven't reached maxAttempts yet if numAttempts < maxAttempts: # safety guard if messageHash in client.subscriptions: numAttempts = self.sum(numAttempts, 1) subscription['numAttempts'] = numAttempts client.subscriptions[messageHash] = subscription self.spawn(self.fetch_order_book_snapshot, client, message, subscription) else: # raise upon failing to synchronize in maxAttempts raise InvalidNonce( self.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + ' in ' + str(maxAttempts) + ' attempts') else: orderbook.reset(snapshot) # unroll the accumulated deltas # 3. Playback the cached Level 2 data flow. for i in range(0, len(messages)): message = messages[i] self.handle_order_book_message(client, message, orderbook) self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) except Exception as e: client.reject(e, messageHash)
def throw_exception_on_error(self, response): if 'success' in response: if not response['success']: if 'code' in response: message = self.safe_string(response, 'msg') if response['code'] == 'UNAUTH': if message == 'Invalid nonce': raise InvalidNonce(self.id + ' ' + message) raise AuthenticationError(self.id + ' ' + self.json(response)) elif response['code'] == 'ERROR': if message.find('precision of amount') >= 0: raise InvalidOrder(self.id + ' ' + message)
def handle_errors(self, code, reason, url, method, headers, body): if code >= 400: if body and(body[0] == "{"): response = json.loads(body) if 'success' in response: if not response['success']: if 'code' in response: if response['code'] == 'UNAUTH': message = self.safe_string(response, 'msg') if message == 'Invalid nonce': raise InvalidNonce(self.id + ' ' + message) raise AuthenticationError(self.id + ' ' + self.json(response)) raise ExchangeError(self.id + ' ' + self.json(response)) else: raise ExchangeError(self.id + ' ' + str(code) + ' ' + reason)
def throw_exception_on_error(self, response): # # API endpoints return the following formats # {success: False, code: "ERROR", msg: "Min price:100.0"} # {success: True, code: "OK", msg: "Operation succeeded."} # # Web OHLCV endpoint returns self: # {s: "ok", o: [], h: [], l: [], c: [], v: []} # # This particular method handles API responses only # if not ('success' in list(response.keys())): return if response['success'] is True: return # not an error if not ('code' in list(response.keys())) or not ('msg' in list( response.keys())): raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) code = self.safe_string(response, 'code') message = self.safe_string(response, 'msg') feedback = self.id + ' ' + self.json(response) if code == 'UNAUTH': if message == 'Invalid nonce': raise InvalidNonce(feedback) raise AuthenticationError(feedback) elif code == 'ERROR': if message.find('The precision of amount') >= 0: raise InvalidOrder( feedback) # amount violates precision.amount if message.find('Min amount each order') >= 0: raise InvalidOrder(feedback) # amount < limits.amount.min if message.find('Min price:') >= 0: raise InvalidOrder(feedback) # price < limits.price.min if message.find('Max price:') >= 0: raise InvalidOrder(feedback) # price > limits.price.max if message.find('The precision of price') >= 0: raise InvalidOrder(feedback) # price violates precision.price elif code == 'NO_BALANCE': if message.find('Insufficient balance') >= 0: raise InsufficientFunds(feedback) raise ExchangeError(self.id + ': unknown response: ' + self.json(response))
async def fetch_order_book_snapshot(self, client, symbol, params={}): orderbook = self.orderbooks[symbol] market = self.market(symbol) messageHash = 'l2orderbook' + ':' + market['id'] subscription = client.subscriptions[messageHash] maxAttempts = self.safe_integer(self.options, 'fetchOrderBookSnapshotMaxAttempts', 3) subscription['fetchingOrderBookSnapshot'] = True try: limit = self.safe_integer(subscription, 'limit', 0) # 3. Request a level-2 order book snapshot for the market from the REST API Order Books endpoint with limit set to 0. snapshot = await self.fetch_order_book(symbol, limit) firstBuffered = self.safe_value(orderbook.cache, 0) firstData = self.safe_value(firstBuffered, 'data') firstNonce = self.safe_integer(firstData, 'u') length = len(orderbook.cache) lastBuffered = self.safe_value(orderbook.cache, length - 1) lastData = self.safe_value(lastBuffered, 'data') lastNonce = self.safe_integer(lastData, 'u') # ensure the snapshot is inside the range of our cached messages bothExist = (firstNonce is not None) and (lastNonce is not None) if bothExist and (snapshot['nonce'] > firstNonce) and (snapshot['nonce'] < lastNonce): orderbook.reset(snapshot) for i in range(0, len(orderbook.cache)): message = orderbook.cache[i] data = self.safe_value(message, 'data') u = self.safe_integer(data, 'u') if u > orderbook['nonce']: # 5. Discard all order book update messages with sequence numbers less than or equal to the snapshot sequence number. # 6. Apply the remaining buffered order book update messages and any incoming order book update messages to the order book snapshot. self.handle_order_book_message(client, message, orderbook) subscription['fetchingOrderBookSnapshot'] = False client.resolve(orderbook, messageHash) else: # 4. If the sequence in the order book snapshot is less than the sequence of the # first buffered order book update message, discard the order book snapshot and retry step 3. subscription['numAttempts'] = subscription['numAttempts'] + 1 if subscription['numAttempts'] < maxAttempts: self.delay(self.rateLimit, self.fetch_order_book_snapshot, client, symbol) else: raise InvalidNonce(self.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + ' in ' + str(maxAttempts) + ' attempts') except Exception as e: subscription['fetchingOrderBookSnapshot'] = False client.reject(e, messageHash)
def handle_errors(self, code, reason, url, method, headers, body, response): if response is None: return # fallback to default error handler if 'Success' in response: success = self.safe_value(response, 'Success') if success is not None: if not success: error = self.safe_string(response, 'Error') feedback = self.id + ' ' + body if isinstance(error, basestring): if error.find('Invalid trade amount') >= 0: raise InvalidOrder(feedback) if error.find('No matching trades found') >= 0: raise OrderNotFound(feedback) if error.find('does not exist') >= 0: raise OrderNotFound(feedback) if error.find('Insufficient Funds') >= 0: raise InsufficientFunds(feedback) if error.find('Nonce has already been used') >= 0: raise InvalidNonce(feedback) raise ExchangeError(feedback)