Add order cancellation and submission statuses

Generalize the methods for cancelling groups of orders (all or those
under cursor) and add new group status support such that statuses for
each cancel or order submission is displayed in the status bar. In the
"cancel-all-orders" case, use the new group status stuff.
asyncify_input_modes
Tyler Goodlet 2021-06-17 17:00:57 -04:00
parent 572f984d06
commit eeeeb29f71
2 changed files with 86 additions and 12 deletions

View File

@ -125,7 +125,9 @@ def get_orders(
if _orders is None: if _orders is None:
# setup local ui event streaming channels for request/resp # setup local ui event streaming channels for request/resp
# streamging with EMS daemon # streamging with EMS daemon
_orders = OrderBook(*trio.open_memory_channel(1)) _orders = OrderBook(
*trio.open_memory_channel(100),
)
return _orders return _orders

View File

@ -28,10 +28,10 @@ import uuid
import pyqtgraph as pg import pyqtgraph as pg
from pydantic import BaseModel from pydantic import BaseModel
import trio 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
from ._window import MultiStatus, main_window
from ..clearing._client import open_ems, OrderBook from ..clearing._client import open_ems, OrderBook
from ..data._source import Symbol from ..data._source import Symbol
from ..log import get_logger from ..log import get_logger
@ -49,18 +49,28 @@ class Position(BaseModel):
@dataclass @dataclass
class OrderMode: class OrderMode:
"""Major mode for placing orders on a chart view. '''Major mode for placing orders on a chart view.
This is the default mode that pairs with "follow mode" This is the default mode that pairs with "follow mode"
(when wathing the rt price update at the current time step) (when wathing the rt price update at the current time step)
and allows entering orders using the ``a, d, f`` keys and and allows entering orders using mouse and keyboard.
cancelling moused-over orders with the ``c`` key.
""" Current manual:
a -> alert
s/ctrl -> submission type modifier {on: live, off: dark}
f (fill) -> buy limit order
d (dump) -> sell limit order
c (cancel) -> cancel order under cursor
cc -> cancel all submitted orders on chart
mouse click and drag -> modify current order under cursor
'''
chart: 'ChartPlotWidget' # type: ignore # noqa chart: 'ChartPlotWidget' # type: ignore # noqa
book: OrderBook book: OrderBook
lines: LineEditor lines: LineEditor
arrows: ArrowEditor arrows: ArrowEditor
status_bar: MultiStatus
_colors = { _colors = {
'alert': 'alert_yellow', 'alert': 'alert_yellow',
'buy': 'buy_green', 'buy': 'buy_green',
@ -72,7 +82,8 @@ class OrderMode:
_position: Dict[str, Any] = field(default_factory=dict) _position: Dict[str, Any] = field(default_factory=dict)
_position_line: dict = None _position_line: dict = None
key_map: Dict[str, Callable] = field(default_factory=dict) _pending_submissions: dict[str, (LevelLine, Callable)] = field(
default_factory=dict)
def on_position_update( def on_position_update(
self, self,
@ -134,6 +145,13 @@ class OrderMode:
""" """
line = self.lines.commit_line(uuid) line = self.lines.commit_line(uuid)
pending = self._pending_submissions.get(uuid)
if pending:
order_line, func = pending
assert order_line is line
func()
return line return line
def on_fill( def on_fill(
@ -191,6 +209,10 @@ class OrderMode:
self.lines.remove_line(uuid=uuid) self.lines.remove_line(uuid=uuid)
self.chart._cursor.show_xhair() self.chart._cursor.show_xhair()
pending = self._pending_submissions.pop(uuid, None)
if pending:
order_line, func = pending
func()
else: else:
log.warning( log.warning(
f'Received cancel for unsubmitted order {pformat(msg)}' f'Received cancel for unsubmitted order {pformat(msg)}'
@ -245,17 +267,67 @@ class OrderMode:
) )
line.oid = uid line.oid = uid
# enter submission which will be popped once a response
# from the EMS is received to move the order to a different# status
self._pending_submissions[uid] = (
line,
self.status_bar.open_status(
f'submitting {self._exec_mode}-{action}',
final_msg=f'submitted {self._exec_mode}-{action}',
clear_on_next=True,
)
)
# hook up mouse drag handlers # hook up mouse drag handlers
line._on_drag_start = self.order_line_modify_start line._on_drag_start = self.order_line_modify_start
line._on_drag_end = self.order_line_modify_complete line._on_drag_end = self.order_line_modify_complete
return line return line
def cancel_order_under_cursor(self) -> None: def cancel_orders_under_cursor(self) -> list[str]:
for line in self.lines.lines_under_cursor(): return self.cancel_orders_from_lines(
self.book.cancel(uuid=line.oid) self.lines.lines_under_cursor()
)
def cancel_all_orders(self) -> list[str]:
return self.cancel_orders_from_lines(
self.lines.all_lines()
)
def cancel_orders_from_lines(
self,
lines: list[LevelLine],
) -> list[str]:
ids: list = []
if lines:
key = self.status_bar.open_status(
f'cancelling {len(lines)} orders',
final_msg=f'cancelled {len(lines)} orders',
group_key=True
)
# cancel all active orders and triggers
for line in lines:
oid = getattr(line, 'oid', None)
if oid:
self._pending_submissions[oid] = (
line,
self.status_bar.open_status(
f'cancelling order {oid[:6]}',
group_key=key,
),
)
ids.append(oid)
self.book.cancel(uuid=oid)
return ids
# order-line modify handlers # order-line modify handlers
def order_line_modify_start( def order_line_modify_start(
self, self,
line: LevelLine, line: LevelLine,
@ -281,13 +353,14 @@ async def open_order_mode(
chart: pg.PlotWidget, chart: pg.PlotWidget,
book: OrderBook, book: OrderBook,
): ):
status_bar: MultiStatus = main_window().status_bar
view = chart._vb view = chart._vb
lines = LineEditor(chart=chart, _order_lines=_order_lines) lines = LineEditor(chart=chart, _order_lines=_order_lines)
arrows = ArrowEditor(chart, {}) arrows = ArrowEditor(chart, {})
log.info("Opening order mode") log.info("Opening order mode")
mode = OrderMode(chart, book, lines, arrows) mode = OrderMode(chart, book, lines, arrows, status_bar)
view.mode = mode view.mode = mode
asset_type = symbol.type_key asset_type = symbol.type_key
@ -318,7 +391,6 @@ async def start_order_mode(
symbol: Symbol, symbol: Symbol,
brokername: str, brokername: str,
# task_status: TaskStatus[trio.Event] = trio.TASK_STATUS_IGNORED,
started: trio.Event, started: trio.Event,
) -> None: ) -> None: