commit
73b153b6fd
|
@ -4,6 +4,7 @@ Questrade API backend.
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import configparser
|
||||||
|
|
||||||
import trio
|
import trio
|
||||||
from async_generator import asynccontextmanager
|
from async_generator import asynccontextmanager
|
||||||
|
@ -34,7 +35,7 @@ class Client:
|
||||||
|
|
||||||
Provides a high-level api which wraps the underlying endpoint calls.
|
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._sess = asks.Session()
|
||||||
self.api = _API(self._sess)
|
self.api = _API(self._sess)
|
||||||
self._conf = config
|
self._conf = config
|
||||||
|
@ -43,6 +44,7 @@ class Client:
|
||||||
self._reload_config(config)
|
self._reload_config(config)
|
||||||
|
|
||||||
def _reload_config(self, config=None, **kwargs):
|
def _reload_config(self, config=None, **kwargs):
|
||||||
|
log.warn("Reloading access config data")
|
||||||
self._conf = config or get_config(**kwargs)
|
self._conf = config or get_config(**kwargs)
|
||||||
self.access_data = dict(self._conf['questrade'])
|
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")
|
log.debug("Check time to ensure access token is valid")
|
||||||
try:
|
try:
|
||||||
await client.api.time()
|
await client.api.time()
|
||||||
except Exception as err:
|
except Exception:
|
||||||
# access token is likely no good
|
# access token is likely no good
|
||||||
log.warn(f"Access token {client.access_data['access_token']} seems"
|
log.warn(f"Access token {client.access_data['access_token']} seems"
|
||||||
f" expired, forcing refresh")
|
f" expired, forcing refresh")
|
||||||
|
@ -313,11 +315,15 @@ async def quoter(client: Client, tickers: [str]):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quotes_resp = await client.api.quotes(ids=ids)
|
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]):
|
if "Access token is invalid" in str(qterr.args[0]):
|
||||||
# out-of-process piker may have renewed already
|
# out-of-process piker may have renewed already
|
||||||
client._reload_config()
|
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:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -340,7 +346,10 @@ async def quoter(client: Client, tickers: [str]):
|
||||||
return get_quote
|
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 = {
|
_qt_keys = {
|
||||||
'symbol': 'symbol', # done manually in qtconvert
|
'symbol': 'symbol', # done manually in qtconvert
|
||||||
'%': '%',
|
'%': '%',
|
||||||
|
@ -394,7 +403,7 @@ def format_quote(
|
||||||
previous = symbol_data[symbol]['prevDayClosePrice']
|
previous = symbol_data[symbol]['prevDayClosePrice']
|
||||||
change = percent_change(previous, last)
|
change = percent_change(previous, last)
|
||||||
share_count = symbol_data[symbol].get('outstandingShares', None)
|
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 = {
|
computed = {
|
||||||
'symbol': quote['symbol'],
|
'symbol': quote['symbol'],
|
||||||
'%': round(change, 3),
|
'%': round(change, 3),
|
||||||
|
@ -417,7 +426,6 @@ def format_quote(
|
||||||
new_key, func = new_key
|
new_key, func = new_key
|
||||||
display_value = func(value) if value else value
|
display_value = func(value) if value else value
|
||||||
|
|
||||||
|
|
||||||
new[new_key] = value
|
new[new_key] = value
|
||||||
displayable[new_key] = display_value
|
displayable[new_key] = display_value
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,7 @@ class TickerTable(GridLayout):
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def render_rows(
|
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``.
|
"""Sort and render all rows on the ticker grid from ``pairs``.
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue