def calc_arb_trigger_price(self, mid_prices, longshort='long', value=None): index_exchange, index_symbol = self.indexes.get(self.cur_symbol, [None, None]) diff, is_percentage = self.arb_diffs[self.cur_symbol][longshort] if value is not None: diff, is_percentage = value if diff is None or is_percentage and diff <= -100: return None p_ixc = mid_prices.get((index_exchange, index_symbol)) if p_ixc is None: return None if is_percentage: p_trigger = p_ixc / (1 + diff/100) else: p_trigger = p_ixc - diff xs = self.streamers[self.exchange] min_price = deep_get([xs.markets], [self.cur_symbol, 'limits', 'price', 'min']) price_pcn = deep_get([xs.markets], [self.cur_symbol, 'precision', 'price']) if min_price is None and price_pcn is not None: min_price = pow(10, -price_pcn) if xs.api.precisionMode!=TICK_SIZE else price_pcn if longshort=='short' and min_price and p_trigger < min_price: p_trigger = min_price elif p_trigger <= 0: p_trigger = None return p_trigger
def extract_errors(self, R): """ :type R: Response :returns: list of errors May want to override this method """ r = R.data if isinstance(R, Response) else R key = self.message['error']['key'] if not isinstance(r, dict) or key is None: return [] try: if hasattr(key, '__call__'): msg = key(r) else: msg = deep_get([r], key) e_cls = self.exceptions.get(msg, self.exceptions['__default__']) args = self.create_error_args(r) if not args: args = [msg] errors = [e_cls(*args)] except Exception as e: logger.error('{} - exception occurred while extracting errors: {}. r: {}.' \ .format(self.name, repr(e), r)) logger.exception(e) errors = [] return errors
def auth_satisfied(self, channel): is_private = self.wc.cis.get_value(channel, 'is_private') seq = self.auth_seq(channel) auth_required = deep_get(seq, 'required', return2=None) if (not is_private and auth_required is None) or \ (auth_required is not None and not auth_required): return True via_url = deep_get(seq, 'via_url') if via_url: return True each_time = deep_get(seq, 'each_time') if each_time or self.authenticated: return True #One for authentication subscription, the other for current subscription elif self.free_subscription_slots >= 2: return True return False
def _fetch_exchange_specific(exchange, type, param, param_xc, **kw): exchange, type = _exchange_and_type_to_str(exchange, type) enabled = get_setting(param) enabled_for_xcs = get_setting(param_xc) xc_specific = enabled_for_xcs.get( exchange) if enabled_for_xcs is not None else {} search = [xc_specific, enabled] return deep_get(search, type, **kw)
def clear_setting(param, make_permanent=False): deep_param = [param] if isinstance(param, str) else param D = _d = {} ln = len(deep_param) for i, x in enumerate(deep_param): _d[x] = {} if i < ln-1 else \ deepcopy(deep_get([_SETTINGS_INITIAL], deep_param, return2=DEL)) _d = _d[x] set(D, make_permanent) #, shallow=deep_param)
async def do_auth(out): seq = cnxi.auth_seq(channel, i) auth_required = deep_get(seq, 'is_required', return2=None) takes_input = deep_get(seq, 'takes_input', return2=True) via_url = deep_get(seq, 'via_url') each_time = deep_get(seq, 'each_time') send_separately = deep_get(seq, 'send_separately') set_authenticated = deep_get(seq, 'set_authenticated', return2=True) self.wc.log2('do_auth || i: {i} params: {params}, is_private: {is_private} auth_required: {auth_required} '\ 'via_url: {via_url} each_time: {each_time} send_separately: {send_separately} '\ 'takes_input: {takes_input} set_authenticated: {set_authenticated} cnxi.authenticated: {authenticated}' .format(i=i, params=params, is_private=is_private, auth_required=auth_required, via_url=via_url, each_time=each_time, send_separately=send_separately, takes_input=takes_input, set_authenticated=set_authenticated, authenticated=cnxi.authenticated)) if (is_private and auth_required is None or auth_required) and not via_url: _aInp = [out] if takes_input else [] if each_time: out = await sign(_aInp) elif not cnxi.authenticated: _aOut = await sign(_aInp) if not send_separately: out = _aOut else: await cnx.send(_aOut) if set_authenticated: cnxi.authenticated = True return out
def create_error_args(self, r): """ :returns: arguments for initiating the error May want to override this method """ args_keys = self.message['error']['args_keys'] if args_keys is None: return [] args = [] for key in args_keys: if hasattr(key, '__call__'): args.append(key(r)) else: args.append(deep_get([r], key)) return args
def extract_message_id(self, R): """ :type R: Response May want to override this method """ r = R.data key = self.message['id']['key'] if not isinstance(r, dict) or key is None: return None try: if hasattr(key, '__call__'): return key(r) else: return deep_get([r], key) except Exception as e: logger.error('{} - could not extract id: {}. r: {}.'.format( self.name, e, r)) return None
def get_value(self, _, keywords, default=None, set='channel'): """:param set: 'channel' or 'cnx'""" return deep_get(self.srch_seq(_, set), keywords, return2=default)
def get_auth_value(self, channel, *args, **kw): """Deep get auth value of channel""" return deep_get(self.auth_seq(channel), *args, **kw)
async def send(self, params, wait=False, id=None, cnx=None, sub=None): """ :type params: dict, Request, Subscription :type cnx: Connection :param params: must contain '_' key; also accepts Subscription object :param wait: False : don't wait 'default', True->'default', None, of type int/float: timeout to wait 'return': doesn't wait, but returns the waiter if signalr/socketio is enabled then id system is probably not implementable (leave wait to False). :param id: custom id to be attached to the waiter. in that case user must forward the response to the waiter manually (e.g. through .handle) if channel sends multiple messages, id may be given as [id0, id1, ..] :param cnx: may be specified if params is dict, otherwise cnx of given request will be used :param sub: if subscription, True for subbing, False for unsubbing """ if isinstance(params, dict): params = params.copy() output = self.wc.transform(params) if output is not None: params = output if isinstance(params, dict) and cnx is None: cnx = self.wc.sh.find_available_connection(params, create=True) rq = Request(params, self.wc, cnx) if not isinstance(params, Request) else params #Forward to thread if not self._is_in_thread_loop(): fut = asyncio.Future() await self.send_queue.put((fut, rq, wait, id, cnx, sub)) return await fut params = rq.params cnx = rq.cnx channel = rq.channel cnxi = self.wc.cm.cnx_infs[cnx] send = self.wc.cis.get_value(channel, 'send') drop = self.wc.cis.get_value(channel, 'drop_unused_connection', not send) if not cnx.is_running() and (sub is None or sub): try: cnx.start() except RuntimeError: pass await cnx.wait_till_active(cnx.connect_timeout) elif (sub is not None and not sub) and cnx.url and drop: # Due to param variance difference old connections would not satisfy # the new variance, and would remain unused # if rq.is_merger() and not any(_s.merger is not rq and _s.cnx is cnx # for _s in self.wc.sh.subscriptions): if not any(cnx is _s.cnx for _s in self.wc.sh.subscriptions): self.wc.cm.remove_connection(cnx, True) if cnx.is_running(): await cnx.stop() wait = 'default' if wait is True else wait add_waiter = wait is not False return_waiter = wait in ('return', 'return-waiter', 'return_waiter', 'return waiter') def _return(): if not add_waiter or not return_waiter: return None else: f = asyncio.Future() f.set_result(None) return f if not cnx.url or not send: return _return() packs = self.wc.encode(rq, sub) # WSClient.encode should return `None` if this specific request is not meant to be sent if packs is None: return _return() if not isinstance(packs, Merged): packs = [packs] single = (len(packs) == 1) is_private = self.wc.cis.get_value(channel, 'is_private') auth_seq = cnxi.auth_seq(channel) apply_to_packs = deep_get(auth_seq, 'apply_to_packs') iscoro = asyncio.iscoroutinefunction(self.wc.sign) async def sign(_aInp): if iscoro: return await self.wc.sign(*_aInp) else: return self.wc.sign(*_aInp) async def send_pack(i): pck = packs[i] msg_id = None if isinstance(pck, tuple): out, msg_id = pck else: out = pck if id is not None: try: msg_id = id[i] if not isinstance(id, (str, int)) else id except IndexError: pass if add_waiter and msg_id is None: raise RuntimeError('{} - waiter requested but "message_id" was not ' \ 'provided / returned by .encode(config)'.format(self.wc.name)) async def do_auth(out): seq = cnxi.auth_seq(channel, i) auth_required = deep_get(seq, 'is_required', return2=None) takes_input = deep_get(seq, 'takes_input', return2=True) via_url = deep_get(seq, 'via_url') each_time = deep_get(seq, 'each_time') send_separately = deep_get(seq, 'send_separately') set_authenticated = deep_get(seq, 'set_authenticated', return2=True) self.wc.log2('do_auth || i: {i} params: {params}, is_private: {is_private} auth_required: {auth_required} '\ 'via_url: {via_url} each_time: {each_time} send_separately: {send_separately} '\ 'takes_input: {takes_input} set_authenticated: {set_authenticated} cnxi.authenticated: {authenticated}' .format(i=i, params=params, is_private=is_private, auth_required=auth_required, via_url=via_url, each_time=each_time, send_separately=send_separately, takes_input=takes_input, set_authenticated=set_authenticated, authenticated=cnxi.authenticated)) if (is_private and auth_required is None or auth_required) and not via_url: _aInp = [out] if takes_input else [] if each_time: out = await sign(_aInp) elif not cnxi.authenticated: _aOut = await sign(_aInp) if not send_separately: out = _aOut else: await cnx.send(_aOut) if set_authenticated: cnxi.authenticated = True return out if apply_to_packs is None or i in apply_to_packs: out = await do_auth(out) await cnx.send(out) waiter = self.add_waiter(msg_id) if add_waiter else None if not add_waiter: return None elif return_waiter: return waiter else: _wait = wait if wait != 'default' else \ self.wc.cis.get_value(channel, 'waiter_timeout', set='cnx') try: return await self.wait_on_waiter(waiter, _wait) finally: self.remove_waiter(waiter) r = [] for i in range(len(packs)): r.append(await send_pack(i)) return r[0] if single else r