Example #1
0
    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
Example #2
0
    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]
Example #3
0
 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)])
Example #4
0
    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]
Example #5
0
    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()
Example #6
0
    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()
Example #7
0
    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
Example #8
0
 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)
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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)
Example #12
0
    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]))
Example #13
0
 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 = {}
Example #14
0
    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
Example #15
0
    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)
Example #16
0
    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)
Example #17
0
 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)
                     }
                 ))
Example #18
0
    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]
Example #19
0
    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]
Example #20
0
    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]
Example #21
0
    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]