def __init__(self, address, pairs=None, channels=None, config=None, callbacks=None, book_interval=1000): self.config = {} self.address = address self.book_update_interval = book_interval self.updates = 0 self.do_deltas = False self.pairs = [] self.channels = [] load_exchange_pair_mapping(self.id) if channels is not None and FUNDING in channels and self.id == BITFINEX: if len(channels) > 1: raise ValueError( "Funding channel must be in a separate feedhanlder on Bitfinex or you must use config" ) if config is not None and (pairs is not None or channels is not None): raise ValueError("Use config, or channels and pairs, not both") if config is not None: for channel in config: chan = feed_to_exchange(self.id, channel) self.config[chan] = [ pair_std_to_exchange(pair, self.id) for pair in config[channel] ] if pairs: self.pairs = [ pair_std_to_exchange(pair, self.id) for pair in pairs ] if channels: self.channels = [ feed_to_exchange(self.id, chan) for chan in channels ] self.l3_book = {} self.l2_book = {} self.callbacks = { TRADES: Callback(None), TICKER: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), VOLUME: Callback(None), FUNDING: Callback(None) } if callbacks: for cb_type, cb_func in callbacks.items(): self.callbacks[cb_type] = cb_func if cb_type == BOOK_DELTA: self.do_deltas = True
def __init__(self, address, pairs=None, channels=None, config=None, callbacks=None, book_interval=1000): self.hash = str(uuid.uuid4()) self.uuid = self.id + self.hash self.config = {} self.address = address self.book_update_interval = book_interval self.updates = 0 self.do_deltas = False self.pairs = [] self.channels = [] load_exchange_pair_mapping(self.id) if config is not None and (pairs is not None or channels is not None): raise ValueError("Use config, or channels and pairs, not both") if config is not None: for channel in config: chan = feed_to_exchange(self.id, channel) self.config[chan] = [ pair_std_to_exchange(pair, self.id) for pair in config[channel] ] if pairs: self.pairs = [ pair_std_to_exchange(pair, self.id) for pair in pairs ] if channels: self.channels = [ feed_to_exchange(self.id, chan) for chan in channels ] self.l3_book = {} self.l2_book = {} self.callbacks = { TRADES: Callback(None), TICKER: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), VOLUME: Callback(None), FUNDING: Callback(None), INSTRUMENT: Callback(None) } if callbacks: for cb_type, cb_func in callbacks.items(): self.callbacks[cb_type] = cb_func if cb_type == BOOK_DELTA: self.do_deltas = True for key, callback in self.callbacks.items(): if not isinstance(callback, list): self.callbacks[key] = [callback]
def __init__(self, address, pairs=None, channels=None, config=None, callbacks=None, max_depth=None, book_interval=1000, cross_check=False, origin=None): self.hash = str(uuid.uuid4()) self.uuid = self.id + self.hash self.config = defaultdict(set) self.address = address self.book_update_interval = book_interval self.cross_check = cross_check self.updates = defaultdict(int) self.do_deltas = False self.pairs = [] self.channels = [] self.max_depth = max_depth self.previous_book = defaultdict(dict) self.origin = origin load_exchange_pair_mapping(self.id) if config is not None and (pairs is not None or channels is not None): raise ValueError("Use config, or channels and pairs, not both") if config is not None: for channel in config: chan = feed_to_exchange(self.id, channel) self.config[chan].update([pair_std_to_exchange(pair, self.id) for pair in config[channel]]) if pairs: self.pairs = [pair_std_to_exchange(pair, self.id) for pair in pairs] if channels: self.channels = list(set([feed_to_exchange(self.id, chan) for chan in channels])) self.l3_book = {} self.l2_book = {} self.callbacks = { TICKER_OKS: Callback(None), TICKER_FUTURES: Callback(None),TRADES: Callback(None), TICKER: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), VOLUME: Callback(None), FUNDING: Callback(None), OPEN_INTEREST: Callback(None), LIQUIDATIONS: Callback(None)} if callbacks: for cb_type, cb_func in callbacks.items(): self.callbacks[cb_type] = cb_func if cb_type == BOOK_DELTA: self.do_deltas = True for key, callback in self.callbacks.items(): if not isinstance(callback, list): self.callbacks[key] = [callback]
def _get_orders(self, body): """ https://docs.gdax.com/?python#list-orders """ endpoint = "/orders" if 'status' in body: for status in body['status']: if 'status' not in endpoint: endpoint = '{}?status={}'.format(endpoint, status) else: endpoint = '{}&status{}'.format(endpoint, status) if 'product_id' in body: product_id = pair_std_to_exchange(body['product_id'], self.ID) if 'status' in endpoint: endpoint = '{}&product_id={}'.format(endpoint, product_id) else: endpoint = '{}?product_id={}'.format(endpoint, product_id) endpoint = self._pagination(endpoint, body) header = self._generate_signature(endpoint, "GET") data = self._make_request("GET", endpoint, header) data = list(map(self._trade_normalization, data)) return data
def trades(self, symbol: str, start=None, end=None, retry=None, retry_wait=10): sym = pair_std_to_exchange(symbol, self.ID) params = {'limit_trades': 500} if start: params['since'] = int(pd.Timestamp(start).timestamp() * 1000) if end: end_ts = int(pd.Timestamp(end).timestamp() * 1000) def _trade_normalize(trade): return { 'feed': self.ID, 'order_id': trade['tid'], 'pair': sym, 'side': trade['type'], 'amount': Decimal(trade['amount']), 'price': Decimal(trade['price']), 'timestamp': trade['timestampms'] / 1000.0 } while True: data = reversed(self._get(f"/v1/trades/{sym}?", retry, retry_wait, params=params)) if end: data = [_trade_normalize(d) for d in data if d['timestampms'] <= end_ts] else: data = [_trade_normalize(d) for d in data] yield data if start: params['since'] = int(data[-1]['timestamp'] * 1000) + 1 if len(data) < 500: break if not start and not end: break # GEMINI rate limits to 120 requests a minute sleep(0.5)
def _book(self, symbol: str, retry=0, retry_wait=0): ret = {} symbol = pair_std_to_exchange(symbol, self.ID) ret[symbol] = {BID: sd(), ASK: sd()} @request_retry(self.ID, retry, retry_wait) def helper(): return requests.get( f"{self.api}get_order_book?depth=10000&instrument_name={symbol}" ) while True: r = helper() if r.status_code == 429: sleep(int(r.headers['Retry-After'])) continue elif r.status_code == 500: LOG.warning("%s: 500 for URL %s - %s", self.ID, r.url, r.text) sleep(retry_wait) if retry == 0: break continue elif r.status_code != 200: self._handle_error(r, LOG) data = r.json() break for side, key in ((BID, 'bids'), (ASK, 'asks')): for entry_bid in data["result"][key]: price, amount = entry_bid ret[symbol][side][price] = amount return ret
def place_order(self, symbol: str, side: str, order_type: str, amount: Decimal, price=None, options=None): ot = normalize_trading_options(self.ID, order_type) parameters = { 'pair': pair_std_to_exchange(symbol, self.ID + 'REST'), 'type': 'buy' if side == BUY else 'sell', 'volume': str(amount), 'ordertype': ot } if price is not None: parameters['price'] = str(price) if options: parameters['oflags'] = ','.join( [normalize_trading_options(self.ID, o) for o in options]) data = self._post_private('/private/AddOrder', parameters) if len(data['error']) != 0: return data else: if len(data['result']['txid']) == 1: return self.order_status(data['result']['txid'][0]) else: return [self.order_status(tx) for tx in data['result']['txid']]
def _get_fills(self, symbol=None, retry=None, retry_wait=0, start_date=None, end_date=None): endpoint = '/fills' if symbol is not None: symbol = pair_std_to_exchange(symbol, self.ID) endpoint = '{}?product_id={}'.format(endpoint, symbol) header = self._generate_signature(endpoint, "GET") data = self._make_request("GET", endpoint, header, retry=retry) if data == []: LOG.warning("%s: No data", self.ID) elif start_date is not None and end_date is not None: # filter out data not in specified range data_in_range = [] start_time = pd.Timestamp(start_date).to_pydatetime() end_time = pd.Timestamp(end_date).to_pydatetime() for entry in data: entry_time = dt.strptime(entry['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ") if entry_time >= start_time and entry_time <= end_time: data_in_range.append(entry) data = data_in_range data = list(map(self._trade_normalization, data)) return data
def place_order(self, symbol: str, side: str, order_type: str, amount: Decimal, price=None, options=None): if not price: raise ValueError('Poloniex only supports limit orders, must specify price') # Poloniex only supports limit orders, so check the order type _ = normalize_trading_options(self.ID, order_type) parameters = {} if options: parameters = { normalize_trading_options(self.ID, o): 1 for o in options } parameters['currencyPair'] = pair_std_to_exchange(symbol, self.ID) parameters['amount'] = str(amount) parameters['rate'] = str(price) endpoint = None if side == BUY: endpoint = 'buy' elif side == SELL: endpoint = 'sell' data = self._post(endpoint, parameters) order = self.order_status(data['orderNumber']) if 'error' not in order: if len(data['resultingTrades']) == 0: return order else: return Poloniex._trade_status(data['resultingTrades'], symbol, data['orderNumber'], amount) return data
def __init__(self, address, pairs=None, channels=None, callbacks=None, book_interval=1000): self.address = address self.standardized_pairs = pairs self.standardized_channels = channels self.book_update_interval = book_interval self.updates = 0 self.do_deltas = False if channels is not None and FUNDING in channels and self.id == BITFINEX: if len(channels) > 1: raise ValueError("Funding channel must be in a separate feedhanlder on Bitfinex") if pairs: self.pairs = [pair_std_to_exchange(pair, self.id) for pair in pairs] if channels: self.channels = [feed_to_exchange(self.id, chan) for chan in channels] self.l3_book = {} self.l2_book = {} self.callbacks = {TRADES: Callback(None), TICKER: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), VOLUME: Callback(None), FUNDING: Callback(None)} if callbacks: for cb in callbacks: self.callbacks[cb] = callbacks[cb] if isinstance(callbacks[cb], BookUpdateCallback): self.do_deltas = True
def place_order(self, symbol: str, side: str, order_type: str, amount: Decimal, price=None, options=None): if not price: raise ValueError( 'Poloniex only supports limit orders, must specify price') # Poloniex only supports limit orders, so check the order type _ = normalize_trading_options(self.ID, order_type) parameters = { normalize_trading_options(self.ID, o): 1 for o in options } parameters['currencyPair'] = pair_std_to_exchange(symbol, self.ID) parameters['amount'] = str(amount) parameters['rate'] = str(price) endpoint = None if side == BUY: endpoint = 'buy' elif side == SELL: endpoint = 'sell' return self._post(endpoint, parameters)
def place_order(self, symbol: str, side: str, order_type: str, amount: Decimal, price=None, client_order_id=None, options=None): if not price: raise ValueError( 'Gemini only supports limit orders, must specify price') ot = normalize_trading_options(self.ID, order_type) sym = pair_std_to_exchange(symbol, self.ID) parameters = { 'type': ot, 'symbol': sym, 'side': side, 'amount': str(amount), 'price': str(price), 'options': [normalize_trading_options(self.ID, o) for o in options] if options else [] } if client_order_id: parameters['client_order_id'] = client_order_id data = self._post("/v1/order/new", parameters) return Gemini._order_status(data)
def __init__(self, address, pairs=None, channels=None, callbacks=None): self.address = address self.standardized_pairs = pairs self.standardized_channels = channels if pairs: self.pairs = [ pair_std_to_exchange(pair, self.id) for pair in pairs ] if channels: self.channels = [ feed_to_exchange(self.id, chan) for chan in channels ] self.l3_book = {} self.l2_book = {} self.callbacks = { TRADES: Callback(None), TICKER: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), VOLUME: Callback(None) } if callbacks: for cb in callbacks: self.callbacks[cb] = callbacks[cb]
def _historical_trades(self, symbol, start_date, end_date, retry, retry_wait, freq='6H'): symbol = pair_std_to_exchange(symbol, self.ID + 'REST') @request_retry(self.ID, retry, retry_wait) def helper(start_date): endpoint = f"{self.api}/public/Trades?pair={symbol}&since={start_date}" return requests.get(endpoint) start_date = API._timestamp(start_date).timestamp() * 1000000000 end_date = API._timestamp(end_date).timestamp() * 1000000000 while start_date < end_date: r = helper(start_date) if r.status_code == 504 or r.status_code == 520: # cloudflare gateway timeout or other error time.sleep(60) continue elif r.status_code != 200: self._handle_error(r, LOG) else: time.sleep(RATE_LIMIT_SLEEP) data = r.json() if 'error' in data and data['error']: if data['error'] == ['EAPI:Rate limit exceeded']: time.sleep(5) continue else: raise Exception(f"Error processing URL {r.url}: {data['error']}") yield data start_date = int(data['result']['last'])
def trades(self, symbol, start=None, end=None, retry=None, retry_wait=10): symbol = pair_std_to_exchange(symbol, self.ID) @request_retry(self.ID, retry, retry_wait) def helper(s=None, e=None): data = self._get("returnTradeHistory", {'currencyPair': symbol, 'start': s, 'end': e}) data.reverse() return data if not start: yield map(lambda x: self._trade_normalize(x, symbol), helper()) else: if not end: end = pd.Timestamp.utcnow() start = API._timestamp(start) end = API._timestamp(end) - pd.Timedelta(nanoseconds=1) start = int(start.timestamp()) end = int(end.timestamp()) s = start e = start + 21600 while True: if e > end: e = end yield map(lambda x: self._trade_normalize(x, symbol), helper(s=s, e=e)) s = e e += 21600 if s >= end: break
def trades(self, symbol): symbol = pair_std_to_exchange(symbol, self.ID) """ Parameters: pair = asset pair to get trade data for since = return trade data since given id (optional. exclusive) """ return self._post_public("/public/Trades", {'pair': symbol})
def ticker(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID) data = self._get(f"ticker/{sym}", retry, retry_wait) return {'pair': symbol, 'feed': self.ID, 'bid': Decimal(data[0]), 'ask': Decimal(data[2]) }
def trades(self, symbol, start=None, end=None, retry=None, retry_wait=10): symbol = pair_std_to_exchange(symbol, self.ID).replace("/", "") if start and end: for data in self._historical_trades(symbol, start, end, retry, retry_wait): yield list(map(lambda x: self._trade_normalization(x, symbol), data['result'][next(iter(data['result']))])) else: yield self._post_public("/public/Trades", {'pair': symbol})
def ticker(self, symbol: str, retry=None, retry_wait=10): sym = pair_std_to_exchange(symbol, self.ID) data = self._get("returnTicker", retry=retry, retry_wait=retry_wait) return {'pair': symbol, 'feed': self.ID, 'bid': Decimal(data[sym]['lowestAsk']), 'ask': Decimal(data[sym]['highestBid']) }
def trades(self, symbol: str, start=None, end=None, retry=None, retry_wait=10): symbol = pair_std_to_exchange(symbol, self.ID) for data in self._get_trades(symbol, start, end, retry, retry_wait): yield data
def ticker(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID) data = self._get(f"/markets/{sym}", retry=retry, retry_wait=retry_wait) return {'pair': symbol, 'feed': self.ID, 'bid': data['result']['bid'], 'ask': data['result']['ask'] }
def trade_history(self, symbol: str, start=None, end=None): payload = {'currencyPair': pair_std_to_exchange(symbol, self.ID)} if start: payload['start'] = API._timestamp(start).timestamp() if end: payload['end'] = API._timestamp(end).timestamp() payload['limit'] = 10000 return self._post("returnTradeHistory", payload)
def feed_to_exchange(exchange, feed): if exchange == POLONIEX: if feed not in _feed_to_exchange_map: return pair_std_to_exchange(feed, POLONIEX) ret = _feed_to_exchange_map[feed][exchange] if ret == UNSUPPORTED: LOG.error("{} is not supported on {}".format(feed, exchange)) raise ValueError("{} is not supported on {}".format(feed, exchange)) return ret
def __init__(self, pairs=None, channels=None, callbacks=None, **kwargs): self.channels = None if pairs and len(pairs) == 1: self.pair = pairs[0] super().__init__('wss://bitmax.io/api/public/', pairs=None, channels=None, callbacks=callbacks, **kwargs) self.address += pair_std_to_exchange(self.pair, self.id).replace('/', '-') self.pairs = pairs else: self.pairs = pairs self.config = kwargs.get('config', None) self.callbacks = callbacks
def l2_book(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID) data = self._get("returnOrderBook", {'currencyPair': sym}, retry=retry, retry_wait=retry_wait) return { BID: sd({Decimal(u[0]): Decimal(u[1]) for u in data['bids']}), ASK: sd({Decimal(u[0]): Decimal(u[1]) for u in data['asks']}) }
def ticker(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID + 'REST') data = self._post_public(f"/public/Ticker", payload={'pair': sym}, retry=retry, retry_wait=retry_wait) data = data['result'] for _, val in data.items(): return {'pair': symbol, 'feed': self.ID, 'bid': Decimal(val['b'][0]), 'ask': Decimal(val['a'][0]) }
def l2_book(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID) data = self._get(f"/markets/{sym}/orderbook", {'depth': 100}, retry=retry, retry_wait=retry_wait) return { BID: sd({u[0]: u[1] for u in data['result']['bids']}), ASK: sd({u[0]: u[1] for u in data['result']['asks']}) }
def trades(self, symbol: str, start=None, end=None, retry=None, retry_wait=10): if start: if not end: end = pd.Timestamp.utcnow() for data in self._historical_trades(symbol, start, end, retry, retry_wait): yield list(map(lambda x: self._trade_normalization(x, symbol), data['result'][next(iter(data['result']))])) else: sym = pair_std_to_exchange(symbol, self.ID + 'REST') data = self._post_public("/public/Trades", {'pair': sym}, retry=retry, retry_wait=retry_wait) data = data['result'] data = data[list(data.keys())[0]] yield [self._trade_normalization(d, symbol) for d in data]
def l2_book(self, symbol: str, retry=None, retry_wait=0): sym = pair_std_to_exchange(symbol, self.ID) data = self._get(f"/v1/book/{sym}", retry, retry_wait) return { BID: sd({ Decimal(u['price']): Decimal(u['amount']) for u in data['bids'] }), ASK: sd({ Decimal(u['price']): Decimal(u['amount']) for u in data['asks'] }) }
def trades(self, symbol: str, start=None, end=None, retry=None, retry_wait=10): # funding symbols start with f, eg: fUSD, fBTC, etc if symbol[0] != 'f': symbol = pair_std_to_exchange(symbol, self.ID) if start and end: for data in self._get_trades_hist(symbol, start, end, retry, retry_wait): yield data