Пример #1
0
    async def fetch(self, url, method='GET', headers=None, body=None):
        """Perform a HTTP request and return decoded JSON data"""
        request_headers = self.prepare_request_headers(headers)
        url = self.proxy + url

        if self.verbose:
            print("\nRequest:", method, url, headers, body)
        self.logger.debug("%s %s, Request: %s %s", method, url, headers, body)

        request_body = body
        encoded_body = body.encode() if body else None
        self.open()
        session_method = getattr(self.session, method.lower())

        http_response = None
        http_status_code = None
        http_status_text = None
        json_response = None
        try:
            async with session_method(yarl.URL(url, encoded=True),
                                      data=encoded_body,
                                      headers=request_headers,
                                      timeout=(self.timeout / 1000),
                                      proxy=self.aiohttp_proxy) as response:
                http_response = await response.text()
                http_status_code = response.status
                http_status_text = response.reason
                json_response = self.parse_json(http_response)
                headers = response.headers
                if self.enableLastHttpResponse:
                    self.last_http_response = http_response
                if self.enableLastResponseHeaders:
                    self.last_response_headers = headers
                if self.enableLastJsonResponse:
                    self.last_json_response = json_response
                if self.verbose:
                    print("\nResponse:", method, url, http_status_code,
                          headers, http_response)
                self.logger.debug("%s %s, Response: %s %s %s", method, url,
                                  http_status_code, headers, http_response)

        except socket.gaierror as e:
            raise ExchangeNotAvailable(method + ' ' + url)

        except concurrent.futures._base.TimeoutError as e:
            raise RequestTimeout(method + ' ' + url)

        except aiohttp.client_exceptions.ClientConnectionError as e:
            raise ExchangeNotAvailable(method + ' ' + url)

        except aiohttp.client_exceptions.ClientError as e:  # base exception class
            raise ExchangeError(method + ' ' + url)

        self.handle_errors(http_status_code, http_status_text, url, method,
                           headers, http_response, json_response,
                           request_headers, request_body)
        self.handle_rest_errors(http_status_code, http_status_text,
                                http_response, url, method)
        self.handle_rest_response(http_response, json_response, url, method)
        if json_response is not None:
            return json_response
        if self.is_text_response(headers):
            return http_response
        return response.content
Пример #2
0
    async def fetch(self, url, method='GET', headers=None, body=None):
        """Perform a HTTP request and return decoded JSON data"""
        request_headers = self.prepare_request_headers(headers)
        url = self.proxy + url

        if self.verbose:
            self.print("\nRequest:", method, url, headers, body)
        self.logger.debug("%s %s, Request: %s %s", method, url, headers, body)

        request_body = body
        encoded_body = body.encode() if body else None
        self.open()
        session_method = getattr(self.session, method.lower())

        http_response = None
        http_status_code = None
        http_status_text = None
        json_response = None
        try:
            async with session_method(yarl.URL(url, encoded=True),
                                      data=encoded_body,
                                      headers=request_headers,
                                      timeout=(self.timeout / 1000),
                                      proxy=self.aiohttp_proxy) as response:
                http_response = await response.text()
                # CIMultiDictProxy
                raw_headers = response.headers
                headers = {}
                for header in raw_headers:
                    if header in headers:
                        headers[header] = headers[header] + ', ' + raw_headers[
                            header]
                    else:
                        headers[header] = raw_headers[header]
                http_status_code = response.status
                http_status_text = response.reason
                http_response = self.on_rest_response(
                    http_status_code, http_status_text, url, method, headers,
                    http_response, request_headers, request_body)
                json_response = self.parse_json(http_response)
                if self.enableLastHttpResponse:
                    self.last_http_response = http_response
                if self.enableLastResponseHeaders:
                    self.last_response_headers = headers
                if self.enableLastJsonResponse:
                    self.last_json_response = json_response
                if self.verbose:
                    self.print("\nResponse:", method, url, http_status_code,
                               headers, http_response)
                self.logger.debug("%s %s, Response: %s %s %s", method, url,
                                  http_status_code, headers, http_response)

        except socket.gaierror as e:
            details = ' '.join([self.id, method, url])
            raise ExchangeNotAvailable(details) from e

        except (concurrent.futures.TimeoutError, asyncio.TimeoutError) as e:
            details = ' '.join([self.id, method, url])
            raise RequestTimeout(details) from e

        except aiohttp.ClientConnectionError as e:
            details = ' '.join([self.id, method, url])
            raise ExchangeNotAvailable(details) from e

        except aiohttp.ClientError as e:  # base exception class
            details = ' '.join([self.id, method, url])
            raise ExchangeError(details) from e

        self.handle_errors(http_status_code, http_status_text, url, method,
                           headers, http_response, json_response,
                           request_headers, request_body)
        self.handle_http_status_code(http_status_code, http_status_text, url,
                                     method, http_response)
        if json_response is not None:
            return json_response
        if self.is_text_response(headers):
            return http_response
        return response.content
Пример #3
0
 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 == 'invalid sign':
                     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 ExchangeNotAvailable(feedback)
                 elif message == 'data unavailable':
                     raise ExchangeNotAvailable(feedback)
                 elif message == 'external service unavailable':
                     raise ExchangeNotAvailable(feedback)
                 else:
                     raise ExchangeError(self.id +
                                         ' unknown "error" value: ' +
                                         self.json(response))
Пример #4
0
 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 ' +
                 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 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:
                 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
             # 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 ExchangeNotAvailable(self.id + ' ' + body)
                     raise exceptions[error](self.id + ' ' + body)
                 else:
                     raise ExchangeError(self.id + ' ' + body)
             if not success:
                 raise ExchangeError(self.id + ' ' + body)
Пример #5
0
 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)