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

backup_asyncify_input_modes
Tyler Goodlet 2021-06-16 08:28:57 -04:00
parent 29d3ad59dc
commit 8dd5bbf4fa
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)
def load_symbol(
self,
providername: str,
symbol_key: str,
loglevel: str,
ohlc: bool = True,
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.
"""
'''
# our symbol key style is always lower case
symbol_key = symbol_key.lower()
@ -178,6 +180,8 @@ class GodWidget(QtGui.QWidget):
linkedsplits = self.get_chart_symbol(fqsn)
order_mode_started = trio.Event()
if not self.vbox.isEmpty():
# XXX: this is CRITICAL especially with pixel buffer caching
self.linkedsplits.hide()
@ -200,10 +204,15 @@ class GodWidget(QtGui.QWidget):
providername,
symbol_key,
loglevel,
order_mode_started,
)
self.set_chart_symbol(fqsn, linkedsplits)
else:
# symbol is already loaded and ems ready
order_mode_started.set()
self.vbox.addWidget(linkedsplits)
# chart is already in memory so just focus it
@ -223,6 +232,8 @@ class GodWidget(QtGui.QWidget):
f'tick:{symbol.tick_size}'
)
return order_mode_started
class LinkedSplits(QtGui.QWidget):
'''
@ -1527,6 +1538,8 @@ async def display_symbol_data(
sym: str,
loglevel: str,
order_mode_started: trio.Event,
) -> None:
'''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")
n.start_soon(
spawn_fsps,
@ -1644,6 +1656,7 @@ async def display_symbol_data(
wap_in_history,
)
# TODO: instead we should start based on instrument trading hours?
# wait for a first quote before we start any update tasks
# quote = await feed.receive()
# log.info(f'Received first quote {quote}')
@ -1651,24 +1664,11 @@ async def display_symbol_data(
n.start_soon(
check_for_new_bars,
feed,
# delay,
ohlcv,
linkedsplits
)
# interactive testing
# 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)
await start_order_mode(chart, symbol, provider, order_mode_started)
async def load_providers(
@ -1773,7 +1773,7 @@ async def _async_main(
symbol, _, provider = sym.rpartition('.')
# 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
async with _search.register_symbol_search(
@ -1791,6 +1791,8 @@ async def _async_main(
# the chart's select cache
root_n.start_soon(load_providers, brokernames, loglevel)
await order_mode_ready.wait()
# start handling search bar kb inputs
async with (

View File

@ -26,8 +26,9 @@ from typing import Optional, Dict, Callable, Any
import uuid
import pyqtgraph as pg
import trio
from pydantic import BaseModel
import trio
# from trio_typing import TaskStatus
from ._graphics._lines import LevelLine, position_line
from ._editors import LineEditor, ArrowEditor, _order_lines
@ -108,6 +109,10 @@ class OrderMode:
"""Set execution mode.
"""
# not initialized yet
if not self.chart._cursor:
return
self._action = action
self.lines.stage_line(
@ -306,10 +311,14 @@ async def open_order_mode(
async def start_order_mode(
chart: 'ChartPlotWidget', # noqa
symbol: Symbol,
brokername: str,
# task_status: TaskStatus[trio.Event] = trio.TASK_STATUS_IGNORED,
started: trio.Event,
) -> None:
'''Activate chart-trader order mode loop:
- connect to emsd
@ -322,7 +331,11 @@ async def start_order_mode(
# spawn EMS actor-service
async with (
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
@ -345,83 +358,90 @@ async def start_order_mode(
# Begin order-response streaming
done()
# this is where we receive **back** messages
# about executions **from** the EMS actor
async for msg in trades_stream:
# start async input handling for chart's view
async with chart._vb.open_async_input_handler():
fmsg = pformat(msg)
log.info(f'Received order msg:\n{fmsg}')
# signal to top level symbol loading task we're ready
# to handle input since the ems connection is ready
started.set()
name = msg['name']
if name in (
'position',
):
# show line label once order is live
order_mode.on_position_update(msg)
continue
# this is where we receive **back** messages
# about executions **from** the EMS actor
async for msg in trades_stream:
resp = msg['resp']
oid = msg['oid']
fmsg = pformat(msg)
log.info(f'Received order msg:\n{fmsg}')
# response to 'action' request (buy/sell)
if resp in (
'dark_submitted',
'broker_submitted'
):
# 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')
name = msg['name']
if name in (
'position',
):
# show line label once order is live
order_mode.on_position_update(msg)
continue
action = known_order.action
details = msg['brokerd_msg']
resp = msg['resp']
oid = msg['oid']
# TODO: some kinda progress system
order_mode.on_fill(
oid,
price=details['price'],
pointing='up' if action == 'buy' else 'down',
# response to 'action' request (buy/sell)
if resp in (
'dark_submitted',
'broker_submitted'
):
# TODO: put the actual exchange timestamp
arrow_index=get_index(details['broker_time']),
)
# 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
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']),
)