diff --git a/piker/brokers/questrade.py b/piker/brokers/questrade.py index cc07b751..746f23df 100644 --- a/piker/brokers/questrade.py +++ b/piker/brokers/questrade.py @@ -4,6 +4,7 @@ Questrade API backend. import time import datetime from functools import partial +import configparser import trio from async_generator import asynccontextmanager @@ -34,7 +35,7 @@ class Client: Provides a high-level api which wraps the underlying endpoint calls. """ - def __init__(self, config: 'configparser.ConfigParser'): + def __init__(self, config: configparser.ConfigParser): self._sess = asks.Session() self.api = _API(self._sess) self._conf = config @@ -43,6 +44,7 @@ class Client: self._reload_config(config) def _reload_config(self, config=None, **kwargs): + log.warn("Reloading access config data") self._conf = config or get_config(**kwargs) self.access_data = dict(self._conf['questrade']) @@ -270,7 +272,7 @@ async def get_client() -> Client: log.debug("Check time to ensure access token is valid") try: await client.api.time() - except Exception as err: + except Exception: # access token is likely no good log.warn(f"Access token {client.access_data['access_token']} seems" f" expired, forcing refresh") @@ -313,11 +315,15 @@ async def quoter(client: Client, tickers: [str]): try: quotes_resp = await client.api.quotes(ids=ids) - except QuestradeError as qterr: + except (QuestradeError, BrokerError) as qterr: if "Access token is invalid" in str(qterr.args[0]): # out-of-process piker may have renewed already client._reload_config() - quotes_resp = await client.api.quotes(ids=ids) + try: + quotes_resp = await client.api.quotes(ids=ids) + except BrokerError as qterr: + if "Access token is invalid" in str(qterr.args[0]): + await client.ensure_access(force_refresh=True) else: raise @@ -340,7 +346,10 @@ async def quoter(client: Client, tickers: [str]): return get_quote -# Questrade key conversion / column order +# Questrade column order / value conversion +# XXX: keys-values in this map define the final column values which will +# be "displayable" but not necessarily used for "data processing" +# (i.e. comparisons for sorting purposes or other calculations). _qt_keys = { 'symbol': 'symbol', # done manually in qtconvert '%': '%', @@ -394,7 +403,7 @@ def format_quote( previous = symbol_data[symbol]['prevDayClosePrice'] change = percent_change(previous, last) share_count = symbol_data[symbol].get('outstandingShares', None) - mktcap = share_count * last if (last and share_count) else 'NA' + mktcap = share_count * last if (last and share_count) else 0 computed = { 'symbol': quote['symbol'], '%': round(change, 3), @@ -417,7 +426,6 @@ def format_quote( new_key, func = new_key display_value = func(value) if value else value - new[new_key] = value displayable[new_key] = display_value diff --git a/piker/ui/watchlist.py b/piker/ui/watchlist.py index 3bef11f1..eb1cb3a1 100644 --- a/piker/ui/watchlist.py +++ b/piker/ui/watchlist.py @@ -287,7 +287,7 @@ class TickerTable(GridLayout): return row def render_rows( - self, pairs: (dict, Row), sort_key: str = None, row_filter=None, + self, pairs: {str: (dict, Row)}, sort_key: str = None, row_filter=None, ): """Sort and render all rows on the ticker grid from ``pairs``. """