Display a message when no contracts exist

kivy_mainline_and_py3.8
Tyler Goodlet 2019-01-01 23:42:49 -05:00
parent 0cffa4b97a
commit 4895690642
1 changed files with 109 additions and 91 deletions

View File

@ -37,6 +37,7 @@ class StrikeCell(HeaderCell):
_no_display = ['symbol', 'contract_type', 'strike', 'time', 'open'] _no_display = ['symbol', 'contract_type', 'strike', 'time', 'open']
_strike_row_cache = {} _strike_row_cache = {}
_strike_cell_cache = {} _strike_cell_cache = {}
_no_contracts_msg = "No contracts available for symbol"
class StrikeRow(BoxLayout): class StrikeRow(BoxLayout):
@ -237,14 +238,22 @@ class OptionChain(object):
self._nursery = None self._nursery = None
self._update_nursery = None self._update_nursery = None
self.feed = feed self.feed = feed
self._update_cs = None
self._quote_gen = None self._quote_gen = None
# TODO: this should be moved down to the data feed layer # TODO: this should be moved down to the data feed layer
# right now it's only needed for the UI uupdate loop to cancel itself # right now it's only needed for the UI update loop to cancel itself
self._update_cs = None
self._first_quotes = None self._first_quotes = None
self._last_expiry = None self._last_expiry = None
# flag to determine if one-time widgets have been generated # flag to determine if one-time widgets have been generated
self._static_widgets_initialized = False self._static_widgets_initialized = False
self._no_opts_label = None
@property
def no_opts_label(self):
if self._no_opts_label is None:
label = self._no_opts_label = Label(text=_no_contracts_msg)
label.font_size = 30
return self._no_opts_label
async def _rx_symbols(self): async def _rx_symbols(self):
async with find_local_monitor() as portal: async with find_local_monitor() as portal:
@ -319,95 +328,6 @@ class OptionChain(object):
log.debug("Finished rendering rows!") log.debug("Finished rendering rows!")
async def _start_displaying(self, symbol, expiry=None):
"""Main routine to start displaying the real time updated strike
table.
Clear any existing data feed subscription that is no longer needed
(eg. when clicking a new expiry button) spin up a new subscription,
populate the table and start updating it.
"""
# redraw any symbol specific UI components
if self.symbol != symbol or expiry is None:
# set window title
self.widgets['window'].set_title(
self._title.format(symbol=symbol)
)
# retreive all contracts to populate expiry row
all_contracts = await contracts(self.feed.brokermod, symbol)
# start streaming soonest contract by default if not provided
expiry = next(iter(all_contracts)).expiry if not expiry else expiry
# TODO: figure out how to compact these buttons
expiries = {
key.expiry: key.expiry[:key.expiry.find('T')]
for key in all_contracts
}
expiry_row = self.widgets['expiry_row']
expiry_row.clear_widgets()
for expiry, justdate in expiries.items():
button = ExpiryButton(text=str(justdate), key=expiry)
# assign us to each expiry button
button.chain = self
expiry_row.add_widget(button)
if self.widgets.get('table'):
self.clear_strikes()
if self._update_cs:
log.warn("Cancelling existing update task")
self._update_cs.cancel()
await trio.sleep(0)
if self._quote_gen:
await self._quote_gen.aclose()
if self._nursery is None:
raise RuntimeError(
"You must call open this chain's update scope first!")
log.debug(f"Waiting on first_quotes for {symbol}:{expiry}")
self._quote_gen, first_quotes = await self.feed.open_stream(
[(symbol, expiry)]
)
log.debug(f"Got first_quotes for {symbol}:{expiry}")
records, displayables = self.feed.format_quotes(first_quotes)
# draw static widgets only once
if self._static_widgets_initialized is False:
self._init_static_widgets(displayables)
self._static_widgets_initialized = True
n = self._nursery
self.render_rows(records, displayables)
with trio.open_cancel_scope() as cs:
self._update_cs = cs
await n.start(
partial(
update_quotes,
n,
self.feed.brokermod.format_option_quote,
self.widgets,
self._quote_gen,
symbol_data={},
first_quotes=first_quotes,
)
)
self.symbol, self.expiry = symbol, expiry
def start_displaying(self, symbol, expiry):
if self.symbol == symbol and self.expiry == expiry:
log.info(f"Clicked {symbol}:{expiry} is already selected")
return
log.info(f"Subscribing for {symbol}:{expiry}")
self._nursery.start_soon(
partial(self._start_displaying, symbol, expiry=expiry)
)
def _init_static_widgets(self, displayables): def _init_static_widgets(self, displayables):
assert self._static_widgets_initialized is False assert self._static_widgets_initialized is False
container = self.widgets['container'] container = self.widgets['container']
@ -469,6 +389,104 @@ class OptionChain(object):
'pager': pager, 'pager': pager,
}) })
async def _start_displaying(self, symbol, expiry=None):
"""Main routine to start displaying the real time updated strike
table.
Clear any existing data feed subscription that is no longer needed
(eg. when clicking a new expiry button) spin up a new subscription,
populate the table and start updating it.
"""
table = self.widgets.get('table')
if table:
self.clear_strikes()
if self._update_cs:
log.warn("Cancelling existing update task")
self._update_cs.cancel()
await trio.sleep(0)
if self._quote_gen:
await self._quote_gen.aclose()
# redraw any symbol specific UI components
if self.symbol != symbol or expiry is None:
# set window title
self.widgets['window'].set_title(
self._title.format(symbol=symbol)
)
# retreive all contracts to populate expiry row
all_contracts = await contracts(self.feed.brokermod, symbol)
if not all_contracts:
label = self.no_opts_label
label.symbol = symbol
if table:
table.add_widget(label)
return
# start streaming soonest contract by default if not provided
expiry = next(iter(all_contracts)).expiry if not expiry else expiry
# TODO: figure out how to compact these buttons
expiries = {
key.expiry: key.expiry[:key.expiry.find('T')]
for key in all_contracts
}
expiry_row = self.widgets['expiry_row']
expiry_row.clear_widgets()
for expiry, justdate in expiries.items():
button = ExpiryButton(text=str(justdate), key=expiry)
# assign us to each expiry button
button.chain = self
expiry_row.add_widget(button)
if self._nursery is None:
raise RuntimeError(
"You must call open this chain's update scope first!")
log.debug(f"Waiting on first_quotes for {symbol}:{expiry}")
self._quote_gen, first_quotes = await self.feed.open_stream(
[(symbol, expiry)]
)
log.debug(f"Got first_quotes for {symbol}:{expiry}")
records, displayables = self.feed.format_quotes(first_quotes)
# draw static widgets only once
if self._static_widgets_initialized is False:
self._init_static_widgets(displayables)
self._static_widgets_initialized = True
self.render_rows(records, displayables)
with trio.open_cancel_scope() as cs:
self._update_cs = cs
await self._nursery.start(
partial(
update_quotes,
self._nursery,
self.feed.brokermod.format_option_quote,
self.widgets,
self._quote_gen,
symbol_data={},
first_quotes=first_quotes,
)
)
# always keep track of current subscription
self.symbol, self.expiry = symbol, expiry
def start_displaying(self, symbol, expiry):
if self.symbol == symbol and self.expiry == expiry:
log.info(f"Clicked {symbol}:{expiry} is already selected")
return
log.info(f"Subscribing for {symbol}:{expiry}")
self._nursery.start_soon(
partial(self._start_displaying, symbol, expiry=expiry)
)
async def new_chain_ui( async def new_chain_ui(
portal: tractor._portal.Portal, portal: tractor._portal.Portal,