def handle_errors(self, code, reason, url, method, headers, 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)
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): 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, 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)
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, code, reason, url, method, headers, body, response=None): 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)