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]
async def subscribe(self, websocket): # Binance does not have a separate subscribe message, the # subsription information is included in the # connection endpoint self.__reset() # If full book enabled, collect snapshot first if feed_to_exchange(self.id, L2_BOOK) in self.channels: await self._snapshot(self.pairs) elif feed_to_exchange(self.id, L2_BOOK) in self.config: await self._snapshot(self.config[feed_to_exchange(self.id, L2_BOOK)])
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 __init__(self, symbols=None, channels=None, subscription=None, **kwargs): self.pair_mapping = poloniex_id_symbol_mapping() super().__init__('wss://api2.poloniex.com', symbols=symbols, channels=channels, subscription=subscription, **kwargs) """ Due to the way Poloniex subscriptions work, need to do some ugly manipulation of the subscription/channels,pairs to create a list of channels to subscribe to for Poloniex as well as a callback map that we can use to determine if an update should be delivered to the end client or not """ p_ticker = feed_to_exchange(self.id, TICKER) p_volume = feed_to_exchange(self.id, VOLUME) if channels: self.channels = self.symbols check = channels self.callback_map = { chan: set(symbols) for chan in channels if chan not in {p_ticker, p_volume} } elif subscription: self.channels = [] for c, v in self.subscription.items(): if c not in {p_ticker, p_volume}: self.channels.extend(v) check = subscription self.callback_map = { key: set(value) for key, value in subscription.items() } else: raise ValueError( f'{self.id}: the arguments channels and subscription are empty - cannot subscribe' ) if TICKER in check: self.channels.append(p_ticker) if VOLUME in check: self.channels.append(p_volume) # channels = pairs = cannot have duplicates self.channels = list(set(self.channels)) self.__reset()
def __init__(self, pairs=None, channels=None, callbacks=None, config=None, **kwargs): self.pair_mapping = poloniex_id_pair_mapping() super().__init__('wss://api2.poloniex.com', pairs=pairs, channels=channels, callbacks=callbacks, config=config, **kwargs) """ Due to the way poloniex subcriptions work, need to do some ugly manipulation of the config/channels,pairs to create a list of channels to subscribe to for poloniex as well as a callback map that we can use to determine if an update should be delivered to the end client or not """ p_ticker = feed_to_exchange(self.id, TICKER) p_volume = feed_to_exchange(self.id, VOLUME) if channels: self.channels = self.pairs check = channels self.callback_map = { channel: set(pairs) for channel in channels if channel not in {p_ticker, p_volume} } elif config: self.channels = [] for c, v in self.config.items(): if c not in {p_ticker, p_volume}: self.channels.extend(v) check = config self.callback_map = { key: set(value) for key, value in config.items() } if TICKER in check: self.channels.append(p_ticker) if VOLUME in check: self.channels.append(p_volume) # channels = pairs = cannot have duplicates self.channels = list(set(self.channels)) self.__reset()
def info(cls) -> dict: """ Return information about the Exchange - what trading pairs are supported, what data channels, etc """ pairs, info = get_exchange_info(cls.id) data = {'pairs': list(pairs.keys()), 'channels': []} for channel in (LIQUIDATIONS, OPEN_INTEREST, FUNDING, VOLUME, TICKER, L2_BOOK, L3_BOOK, TRADES, FUTURES_INDEX): try: feed_to_exchange(cls.id, channel, silent=True) data['channels'].append(channel) except UnsupportedDataFeed: pass data.update(info) return data
async def message_handler(self, msg: str, timestamp: float): msg = json.loads(msg, parse_float=Decimal) if 'bts' in msg['event']: if msg['event'] == 'bts:connection_established': pass elif msg['event'] == 'bts:subscription_succeeded': pass else: LOG.warning("%s: Unexpected message %s", self.id, msg) elif msg['event'] == 'trade': await self._trades(msg, timestamp) elif msg['event'] == 'data': if msg['channel'].startswith(feed_to_exchange(self.id, L2_BOOK)): await self._l2_book(msg, timestamp) if msg['channel'].startswith(feed_to_exchange(self.id, L3_BOOK)): await self._l3_book(msg, timestamp) else: LOG.warning("%s: Invalid message type %s", self.id, msg)
def info(cls, key_id: str = None) -> dict: """ Return information about the Exchange - what trading pairs are supported, what data channels, etc key_id: str API key to query the feed, required when requesting supported coins/pairs. """ pairs, info = get_exchange_info(cls.id, key_id=key_id) data = {'pairs': list(pairs.keys()), 'channels': []} for channel in (FUNDING, FUTURES_INDEX, LIQUIDATIONS, L2_BOOK, L3_BOOK, OPEN_INTEREST, MARKET_INFO, TICKER, TRADES, TRANSACTIONS, VOLUME): try: feed_to_exchange(cls.id, channel, silent=True) data['channels'].append(channel) except UnsupportedDataFeed: pass data.update(info) return data
def info(cls) -> dict: """ Return information about the Exchange - what trading symbols are supported, what data channels, etc key_id: str API key to query the feed, required when requesting supported coins/symbols. """ symbols = cls.symbol_mapping() data = Symbols.get(cls.id)[1] data['symbols'] = list(symbols.keys()) data['channels'] = [] for channel in (FUNDING, FUTURES_INDEX, LIQUIDATIONS, L2_BOOK, L3_BOOK, OPEN_INTEREST, MARKET_INFO, TICKER, TRADES, VOLUME, CANDLES): try: feed_to_exchange(cls.id, channel, silent=True) data['channels'].append(channel) except UnsupportedDataFeed: pass return data
def place_order(self, pair: str, side: str, order_type: str, amount: str, price: str, start_time: str, expire_time: str): """ Parameters: pair = asset pair type = type of order (buy/sell) ordertype = order type: market limit (price = limit price) stop-loss (price = stop loss price) take-profit (price = take profit price) stop-loss-profit (price = stop loss price, price2 = take profit price) stop-loss-profit-limit (price = stop loss price, price2 = take profit price) stop-loss-limit (price = stop loss trigger price, price2 = triggered limit price) take-profit-limit (price = take profit trigger price, price2 = triggered limit price) trailing-stop (price = trailing stop offset) trailing-stop-limit (price = trailing stop offset, price2 = triggered limit offset) stop-loss-and-limit (price = stop loss price, price2 = limit price) settle-position price = price (optional. dependent upon ordertype) price2 = secondary price (optional. dependent upon ordertype) volume = order volume in lots leverage = amount of leverage desired (optional. default = none) oflags = comma delimited list of order flags (optional): viqc = volume in quote currency (not available for leveraged orders) fcib = prefer fee in base currency fciq = prefer fee in quote currency nompp = no market price protection post = post only order (available when ordertype = limit) starttm = scheduled start time (optional): 0 = now (default) +<n> = schedule start time <n> seconds from now <n> = unix timestamp of start time expiretm = expiration time (optional): 0 = no expiration (default) +<n> = expire <n> seconds from now <n> = unix timestamp of expiration time userref = user reference id. 32-bit signed number. (optional) validate = validate inputs only. do not submit order (optional) """ ot = feed_to_exchange(self.ID, order_type) parameters = {'pair': pair, 'type': side, 'volume': amount} if price is not None: parameters['price'] = price if start_time is not None: parameters['starttm'] = start_time if expire_time is not None: parameters['expiretm'] = expire_time return self._post_private('/private/AddOrder', parameters)
async def subscribe(self, conn: AsyncConnection, symbol=None): self.__reset(symbol=symbol) for chan in set(self.channels or self.subscription): await conn.send(json.dumps({"type": "subscribe", "product_ids": list(self.symbols or self.subscription[chan]), "channels": [chan] })) chan = feed_to_exchange(self.id, L3_BOOK) if chan in set(self.channels or self.subscription): await self._book_snapshot(list(self.symbols or self.subscription[chan]))
def __reset(self, symbol=None): if symbol: self.seq_no[symbol] = None self.order_map.pop(symbol, None) self.order_type_map.pop(symbol, None) self.l3_book.pop(symbol, None) self.l2_book.pop(symbol, None) else: self.order_map = {} self.order_type_map = {} self.seq_no = None # sequence number validation only works when the FULL data stream is enabled chan = feed_to_exchange(self.id, L3_BOOK) if chan in set(self.channels or self.subscription): pairs = set(self.symbols or self.subscription[chan]) self.seq_no = {pair: None for pair in pairs} self.l3_book = {} self.l2_book = {}
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_type, cb_func in callbacks.items(): self.callbacks[cb_type] = cb_func if cb_type == BOOK_DELTA: self.do_deltas = True
async def update_subscription(self, subscription=None): normalized_subscription = defaultdict(set) for channel in subscription: chan = feed_to_exchange(self.id, channel) if is_authenticated_channel(channel): if not self.key_id or not self.key_secret: raise ValueError("Authenticated channel subscribed to, but no auth keys provided") normalized_subscription[chan].update([self.std_symbol_to_exchange_symbol(symbol, self.id) for symbol in subscription[channel]]) channels_to_subscribe = [] channels_to_unsubscribe = [] for chan in normalized_subscription: for pair in (set(normalized_subscription[chan]) - set(self.subscription[chan])): channels_to_subscribe.append(Deribit.build_channel_name(chan, pair)) for pair in (set(self.subscription[chan]) - set(normalized_subscription[chan])): channels_to_unsubscribe.append(Deribit.build_channel_name(chan, pair)) self.subscription = normalized_subscription LOG.info(f"Updating subscription. Channels to subscribe: {channels_to_subscribe}. Channels to unsubscribe: {channels_to_unsubscribe}") await self.subscribe_inner(self.connection, channels_to_subscribe) await self.unsubscribe_inner(self.connection, channels_to_unsubscribe)
def place_order(self, pair: str, side: str, order_type: str, amount: Decimal, price: Decimal, client_order_id=None, options=None): ot = feed_to_exchange(self.ID, order_type) sym = pair_std_to_exchange(self.ID, pair) parameters = { 'type': ot, 'symbol': sym, 'side': side, 'amount': str(amount), 'price': str(price) } if client_order_id: parameters['client_order_id'] = client_order_id return self._post("/v1/order/new", parameters)
async def subscribe(self, websocket): self.websocket = websocket self.__reset() client_id = 0 for chan in self.channels if self.channels else self.config: for pair in self.pairs if self.pairs else self.config[chan]: pair = pair_exchange_to_std(pair) client_id += 1 if chan==feed_to_exchange(self.id, USER_TRADES): await self.send_auth(websocket) await websocket.send(json.dumps( { "op": "sub", "topic": f'{chan}.{pair}', "cid": str(client_id) } )) else: await websocket.send(json.dumps( { "sub": f"market.{pair}.{chan}", "id": str(client_id) } ))
def __init__(self, address: Union[dict, str], timeout=120, timeout_interval=30, retries=10, symbols=None, channels=None, subscription=None, config: Union[Config, dict, str] = None, callbacks=None, max_depth=None, book_interval=1000, snapshot_interval=False, checksum_validation=False, cross_check=False, origin=None, exceptions=None, log_message_on_error=False, sandbox=False): """ address: str, or dict address to be used to create the connection. The address protocol (wss or https) will be used to determine the connection type. Use a "str" to pass one single address, or a dict of option/address timeout: int Time, in seconds, between message to wait before a feed is considered dead and will be restarted. Set to -1 for infinite. timeout_interval: int Time, in seconds, between timeout checks. retries: int Number of times to retry a failed connection. Set to -1 for infinite max_depth: int Maximum number of levels per side to return in book updates book_interval: int Number of updates between snapshots. Only applicable when book deltas are enabled. Book deltas are enabled by subscribing to the book delta callback. snapshot_interval: bool/int Number of updates between snapshots. Only applicable when book delta is not enabled. Updates between snapshots are not delivered to the client checksum_validation: bool Toggle checksum validation, when supported by an exchange. cross_check: bool Toggle a check for a crossed book. Should not be needed on exchanges that support checksums or provide message sequence numbers. origin: str Passed into websocket connect. Sets the origin header. exceptions: list of exceptions These exceptions will not be handled internally and will be passed to the asyncio exception handler. To handle them feedhandler will need to be supplied with a custom exception handler. See the `run` method on FeedHandler, specifically the `exception_handler` keyword argument. log_message_on_error: bool If an exception is encountered in the connection handler, log the raw message sandbox: bool enable sandbox mode for exchanges that support this """ if isinstance(config, Config): LOG.info( '%s: reuse object Config containing the following main keys: %s', self.id, ", ".join(config.config.keys())) self.config = config else: LOG.info('%s: create Config from type: %r', self.id, type(config)) self.config = Config(config) self.sandbox = sandbox self.log_on_error = log_message_on_error self.retries = retries self.exceptions = exceptions self.connection_handlers = [] self.timeout = timeout self.timeout_interval = timeout_interval self.subscription = defaultdict(set) self.address = address self.book_update_interval = book_interval self.snapshot_interval = snapshot_interval self.cross_check = cross_check self.updates = defaultdict(int) self.do_deltas = False self.normalized_symbols = [] self.max_depth = max_depth self.previous_book = defaultdict(dict) self.origin = origin self.checksum_validation = checksum_validation self.ws_defaults = { 'ping_interval': 10, 'ping_timeout': None, 'max_size': 2**23, 'max_queue': None, 'origin': self.origin } self.key_id = os.environ.get(f'CF_{self.id}_KEY_ID') or self.config[ self.id.lower()].key_id self.key_secret = os.environ.get( f'CF_{self.id}_KEY_SECRET') or self.config[ self.id.lower()].key_secret self._feed_config = defaultdict(list) self.http_conn = HTTPAsyncConn(self.id) symbols_cache = Symbols if not symbols_cache.populated(self.id): self.symbol_mapping() self.normalized_symbol_mapping, self.exchange_info = symbols_cache.get( self.id) self.exchange_symbol_mapping = { value: key for key, value in self.normalized_symbol_mapping.items() } if subscription is not None and (symbols is not None or channels is not None): raise ValueError( "Use subscription, or channels and symbols, not both") if subscription is not None: self.channels = list(subscription.keys()) self.symbols = list(subscription[self.channels[0]]) for channel in subscription: chan = feed_to_exchange(self.id, channel) if is_authenticated_channel(channel): if not self.key_id or not self.key_secret: raise ValueError( "Authenticated channel subscribed to, but no auth keys provided" ) self.normalized_symbols.extend(subscription[channel]) self.subscription[chan].update([ self.std_symbol_to_exchange_symbol(symbol) for symbol in subscription[channel] ]) self._feed_config[channel].extend(self.normalized_symbols) if symbols and channels: if any(is_authenticated_channel(chan) for chan in channels): if not self.key_id or not self.key_secret: raise ValueError( "Authenticated channel subscribed to, but no auth keys provided" ) self.channels = channels self.symbols = symbols # if we dont have a subscription dict, we'll use symbols+channels and build one [ self._feed_config[channel].extend(symbols) for channel in channels ] self.normalized_symbols = symbols symbols = [ self.std_symbol_to_exchange_symbol(symbol) for symbol in symbols ] channels = list( set([feed_to_exchange(self.id, chan) for chan in channels])) self.subscription = {chan: symbols for chan in channels} self._feed_config = dict(self._feed_config) self.l3_book = {} self.l2_book = {} self.callbacks = { FUNDING: Callback(None), FUTURES_INDEX: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), LIQUIDATIONS: Callback(None), OPEN_INTEREST: Callback(None), MARKET_INFO: Callback(None), TICKER: Callback(None), TRADES: Callback(None), CANDLES: Callback(None), ORDER_INFO: 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: Union[dict, str], sandbox=False, symbols=None, channels=None, subscription=None, config: Union[Config, dict, str] = None, callbacks=None, max_depth=None, book_interval=1000, snapshot_interval=False, checksum_validation=False, cross_check=False, origin=None): """ address: str, or dict address to be used to create the connection. The address protocol (wss or https) will be used to determine the connection type. Use a "str" to pass one single address, or a dict of option/address sandbox: bool For authenticated channels, run against the sandbox websocket (when True) max_depth: int Maximum number of levels per side to return in book updates book_interval: int Number of updates between snapshots. Only applicable when book deltas are enabled. Book deltas are enabled by subscribing to the book delta callback. snapshot_interval: bool/int Number of updates between snapshots. Only applicable when book delta is not enabled. Updates between snapshots are not delivered to the client checksum_validation: bool Toggle checksum validation, when supported by an exchange. cross_check: bool Toggle a check for a crossed book. Should not be needed on exchanges that support checksums or provide message sequence numbers. origin: str Passed into websocket connect. Sets the origin header. """ if isinstance(config, Config): LOG.info( '%s: reuse object Config containing the following main keys: %s', self.id, ", ".join(config.config.keys())) self.config = config else: LOG.info('%s: create Config from type: %r', self.id, type(config)) self.config = Config(config) self.subscription = defaultdict(set) self.address = address self.book_update_interval = book_interval self.snapshot_interval = snapshot_interval self.cross_check = cross_check self.updates = defaultdict(int) self.do_deltas = False self.symbols = [] self.normalized_symbols = [] self.channels = [] self.max_depth = max_depth self.previous_book = defaultdict(dict) self.origin = origin self.checksum_validation = checksum_validation self.ws_defaults = { 'ping_interval': 10, 'ping_timeout': None, 'max_size': 2**23, 'max_queue': None, 'origin': self.origin } self.key_id = os.environ.get(f'CF_{self.id}_KEY_ID') or self.config[ self.id.lower()].key_id self.key_secret = os.environ.get( f'CF_{self.id}_KEY_SECRET') or self.config[ self.id.lower()].key_secret self._feed_config = defaultdict(list) load_exchange_symbol_mapping(self.id, key_id=self.key_id) if subscription is not None and (symbols is not None or channels is not None): raise ValueError( "Use subscription, or channels and symbols, not both") if subscription is not None: for channel in subscription: chan = feed_to_exchange(self.id, channel) if is_authenticated_channel(channel): if not self.key_id or not self.key_secret: raise ValueError( "Authenticated channel subscribed to, but no auth keys provided" ) self.normalized_symbols.extend(subscription[channel]) self.subscription[chan].update([ symbol_std_to_exchange(symbol, self.id) for symbol in subscription[channel] ]) self._feed_config[channel].extend(self.normalized_symbols) if symbols: self.normalized_symbols = symbols self.symbols = [ symbol_std_to_exchange(symbol, self.id) for symbol in symbols ] if channels: self.channels = list( set([feed_to_exchange(self.id, chan) for chan in channels])) [ self._feed_config[channel].extend(self.normalized_symbols) for channel in channels ] if any(is_authenticated_channel(chan) for chan in channels): if not self.key_id or not self.key_secret: raise ValueError( "Authenticated channel subscribed to, but no auth keys provided" ) self._feed_config = dict(self._feed_config) self.l3_book = {} self.l2_book = {} self.callbacks = { FUNDING: Callback(None), FUTURES_INDEX: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), LIQUIDATIONS: Callback(None), OPEN_INTEREST: Callback(None), MARKET_INFO: Callback(None), TICKER: Callback(None), TRADES: Callback(None), TRANSACTIONS: Callback(None), VOLUME: Callback(None), CANDLES: Callback(None), ORDER_INFO: 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, snapshot_interval=False, checksum_validation=False, cross_check=False, origin=None): """ max_depth: int Maximum number of levels per side to return in book updates book_interval: int Number of updates between snapshots. Only applicable when book deltas are enabled. Book deltas are enabled by subscribing to the book delta callback. snapshot_interval: bool/int Number of updates between snapshots. Only applicable when book delta is not enabled. Updates between snapshots are not delivered to the client checksum_validation: bool Toggle checksum validation, when supported by an exchange. cross_check: bool Toggle a check for a crossed book. Should not be needed on exchanges that support checksums or provide message sequence numbers. origin: str Passed into websocket connect. Sets the origin header. """ self.hash = str(uuid.uuid4()) self.uuid = f"{self.id}-{self.hash}" self.config = defaultdict(set) self.address = address self.book_update_interval = book_interval self.snapshot_interval = snapshot_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 self.checksum_validation = checksum_validation 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 = { 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), FUTURES_INDEX: 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: Union[dict, str], symbols=None, channels=None, subscription=None, config: Union[Config, dict, str] = None, callbacks=None, max_depth=None, book_interval=1000, snapshot_interval=False, checksum_validation=False, cross_check=False, origin=None): """ max_depth: int Maximum number of levels per side to return in book updates book_interval: int Number of updates between snapshots. Only applicable when book deltas are enabled. Book deltas are enabled by subscribing to the book delta callback. snapshot_interval: bool/int Number of updates between snapshots. Only applicable when book delta is not enabled. Updates between snapshots are not delivered to the client checksum_validation: bool Toggle checksum validation, when supported by an exchange. cross_check: bool Toggle a check for a crossed book. Should not be needed on exchanges that support checksums or provide message sequence numbers. origin: str Passed into websocket connect. Sets the origin header. """ if isinstance(config, Config): self.config = config else: self.config = Config(config) self.subscription = defaultdict(set) self.address = address self.book_update_interval = book_interval self.snapshot_interval = snapshot_interval self.cross_check = cross_check self.updates = defaultdict(int) self.do_deltas = False self.symbols = [] self.normalized_symbols = [] self.channels = [] self.max_depth = max_depth self.previous_book = defaultdict(dict) self.origin = origin self.checksum_validation = checksum_validation self.ws_defaults = { 'ping_interval': 10, 'ping_timeout': None, 'max_size': 2**23, 'max_queue': None, 'origin': self.origin } key_id = self.config[self.id.lower()].key_id load_exchange_symbol_mapping(self.id, key_id=key_id) if subscription is not None and (symbols is not None or channels is not None): raise ValueError( "Use subscription, or channels and symbols, not both") if subscription is not None: for channel in subscription: chan = feed_to_exchange(self.id, channel) self.subscription[chan].update([ symbol_std_to_exchange(symbol, self.id) for symbol in subscription[channel] ]) self.normalized_symbols.extend(self.subscription[chan]) if symbols: self.normalized_symbols = symbols self.symbols = [ symbol_std_to_exchange(symbol, self.id) for symbol in symbols ] if channels: self.channels = list( set([feed_to_exchange(self.id, chan) for chan in channels])) self.l3_book = {} self.l2_book = {} self.callbacks = { FUNDING: Callback(None), FUTURES_INDEX: Callback(None), L2_BOOK: Callback(None), L3_BOOK: Callback(None), LIQUIDATIONS: Callback(None), OPEN_INTEREST: Callback(None), MARKET_INFO: Callback(None), TICKER: Callback(None), TRADES: Callback(None), TRANSACTIONS: Callback(None), VOLUME: 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]