Source code for exx.client

# coding=utf-8

import hashlib
import hmac
import time
import requests

from operator import itemgetter

from .exceptions import ExxAPIException, ExxRequestException


[docs]class Client(object): API_URL = 'https://api.exx.com/data/v1' PRIVATE_URL = 'https://trade.exx.com/api' SIDE_BUY = 'buy' SIDE_SELL = 'sell'
[docs] def __init__(self, api_key, api_secret): """Exx API Client constructor https://www.exx.com/help/restApi :param api_key: Api Token Id :type api_key: string :param api_secret: Api Secret :type api_secret: string .. code:: python client = Client(api_key, api_secret) """ self.API_KEY = api_key self.API_SECRET = api_secret self.session = self._init_session()
def _init_session(self): session = requests.session() headers = {'Accept': 'application/json', 'User-Agent': 'python-exx'} session.headers.update(headers) return session def _ordered_query_string(self, data): """Convert params to ordered query string :param data: :return: """ params = [] for key, value in data.items(): params.append((key, value)) # sort parameters by key params.sort(key=itemgetter(0)) return '&'.join(["{}={}".format(d[0], d[1]) for d in params]) def _generate_signature(self, query_string): """Generate the call signature :param path: :param data: :param nonce: :return: signature string """ m = hmac.new(self.API_SECRET.encode('utf-8'), query_string.encode('utf-8'), hashlib.sha512) return m.hexdigest() def _create_uri(self, path, private=False): url = self.API_URL if private: url = self.PRIVATE_URL return '{}/{}'.format(url, path) def _request(self, method, path, signed, **kwargs): kwargs['data'] = kwargs.get('data', {}) uri = self._create_uri(path, signed) query_string = '' if signed: # generate signature kwargs['data']['nonce'] = int(time.time() * 1000) kwargs['data']['accesskey'] = self.API_KEY if len(kwargs['data'].keys()): query_string = self._ordered_query_string(kwargs['data']) if signed: query_string += '&signature={}'.format(self._generate_signature(query_string)) del(kwargs['data']) uri += '?{}'.format(query_string) response = getattr(self.session, method)(uri, timeout=10, **kwargs) return self._handle_response(response) def _handle_response(self, response): """Internal helper for handling API responses from the Quoine server. Raises the appropriate exceptions when necessary; otherwise, returns the response. """ if not str(response.status_code).startswith('2'): raise ExxAPIException(response) try: json = response.json() if 'error' in json: raise ExxAPIException(response) if 'code' in json and json['code'] != 100: raise ExxAPIException(response) return json except ValueError: raise ExxRequestException('Invalid Response: %s' % response.text) def _get(self, path, signed=False, **kwargs): return self._request('get', path, signed, **kwargs) # Market Endpoints
[docs] def get_markets(self): """Get a list of markets .. code:: python markets = client.get_markets() :returns: dict of dicts .. code-block:: python { "eos_btc":{ "amountScale":2, "priceScale":6, "maxLevels":0, "isOpen":false }, "etc_hsr":{ "amountScale":3, "priceScale":3, "maxLevels":0, "isOpen":true }, } :raises: ExxResponseException, ExxAPIException """ return self._get('markets')
# Ticker Endpoints
[docs] def get_tickers(self): """Get all price tickers .. code:: python markets = client.get_markets() :returns: dict of dicts .. code-block:: python { "bts_btc":{ "vol":0.0, "last":0, "sell":0.0, "buy":0.0, "weekRiseRate":0.0, "riseRate":0.0, "high":0.0, "low":0, "monthRiseRate":0.0 }, } :raises: ExxResponseException, ExxAPIException """ return self._get('tickers')
[docs] def get_ticker(self, symbol): """Get symbol price ticker :param symbol: required e.g eth_hsr :type symbol: str .. code:: python markets = client.get_ticker('eth_hsr') :returns: dict .. code-block:: python { "ticker": { "vol": "1447.851", "last": "30.487000000", "sell": "30.499", "buy": "30.487", "weekRiseRate": -1.17, "riseRate": 9.45, "high": "30.812", "low": "27.855", "monthRiseRate": -0.99 }, "date": "1510383406453" } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol } return self._get('ticker', False, data=data)
# Order book endpoint
[docs] def get_order_book(self, symbol): """Get the bid and asks for the symbol :param symbol: required e.g eth_hsr :type symbol: str .. code:: python markets = client.get_order_book('eth_hsr') :returns: dict .. code-block:: python { "asks": [ [ "32.831", # price "0.083" # quantity ]... ], "bids": [ [ "30.434", # price "10.766" # quantity ]... ], "timestamp" : Timestamp } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol } return self._get('depth', False, data=data)
[docs] def get_market_trades(self, symbol): """Get the trades for the symbol :param symbol: required e.g eth_hsr :type symbol: str .. code:: python trades = client.get_market_trades('eth_hsr') :returns: list of dicts .. code-block:: python [ { "amount" : 0.933, "price" : 31.595, "tid" : 2583932, # Trade ID "type" : "sell", # Trade type "date" : 2583932, "trade_type" : "ask", # Order type }, ... ] :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol } return self._get('trades', False, data=data)
# Order endpoints
[docs] def create_order(self, symbol, order_type, price, amount): """Cancel an order :param symbol: e.g eth_hsr :type symbol: str :param order_type: type buy or sell :type symbol: str :param price: price to trade at :type symbol: str :param amount: amount to trade :type symbol: str .. code:: python order = client.create_order('eth_hsr', 'buy', '0.0012', '1023.2') :returns: dict .. code-block:: python { "code": 100, "message": "操作成功", "id": "13877" } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol, 'type': order_type, 'price': price, 'amount': amount } return self._get('order', True, data=data)
[docs] def cancel_order(self, symbol, order_id): """Cancel an order :param symbol: required e.g eth_hsr :type symbol: str :param order_id: required e.g 123456789 :type symbol: int .. code:: python res = client.cancel_order('eth_hsr', 123456789) :returns: dict .. code-block:: python { "code": "100", "message": "操作成功。" } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol, 'id': order_id } return self._get('cancel', True, data=data)
[docs] def get_order(self, symbol, order_id): """Cancel an order :param symbol: required e.g eth_hsr :type symbol: str :param order_id: required e.g 123456789 :type symbol: int .. code:: python order = client.get_order('eth_hsr', 123456789) :returns: dict .. code-block:: python { "fees": 0, "total_amount": 1, "trade_amount": 0, "price": 31, "currency": “eth_hsr", "id": "13877", "trade_money": 0, "type": "buy", "trade_date": 1509728383300, "status": 0 } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol, 'id': order_id } return self._get('getOrder', True, data=data)
[docs] def get_open_orders(self, symbol, order_type=None, page=1): """Get a list of open buy or sell orders, 10 at a time :param symbol: e.g eth_hsr :type symbol: str :param order_type: optional - type buy or sell :type symbol: str :param page: page index starting at 1 :type page: int .. code:: python # get first page of open orders orders = client.get_open_orders('hsr_eth') # get first page of buy orders orders = client.get_open_orders('hsr_eth', 'buy') # second page of sell orders orders = client.get_open_orders('hsr_eth', 'sell', 2) :returns: list of dicts .. code-block:: python { "fees": 0, "total_amount": 1, "trade_amount": 0, "price": 31, "currency": “eth_hsr", "id": "13877", "trade_money": 0, "type": "buy", "trade_date": 1509728383300, "status": 0 } :raises: ExxResponseException, ExxAPIException """ data = { 'currency': symbol, 'type': order_type, 'pageIndex': page } if order_type: data['type'] = order_type try: return self._get('getOpenOrders', True, data=data) except ExxAPIException as e: if e.code == 308: return [] else: raise e
[docs] def get_balance(self): """Get your balance .. code:: python orders = client.get_balance() :returns: dict .. code-block:: python { "credits": [ { "flatRatio": "0.1", "userRatio": "0.0985", "noticeRatio": "0.2", "levels": 10, "flatPrice": 11.01471399 } ], "funds": { "BTS": { "total": "10", "freeze": "0", "balance": "10", "propTag": "BTS", "credit_quota": "121.938066", "credit_borrowed": "0", "credit_interest": "0" }, "MONA": { "total": "0.966033", "freeze": "0.966033", "balance": "0", "propTag": "MONA", "credit_quota": "0", "credit_borrowed": "0", "credit_interest": "0" }, .... "ETH": { "total": "10", "freeze": "0", "balance": "10", "propTag": "ETH", "credit_quota": "121.938066", "credit_borrowed": "0", "credit_interest": "0" }, "LTC": { "total": "0", "freeze": "0", "balance": "0", "propTag": "LTC", "credit_quota": "121.938066", "credit_borrowed": "0", "credit_interest": "0" }, "QTUM": { "total": "0.003", "freeze": "0.003", "balance": "0", "propTag": "QTUM", "credit_quota": "0", "credit_borrowed": "9.65", "credit_interest": "0.026252" } } } :raises: ExxResponseException, ExxAPIException """ return self._get('getBalance', True)