Start input handling **after** order mode is up

asyncify_input_modes
Tyler Goodlet 2021-06-16 08:28:57 -04:00
parent c971997f1a
commit ca23825aff
2 changed files with 115 additions and 93 deletions

View File

@ -158,18 +158,20 @@ class GodWidget(QtGui.QWidget):
# self.toolbar_layout.addWidget(self.strategy_box) # self.toolbar_layout.addWidget(self.strategy_box)
def load_symbol( def load_symbol(
self, self,
providername: str, providername: str,
symbol_key: str, symbol_key: str,
loglevel: str, loglevel: str,
ohlc: bool = True, ohlc: bool = True,
reset: bool = False, reset: bool = False,
) -> None:
"""Load a new contract into the charting app. ) -> trio.Event:
'''Load a new contract into the charting app.
Expects a ``numpy`` structured array containing all the ohlcv fields. Expects a ``numpy`` structured array containing all the ohlcv fields.
""" '''
# our symbol key style is always lower case # our symbol key style is always lower case
symbol_key = symbol_key.lower() symbol_key = symbol_key.lower()
@ -178,6 +180,8 @@ class GodWidget(QtGui.QWidget):
linkedsplits = self.get_chart_symbol(fqsn) linkedsplits = self.get_chart_symbol(fqsn)
order_mode_started = trio.Event()
if not self.vbox.isEmpty(): if not self.vbox.isEmpty():
# XXX: this is CRITICAL especially with pixel buffer caching # XXX: this is CRITICAL especially with pixel buffer caching
self.linkedsplits.hide() self.linkedsplits.hide()
@ -200,10 +204,15 @@ class GodWidget(QtGui.QWidget):
providername, providername,
symbol_key, symbol_key,
loglevel, loglevel,
order_mode_started,
) )
self.set_chart_symbol(fqsn, linkedsplits) self.set_chart_symbol(fqsn, linkedsplits)
else:
# symbol is already loaded and ems ready
order_mode_started.set()
self.vbox.addWidget(linkedsplits) self.vbox.addWidget(linkedsplits)
# chart is already in memory so just focus it # chart is already in memory so just focus it
@ -223,6 +232,8 @@ class GodWidget(QtGui.QWidget):
f'tick:{symbol.tick_size}' f'tick:{symbol.tick_size}'
) )
return order_mode_started
class LinkedSplits(QtGui.QWidget): class LinkedSplits(QtGui.QWidget):
''' '''
@ -1527,6 +1538,8 @@ async def display_symbol_data(
sym: str, sym: str,
loglevel: str, loglevel: str,
order_mode_started: trio.Event,
) -> None: ) -> None:
'''Spawn a real-time displayed and updated chart for provider symbol. '''Spawn a real-time displayed and updated chart for provider symbol.
@ -1623,7 +1636,6 @@ async def display_symbol_data(
}, },
}) })
# load initial fsp chain (otherwise known as "indicators") # load initial fsp chain (otherwise known as "indicators")
n.start_soon( n.start_soon(
spawn_fsps, spawn_fsps,
@ -1644,6 +1656,7 @@ async def display_symbol_data(
wap_in_history, wap_in_history,
) )
# TODO: instead we should start based on instrument trading hours?
# wait for a first quote before we start any update tasks # wait for a first quote before we start any update tasks
# quote = await feed.receive() # quote = await feed.receive()
# log.info(f'Received first quote {quote}') # log.info(f'Received first quote {quote}')
@ -1651,24 +1664,11 @@ async def display_symbol_data(
n.start_soon( n.start_soon(
check_for_new_bars, check_for_new_bars,
feed, feed,
# delay,
ohlcv, ohlcv,
linkedsplits linkedsplits
) )
# interactive testing await start_order_mode(chart, symbol, provider, order_mode_started)
# n.start_soon(
# test_bed,
# ohlcv,
# chart,
# linkedsplits,
# )
# start async input handling for chart's view
# await godwidget._task_stack.enter_async_context(
async with chart._vb.open_async_input_handler():
await start_order_mode(chart, symbol, provider)
async def load_providers( async def load_providers(
@ -1773,7 +1773,7 @@ async def _async_main(
symbol, _, provider = sym.rpartition('.') symbol, _, provider = sym.rpartition('.')
# this internally starts a ``display_symbol_data()`` task above # this internally starts a ``display_symbol_data()`` task above
godwidget.load_symbol(provider, symbol, loglevel) order_mode_ready = godwidget.load_symbol(provider, symbol, loglevel)
# spin up a search engine for the local cached symbol set # spin up a search engine for the local cached symbol set
async with _search.register_symbol_search( async with _search.register_symbol_search(
@ -1791,6 +1791,8 @@ async def _async_main(
# the chart's select cache # the chart's select cache
root_n.start_soon(load_providers, brokernames, loglevel) root_n.start_soon(load_providers, brokernames, loglevel)
await order_mode_ready.wait()
# start handling search bar kb inputs # start handling search bar kb inputs
async with ( async with (

View File

@ -26,8 +26,9 @@ from typing import Optional, Dict, Callable, Any
import uuid import uuid
import pyqtgraph as pg import pyqtgraph as pg
import trio
from pydantic import BaseModel from pydantic import BaseModel
import trio
# from trio_typing import TaskStatus
from ._graphics._lines import LevelLine, position_line from ._graphics._lines import LevelLine, position_line
from ._editors import LineEditor, ArrowEditor, _order_lines from ._editors import LineEditor, ArrowEditor, _order_lines
@ -108,6 +109,10 @@ class OrderMode:
"""Set execution mode. """Set execution mode.
""" """
# not initialized yet
if not self.chart._cursor:
return
self._action = action self._action = action
self.lines.stage_line( self.lines.stage_line(
@ -306,10 +311,14 @@ async def open_order_mode(
async def start_order_mode( async def start_order_mode(
chart: 'ChartPlotWidget', # noqa chart: 'ChartPlotWidget', # noqa
symbol: Symbol, symbol: Symbol,
brokername: str, brokername: str,
# task_status: TaskStatus[trio.Event] = trio.TASK_STATUS_IGNORED,
started: trio.Event,
) -> None: ) -> None:
'''Activate chart-trader order mode loop: '''Activate chart-trader order mode loop:
- connect to emsd - connect to emsd
@ -322,7 +331,11 @@ async def start_order_mode(
# spawn EMS actor-service # spawn EMS actor-service
async with ( async with (
open_ems(brokername, symbol) as (book, trades_stream, positions), open_ems(brokername, symbol) as (book, trades_stream, positions),
open_order_mode(symbol, chart, book) as order_mode open_order_mode(symbol, chart, book) as order_mode,
# # start async input handling for chart's view
# # await godwidget._task_stack.enter_async_context(
# chart._vb.open_async_input_handler(),
): ):
# update any exising positions # update any exising positions
@ -345,83 +358,90 @@ async def start_order_mode(
# Begin order-response streaming # Begin order-response streaming
done() done()
# this is where we receive **back** messages # start async input handling for chart's view
# about executions **from** the EMS actor async with chart._vb.open_async_input_handler():
async for msg in trades_stream:
fmsg = pformat(msg) # signal to top level symbol loading task we're ready
log.info(f'Received order msg:\n{fmsg}') # to handle input since the ems connection is ready
started.set()
name = msg['name'] # this is where we receive **back** messages
if name in ( # about executions **from** the EMS actor
'position', async for msg in trades_stream:
):
# show line label once order is live
order_mode.on_position_update(msg)
continue
resp = msg['resp'] fmsg = pformat(msg)
oid = msg['oid'] log.info(f'Received order msg:\n{fmsg}')
# response to 'action' request (buy/sell) name = msg['name']
if resp in ( if name in (
'dark_submitted', 'position',
'broker_submitted' ):
): # show line label once order is live
order_mode.on_position_update(msg)
# show line label once order is live
order_mode.on_submit(oid)
# resp to 'cancel' request or error condition
# for action request
elif resp in (
'broker_cancelled',
'broker_inactive',
'dark_cancelled'
):
# delete level line from view
order_mode.on_cancel(oid)
elif resp in (
'dark_triggered'
):
log.info(f'Dark order triggered for {fmsg}')
elif resp in (
'alert_triggered'
):
# should only be one "fill" for an alert
# add a triangle and remove the level line
order_mode.on_fill(
oid,
price=msg['trigger_price'],
arrow_index=get_index(time.time())
)
await order_mode.on_exec(oid, msg)
# response to completed 'action' request for buy/sell
elif resp in (
'broker_executed',
):
await order_mode.on_exec(oid, msg)
# each clearing tick is responded individually
elif resp in ('broker_filled',):
known_order = book._sent_orders.get(oid)
if not known_order:
log.warning(f'order {oid} is unknown')
continue continue
action = known_order.action resp = msg['resp']
details = msg['brokerd_msg'] oid = msg['oid']
# TODO: some kinda progress system # response to 'action' request (buy/sell)
order_mode.on_fill( if resp in (
oid, 'dark_submitted',
price=details['price'], 'broker_submitted'
pointing='up' if action == 'buy' else 'down', ):
# TODO: put the actual exchange timestamp # show line label once order is live
arrow_index=get_index(details['broker_time']), order_mode.on_submit(oid)
)
# resp to 'cancel' request or error condition
# for action request
elif resp in (
'broker_cancelled',
'broker_inactive',
'dark_cancelled'
):
# delete level line from view
order_mode.on_cancel(oid)
elif resp in (
'dark_triggered'
):
log.info(f'Dark order triggered for {fmsg}')
elif resp in (
'alert_triggered'
):
# should only be one "fill" for an alert
# add a triangle and remove the level line
order_mode.on_fill(
oid,
price=msg['trigger_price'],
arrow_index=get_index(time.time())
)
await order_mode.on_exec(oid, msg)
# response to completed 'action' request for buy/sell
elif resp in (
'broker_executed',
):
await order_mode.on_exec(oid, msg)
# each clearing tick is responded individually
elif resp in ('broker_filled',):
known_order = book._sent_orders.get(oid)
if not known_order:
log.warning(f'order {oid} is unknown')
continue
action = known_order.action
details = msg['brokerd_msg']
# TODO: some kinda progress system
order_mode.on_fill(
oid,
price=details['price'],
pointing='up' if action == 'buy' else 'down',
# TODO: put the actual exchange timestamp
arrow_index=get_index(details['broker_time']),
)