def __getattr__(self, attr): exch = self.lookup[attr.lower()] if not exch.mapped: try: load_exchange_symbol_mapping(exch.ID + 'REST') except KeyError: load_exchange_symbol_mapping(exch.ID) exch.mapped = True return exch
def __getitem__(self, key): if not self.mapped: try: load_exchange_symbol_mapping(self.ID + 'REST') except KeyError: load_exchange_symbol_mapping(self.ID) self.mapped = True if key == 'trades': return self.trades elif key == 'funding': return self.funding elif key == 'l2_book': return self.l2_book elif key == 'l3_book': return self.l3_book elif key == 'ticker': return self.ticker
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 test_blockchain_symbol_conversions(): load_exchange_symbol_mapping(BLOCKCHAIN) for _, symbol in blockchain_symbols().items(): assert symbol_exchange_to_std(symbol) == symbol_std_to_exchange( symbol, BLOCKCHAIN)
def test_bitcoincom_symbol_conversions(): load_exchange_symbol_mapping(BITCOINCOM) for _, symbol in bitcoincom_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, BITCOINCOM)
def test_bitstamp_symbol_conversions(): load_exchange_symbol_mapping(BITSTAMP) for _, symbol in bitstamp_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, BITSTAMP)
def test_gemini_symbol_conversions(): load_exchange_symbol_mapping(GEMINI) for normalized, symbol in gemini_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, GEMINI) assert normalized == std
def test_hitbtc_symbol_conversions(): load_exchange_symbol_mapping(HITBTC) for _, symbol in hitbtc_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, HITBTC)
def test_bitfinex_symbol_conversions(): load_exchange_symbol_mapping(BITFINEX) for _, symbol in bitfinex_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, BITFINEX)
def test_poloniex_symbol_conversions(): load_exchange_symbol_mapping(POLONIEX) for _, symbol in poloniex_symbols().items(): std = symbol_exchange_to_std(symbol) assert symbol == symbol_std_to_exchange(std, POLONIEX)
def test_coinbase_symbol_conversions(): load_exchange_symbol_mapping(COINBASE) for _, symbol in coinbase_symbols().items(): assert symbol_exchange_to_std(symbol) == symbol_std_to_exchange( symbol, COINBASE)
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]