X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=ccxt_wrapper.py;h=aaccc612ba036ff92123d377aed2ff208c02725d;hb=51bc7cdec15d093272c259e793a9c691775b5194;hp=d37c306882aaae72fa7c56470eada65f77ef5fa1;hpb=3f41520799b97b3c440a6daeba684a6202e5a678;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/ccxt_wrapper.py b/ccxt_wrapper.py index d37c306..aaccc61 100644 --- a/ccxt_wrapper.py +++ b/ccxt_wrapper.py @@ -1,16 +1,78 @@ from ccxt import * import decimal import time +from retry.api import retry_call +import re +from requests.exceptions import RequestException +from ssl import SSLError def _cw_exchange_sum(self, *args): return sum([arg for arg in args if isinstance(arg, (float, int, decimal.Decimal))]) Exchange.sum = _cw_exchange_sum class poloniexE(poloniex): + RETRIABLE_CALLS = [ + re.compile(r"^return"), + re.compile(r"^cancel"), + re.compile(r"^closeMarginPosition$"), + re.compile(r"^getMarginPosition$"), + ] + + def request(self, path, api='public', method='GET', params={}, headers=None, body=None): + """ + Wrapped to allow retry of non-posting requests" + """ + + origin_request = super().request + kwargs = { + "api": api, + "method": method, + "params": params, + "headers": headers, + "body": body + } + + retriable = any(re.match(call, path) for call in self.RETRIABLE_CALLS) + if api == "public" or method == "GET" or retriable: + return retry_call(origin_request, fargs=[path], fkwargs=kwargs, + tries=10, delay=1, exceptions=(RequestTimeout, InvalidNonce)) + else: + return origin_request(path, **kwargs) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # For requests logging + self.session.origin_request = self.session.request + self.session._parent = self + + def request_wrap(self, *args, **kwargs): + kwargs["headers"]["X-market-id"] = str(self._parent._market.market_id) + kwargs["headers"]["X-user-id"] = str(self._parent._market.user_id) + try: + r = self.origin_request(*args, **kwargs) + self._parent._market.report.log_http_request(args[0], + args[1], kwargs["data"], kwargs["headers"], r) + return r + except (SSLError, RequestException) as e: + self._parent._market.report.log_http_request(args[0], + args[1], kwargs["data"], kwargs["headers"], e) + raise e + + self.session.request = request_wrap.__get__(self.session, + self.session.__class__) + @staticmethod def nanoseconds(): return int(time.time() * 1000000000) + def is_dust_trade(self, amount, rate): + if abs(amount) < decimal.Decimal("0.000001"): + return True + if abs(amount * rate) < decimal.Decimal("0.0001"): + return True + return False + def fetch_margin_balance(self): """ portfolio.market.privatePostGetMarginPosition({"currencyPair": "BTC_DASH"}) @@ -177,39 +239,9 @@ class poloniexE(poloniex): return all_balances - def create_exchange_order(self, symbol, type, side, amount, price=None, params={}): - return super(poloniexE, self).create_order(symbol, type, side, amount, price=price, params=params) - - def create_margin_order(self, symbol, type, side, amount, price=None, lending_rate=None, params={}): - if type == 'market': - raise ExchangeError(self.id + ' allows limit orders only') - self.load_markets() - method = 'privatePostMargin' + self.capitalize(side) - market = self.market(symbol) - price = float(price) - amount = float(amount) - if lending_rate is not None: - params = self.extend({"lendingRate": lending_rate}, params) - response = getattr(self, method)(self.extend({ - 'currencyPair': market['id'], - 'rate': self.price_to_precision(symbol, price), - 'amount': self.amount_to_precision(symbol, amount), - }, params)) - timestamp = self.milliseconds() - order = self.parse_order(self.extend({ - 'timestamp': timestamp, - 'status': 'open', - 'type': type, - 'side': side, - 'price': price, - 'amount': amount, - }, response), market) - id = order['id'] - self.orders[id] = order - return self.extend({'info': response}, order) - def order_precision(self, symbol): - return 8 + self.load_markets() + return self.markets[symbol]['precision']['price'] def transfer_balance(self, currency, amount, from_account, to_account): result = self.privatePostTransferBalance({ @@ -267,6 +299,10 @@ class poloniexE(poloniex): "total": decimal.Decimal(summary["totalValue"]), } + def fetch_nth_order_book(self, symbol, action, number): + book = self.fetch_order_book(symbol, limit=number) + return decimal.Decimal(book[action + "s"][-1][0]) + def nonce(self): """ Wrapped to allow nonce with other libraries @@ -327,13 +363,58 @@ class poloniexE(poloniex): def create_order(self, symbol, type, side, amount, price=None, account="exchange", lending_rate=None, params={}): """ - Wrapped to handle margin and exchange accounts + Wrapped to handle margin and exchange accounts, and get decimals """ + if type == 'market': + raise ExchangeError(self.id + ' allows limit orders only') + self.load_markets() if account == "exchange": - return self.create_exchange_order(symbol, type, side, amount, price=price, params=params) + method = 'privatePost' + self.capitalize(side) elif account == "margin": - return self.create_margin_order(symbol, type, side, amount, price=price, lending_rate=lending_rate, params=params) + method = 'privatePostMargin' + self.capitalize(side) + if lending_rate is not None: + params = self.extend({"lendingRate": lending_rate}, params) else: raise NotImplementedError + market = self.market(symbol) + response = getattr(self, method)(self.extend({ + 'currencyPair': market['id'], + 'rate': self.price_to_precision(symbol, price), + 'amount': self.amount_to_precision(symbol, amount), + }, params)) + timestamp = self.milliseconds() + order = self.parse_order(self.extend({ + 'timestamp': timestamp, + 'status': 'open', + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + }, response), market) + id = order['id'] + self.orders[id] = order + return self.extend({'info': response}, order) + def price_to_precision(self, symbol, price): + """ + Wrapped to avoid float + """ + return ('{:.' + str(self.markets[symbol]['precision']['price']) + 'f}').format(price).rstrip("0").rstrip(".") + def amount_to_precision(self, symbol, amount): + """ + Wrapped to avoid float + """ + return ('{:.' + str(self.markets[symbol]['precision']['amount']) + 'f}').format(amount).rstrip("0").rstrip(".") + + def common_currency_code(self, currency): + """ + Wrapped to avoid the currency translation + """ + return currency + + def currency_id(self, currency): + """ + Wrapped to avoid the currency translation + """ + return currency