From afc4adf68f68f1a77a393d696e9e7943314c49b0 Mon Sep 17 00:00:00 2001 From: firepol <1702718+firepol@users.noreply.github.com> Date: Mon, 28 May 2018 23:07:28 +0200 Subject: [PATCH] Fix binance API and add several missing methods This fixes #194 --- bitex/api/REST/binance.py | 35 +++++------- bitex/interface/binance.py | 112 +++++++++++++++++++++++++------------ 2 files changed, 90 insertions(+), 57 deletions(-) diff --git a/bitex/api/REST/binance.py b/bitex/api/REST/binance.py index 4f2eec9..d37c1ba 100644 --- a/bitex/api/REST/binance.py +++ b/bitex/api/REST/binance.py @@ -1,7 +1,7 @@ -"""Bitfinex REST API backend. +"""Binance REST API backend. Documentation available at: - https://docs.bitfinex.com/docs + https://github.com/binance-exchange/binance-official-api-docs/ """ # pylint: disable=too-many-arguments # Import Built-ins @@ -21,11 +21,11 @@ class BinanceREST(RESTAPI): - """Bitfinex REST API class.""" + """Binance REST API class.""" def __init__(self, key=None, secret=None, version=None, addr=None, timeout=None, config=None): """Initialize the class instance.""" - addr = 'https://api.binance.com/api' + addr = 'https://api.binance.com' # We force version to None here as different endpoints require different versions, # so the interface will have to define the version as part of the endpoint. super(BinanceREST, self).__init__(addr=addr, version=None, key=key, secret=secret, @@ -44,31 +44,22 @@ def private_query(self, method_verb, endpoint, **request_kwargs): request_kwargs = self.sign_request_kwargs(endpoint, method_verb, **request_kwargs) return self._query(method_verb, **request_kwargs) - # pylint: disable=arguments-differ def sign_request_kwargs(self, endpoint, method_verb=None, **kwargs): """Sign the request.""" - method_verb = method_verb or 'GET' - req_kwargs = super(BinanceREST, self).sign_request_kwargs(endpoint, **kwargs) - req_kwargs['params'] = {} - # Prepare arguments for query request. - params = kwargs.pop('params', {}) + uri = self.generate_uri(endpoint) + url = self.generate_url(uri) + req_kwargs = {'url': url, 'headers': {'X-MBX-APIKEY': self.key.encode('utf-8')}} + params = kwargs.pop('params', {}) params['timestamp'] = str(int(time.time() * 1000)) - - # Build request address req_string = urllib.parse.urlencode(params) + params['signature'] = hmac.new(self.secret.encode('utf-8'), req_string.encode('utf-8'), + hashlib.sha256).hexdigest() - # generate signature - signature = hmac.new(self.secret.encode('utf-8'), req_string.encode('utf-8'), - hashlib.sha256).hexdigest() - - req_string += '&signature=' + signature - + method_verb = method_verb or 'GET' if method_verb == "GET": - req_kwargs['url'] += '?' + req_string + req_kwargs['params'] = params else: - req_kwargs['data'] = req_string - - req_kwargs['headers'] = {'X-MBX-APIKEY': self.key.encode('utf-8')} + req_kwargs['data'] = params return req_kwargs diff --git a/bitex/interface/binance.py b/bitex/interface/binance.py index 03904d1..0c27357 100644 --- a/bitex/interface/binance.py +++ b/bitex/interface/binance.py @@ -2,14 +2,13 @@ # Import Built-Ins import logging -# Import Third-party -import requests - # Import Homebrew from bitex.api.REST.binance import BinanceREST +from bitex.formatters import BinanceFormattedResponse from bitex.interface.rest import RESTInterface from bitex.utils import format_with, check_and_format_pair -from bitex.formatters import BinanceFormattedResponse + +# Import Third-party # Init Logging Facilities log = logging.getLogger(__name__) @@ -23,19 +22,17 @@ class Binance(RESTInterface): """ # pylint: disable=arguments-differ - def __init__(self, **api_kwargs): """Initialize class instance.""" super(Binance, self).__init__('Binance', BinanceREST(**api_kwargs)) def request(self, verb, endpoint, authenticate=False, **req_kwargs): """Preprocess request to API.""" - return super(Binance, self).request(verb, endpoint, authenticate=authenticate, - **req_kwargs) + return super(Binance, self).request(verb, endpoint, authenticate=authenticate, **req_kwargs) def _get_supported_pairs(self): """Return a list of supported pairs.""" - r = requests.request('GET', 'https://api.binance.com/api/v1/exchangeInfo').json() + r = self.exchange_information().json() pairs = [entry['symbol'] for entry in r['symbols']] return pairs @@ -43,36 +40,32 @@ def _get_supported_pairs(self): @format_with(BinanceFormattedResponse) def ticker(self, pair, *args, **kwargs): """Return the ticker for the given pair.""" - payload = {'symbol': pair} - payload.update(kwargs) - return self.request('GET', 'v1/ticker/24hr', params=payload) + kwargs.update({'symbol': pair}) + return self.request('GET', 'api/v1/ticker/24hr', params=kwargs) @check_and_format_pair @format_with(BinanceFormattedResponse) def order_book(self, pair, *args, **kwargs): """Return the order book for the given pair.""" - payload = {'symbol': pair} - payload.update(kwargs) - return self.request("GET", "v1/depth", params=payload) + kwargs.update({'symbol': pair}) + return self.request('GET', 'api/v1/depth', params=kwargs) @check_and_format_pair @format_with(BinanceFormattedResponse) def trades(self, pair, *args, **kwargs): """Return the trades for the given pair.""" - payload = {'symbol': pair} - payload.update(kwargs) - return self.request('GET', 'v1/trades', params=payload) + kwargs.update({'symbol': pair}) + return self.request('GET', 'api/v1/trades', params=kwargs) # Private Endpoints # pylint: disable=unused-argument def _place_order(self, pair, price, size, side, *args, **kwargs): - payload = {'symbol': pair, - 'side': side, - 'type': "LIMIT_MAKER", - 'price': price, - 'quantity': size} - payload.update(kwargs) - return self.request('POST', 'v3/order', authenticate=True, params=payload) + kwargs.update({'symbol': pair, + 'side': side, + 'type': "LIMIT_MAKER", + 'price': price, + 'quantity': size}) + return self.request('POST', 'api/v3/order', authenticate=True, params=kwargs) @check_and_format_pair @format_with(BinanceFormattedResponse) @@ -89,29 +82,78 @@ def bid(self, pair, price, size, *args, **kwargs): @format_with(BinanceFormattedResponse) def order_status(self, order_id, pair, *args, **kwargs): """Return the status of an order with the given id.""" - payload = {'symbol': pair, - 'orderId': order_id} - payload.update(kwargs) - return self.request('GET', 'v3/order', authenticate=True, params=payload) + kwargs.update({'symbol': pair, 'orderId': order_id}) + return self.request('GET', 'api/v3/order', authenticate=True, params=kwargs) @format_with(BinanceFormattedResponse) def open_orders(self, *args, **kwargs): """Return all open orders.""" - return self.request('GET', 'v3/openOrders', authenticate=True, params=kwargs) + return self.request('GET', 'api/v3/openOrders', authenticate=True, params=kwargs) @format_with(BinanceFormattedResponse) def cancel_order(self, *order_ids, pair, **kwargs): """Cancel the order(s) with the given id(s).""" results = [] for order_id in order_ids: - payload = {'symbol': pair, - 'orderId': order_id} - payload.update(kwargs) - r = self.request('DELETE', 'v3/order', authenticate=True, params=payload) + kwargs.update({'symbol': pair, 'orderId': order_id}) + r = self.request('DELETE', 'api/v3/order', authenticate=True, params=kwargs) results.append(r) return results if len(results) > 1 else results[0] @format_with(BinanceFormattedResponse) def wallet(self, *args, **kwargs): - """Return the wallet of this account.""" - return self.request('GET', "v3/account", True) + """Return the wallet of this account and also the current account information.""" + return self.request('GET', 'api/v3/account', authenticate=True) + + ########################### + # Exchange Specific Methods + ########################### + + def exchange_information(self): + """Return current exchange trading rules and symbol information.""" + return self.request('GET', 'api/v1/exchangeInfo') + + def all_orders(self, currency, **kwargs): + """Return all account orders; active, canceled, or filled.""" + if currency: + kwargs.update({'symbol': currency}) + return self.request('GET', 'api/v3/allOrders', authenticate=True, params=kwargs) + + def trade_history(self, pair, **kwargs): + """Return past trades of the account.""" + if pair: + try: + pair = pair.format_for(self.name).upper() + except AttributeError: + pass + kwargs.update({'symbol': pair}) + return self.request('GET', 'api/v3/myTrades', authenticate=True, params=kwargs) + + def withdraw(self, currency, amount, address, address_tag=None, **kwargs): + """Withdraw currency from the account.""" + kwargs.update({'asset': currency, 'amount': amount, 'address': address}) + if address_tag: + kwargs.update({'addressTag': address_tag}) + return self.request('POST', 'wapi/v3/withdraw.html', authenticate=True, params=kwargs) + + def deposit_history(self, currency=None, **kwargs): + """Return the deposit history of the account.""" + if currency: + kwargs.update({'asset': currency}) + return self.request('GET', 'wapi/v3/depositHistory.html', authenticate=True, params=kwargs) + + def withdraw_history(self, currency=None, **kwargs): + """Return the withdrawal history of the account.""" + if currency: + kwargs.update({'asset': currency}) + return self.request('GET', 'wapi/v3/withdrawHistory.html', authenticate=True, params=kwargs) + + def deposit_address(self, currency, **kwargs): + """Return the deposit address for the given currency.""" + kwargs.update({'asset': currency}) + return self.request('GET', 'wapi/v3/depositAddress.html', authenticate=True, params=kwargs) + + def withdraw_fee(self, currency, **kwargs): + """Return the withdrawal fee for the given currency.""" + kwargs.update({'asset': currency}) + return self.request('GET', 'wapi/v3/withdrawFee.html', authenticate=True, params=kwargs)