def connect( self ) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[ [str, float], None]]]: ret = [] for chan in self.subscription: chan = self.exchange_channel_to_std(chan) if self.is_authenticated_channel(chan): LOG.info(f'{self.id}: {chan} will be authenticated') if chan == ORDER_INFO: ret.append( (WSAsyncConn(self.address['trading'], self.id, **self.ws_defaults), self.subscribe, self.message_handler, self.authenticate)) if chan in [BALANCES, TRANSACTIONS]: ret.append( (WSAsyncConn(self.address['account'], self.id, **self.ws_defaults), self.subscribe, self.message_handler, self.authenticate)) else: ret.append((WSAsyncConn(self.address['market'], self.id, **self.ws_defaults), self.subscribe, self.message_handler, self.authenticate)) return ret
def connect( self ) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[ [str, float], None]]]: """ Generic connection method for exchanges. Exchanges that require/support multiple addresses will need to override this method in their specific class unless they use the same subscribe method and message handler for all connections. Connect returns a list of tuples. Each tuple contains 1. an AsyncConnection object 2. the subscribe function pointer associated with this connection 3. the message handler for this connection """ channels_id = '|'.join(list(self.channels)) symbols_id = '|'.join(list(self.symbols)) conn_id = '-'.join([channels_id, self.id, symbols_id]) ret = [] if isinstance(self.address, str): return [(WSAsyncConn(self.address, conn_id, **self.ws_defaults), self.subscribe, self.message_handler)] for _, addr in self.address.items(): ret.append((WSAsyncConn(addr, conn_id, **self.ws_defaults), self.subscribe, self.message_handler)) return ret
def limit_sub(subscription: dict, limit: int, auth, options: dict): ret = [] sub = {} for channel in subscription: for pair in subscription[channel]: if channel not in sub: sub[channel] = [] sub[channel].append(pair) if sum(map(len, sub.values())) == limit: ret.append((WSAsyncConn(addr, self.id, authentication=auth, subscription=sub, **options), self.subscribe, self.message_handler, self.authenticate)) sub = {} if sum(map(len, sub.values())) > 0: ret.append((WSAsyncConn(addr, self.id, authentication=auth, subscription=sub, **options), self.subscribe, self.message_handler, self.authenticate)) return ret
def connect(self) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[[str, float], None]]]: ret = [] for channel in self.subscription: if is_authenticated_channel(channel): for s in self.subscription[channel]: ret.append((WSAsyncConn(self.address, self.id, **self.ws_defaults), partial(self.user_order_subscribe, symbol=s), self.message_handler, self.authenticate)) else: ret.append((WSAsyncConn(self.address, self.id, **self.ws_defaults), self.subscribe, self.message_handler, self.authenticate)) return ret
def _connect_builder(self, address: str, options: list, header=None, sub=None, handler=None): """ Helper method for building a custom connect tuple """ subscribe = partial(self.subscribe if not sub else sub, options=options) conn = WSAsyncConn(address, self.id, extra_headers=header, **self.ws_defaults) return conn, subscribe, handler if handler else self.message_handler
def connect( self ) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[ [str, float], None]]]: ret = [] if any(pair[-4:] == 'USDT' for pair in self.normalized_symbols): subscribe = partial(self.subscribe, quote='USDT') ret.append((WSAsyncConn(self.address['USDT'], self.id, **self.ws_defaults), subscribe, self.message_handler)) if any(pair[-3:] == 'USD' for pair in self.normalized_symbols): subscribe = partial(self.subscribe, quote='USD') ret.append((WSAsyncConn(self.address['USD'], self.id, **self.ws_defaults), subscribe, self.message_handler)) return ret
def connect(self) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[[str, float], None]]]: """ Generic websocket connection method for exchanges. Uses the websocket endpoints defined in the exchange to determine, based on the subscription information, which endpoints should be used, and what instruments/channels should be enabled on each connection. Connect returns a list of tuples. Each tuple contains 1. an AsyncConnection object 2. the subscribe function pointer associated with this connection 3. the message handler for this connection 4. The authentication method for this connection """ def limit_sub(subscription: dict, limit: int, auth, options: dict): ret = [] sub = {} for channel in subscription: for pair in subscription[channel]: if channel not in sub: sub[channel] = [] sub[channel].append(pair) if sum(map(len, sub.values())) == limit: ret.append((WSAsyncConn(addr, self.id, authentication=auth, subscription=sub, **options), self.subscribe, self.message_handler, self.authenticate)) sub = {} if sum(map(len, sub.values())) > 0: ret.append((WSAsyncConn(addr, self.id, authentication=auth, subscription=sub, **options), self.subscribe, self.message_handler, self.authenticate)) return ret ret = self._connect_rest() for endpoint in self.websocket_endpoints: auth = None if endpoint.authentication: # if a class has an endpoint with the authentication flag set to true, this # method must be define. The method will be called immediately before connecting # to authenticate the connection. _ws_authentication returns a tuple of address and ws options auth = self._ws_authentication limit = endpoint.limit addr = self._address() addr = endpoint.get_address(self.sandbox) if addr is None else addr if not addr: continue # filtering can only be done on normalized symbols, but this subscription needs to have the raw/exchange specific # subscription, so we need to temporarily convert the symbols back and forth. It has to be done here # while in the context of the class temp_sub = {chan: [self.exchange_symbol_to_std_symbol(s) for s in symbols] for chan, symbols in self.subscription.items()} filtered_sub = {chan: [self.std_symbol_to_exchange_symbol(s) for s in symbols] for chan, symbols in endpoint.subscription_filter(temp_sub).items()} count = sum(map(len, filtered_sub.values())) if not filtered_sub or count == 0: continue if limit and count > limit: ret.extend(limit_sub(filtered_sub, limit, auth, endpoint.options)) else: ret.append((WSAsyncConn(addr, self.id, authentication=auth, subscription=filtered_sub, **endpoint.options), self.subscribe, self.message_handler, self.authenticate)) return ret
def connect( self ) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[ [str, float], None]]]: ret = [] if any( self.is_authenticated_channel( self.exchange_channel_to_std(chan)) for chan in self.subscription): ret.append((WSAsyncConn(self.address['private'], self.id, **self.ws_defaults), partial(self.subscribe, private=True), self.message_handler, self.authenticate)) if any(not self.is_authenticated_channel( self.exchange_channel_to_std(chan)) for chan in self.subscription): ret.append( (WSAsyncConn(self.address['public'], self.id, **self.ws_defaults), partial(self.subscribe, private=False), self.message_handler, self.__no_auth)) return ret
def connect(self) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[[str, float], None]]]: # Phemex only allows 5 connections, with 20 subscriptions per connection, so split the subscription into separate # connections if necessary ret = [] sub_pair = [] if self.std_channel_to_exchange(BALANCES) in self.subscription: sub_pair.append([self.std_channel_to_exchange(BALANCES), BALANCES]) for chan, symbols in self.subscription.items(): if self.exchange_channel_to_std(chan) == BALANCES: continue for sym in symbols: sub_pair.append([chan, sym]) if len(sub_pair) == 20: func = partial(self.subscribe, subs=sub_pair) ret.append((WSAsyncConn(self.address, self.id, **self.ws_defaults), func, self.message_handler, self.authenticate)) sub_pair = [] if len(sub_pair) > 0: func = partial(self.subscribe, subs=sub_pair) ret.append((WSAsyncConn(self.address, self.id, **self.ws_defaults), func, self.message_handler, self.authenticate)) return ret
def connect( self ) -> List[Tuple[AsyncConnection, Callable[[None], None], Callable[ [str, float], None]]]: ''' Linear PERPETUAL (USDT) public goes to USDT endpoint Linear PERPETUAL (USDT) private goes to USDTP endpoint Inverse PERPETUAL and FUTURES (USD) both private and public goes to USD endpoint ''' ret = [] if any( pair.split('-')[1] == 'USDT' for pair in self.normalized_symbols): if any( self.is_authenticated_channel( self.exchange_channel_to_std(chan)) for chan in self.subscription): ret.append((WSAsyncConn(self.address['USDTP'], self.id, **self.ws_defaults), partial(self.subscribe, quote='USDTP'), self.message_handler, self.authenticate)) if any(not self.is_authenticated_channel( self.exchange_channel_to_std(chan)) for chan in self.subscription): ret.append((WSAsyncConn(self.address['USDT'], self.id, **self.ws_defaults), partial(self.subscribe, quote='USDT'), self.message_handler, self.__no_auth)) if any( pair.split('-')[1] == 'USD' for pair in self.normalized_symbols): ret.append( (WSAsyncConn(self.address['USD'], self.id, **self.ws_defaults), partial(self.subscribe, quote='USD'), self.message_handler, self.authenticate)) return ret
def build(options: list): subscribe = partial(self.subscribe, options=options) conn = WSAsyncConn(self.address, self.id, **self.ws_defaults) return conn, subscribe, self.message_handler