From eeeeb29f715fc08a92e43ec13586695e0cf321db Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 17 Jun 2021 17:00:57 -0400 Subject: [PATCH] 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. --- piker/clearing/_client.py | 4 +- piker/ui/order_mode.py | 94 ++++++++++++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/piker/clearing/_client.py b/piker/clearing/_client.py index fcceeaac..97869bb9 100644 --- a/piker/clearing/_client.py +++ b/piker/clearing/_client.py @@ -125,7 +125,9 @@ def get_orders( if _orders is None: # setup local ui event streaming channels for request/resp # streamging with EMS daemon - _orders = OrderBook(*trio.open_memory_channel(1)) + _orders = OrderBook( + *trio.open_memory_channel(100), + ) return _orders diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index 5ed9f96d..f4e58329 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -28,10 +28,10 @@ import uuid import pyqtgraph as pg 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 +from ._window import MultiStatus, main_window from ..clearing._client import open_ems, OrderBook from ..data._source import Symbol from ..log import get_logger @@ -49,18 +49,28 @@ class Position(BaseModel): @dataclass 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" (when wathing the rt price update at the current time step) - and allows entering orders using the ``a, d, f`` keys and - cancelling moused-over orders with the ``c`` key. + and allows entering orders using mouse and keyboard. - """ + 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 book: OrderBook lines: LineEditor arrows: ArrowEditor + status_bar: MultiStatus + _colors = { 'alert': 'alert_yellow', 'buy': 'buy_green', @@ -72,7 +82,8 @@ class OrderMode: _position: Dict[str, Any] = field(default_factory=dict) _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( self, @@ -134,6 +145,13 @@ class OrderMode: """ 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 def on_fill( @@ -191,6 +209,10 @@ class OrderMode: self.lines.remove_line(uuid=uuid) self.chart._cursor.show_xhair() + pending = self._pending_submissions.pop(uuid, None) + if pending: + order_line, func = pending + func() else: log.warning( f'Received cancel for unsubmitted order {pformat(msg)}' @@ -245,17 +267,67 @@ class OrderMode: ) 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 line._on_drag_start = self.order_line_modify_start line._on_drag_end = self.order_line_modify_complete return line - def cancel_order_under_cursor(self) -> None: - for line in self.lines.lines_under_cursor(): - self.book.cancel(uuid=line.oid) + def cancel_orders_under_cursor(self) -> list[str]: + return self.cancel_orders_from_lines( + 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 + def order_line_modify_start( self, line: LevelLine, @@ -281,13 +353,14 @@ async def open_order_mode( chart: pg.PlotWidget, book: OrderBook, ): + status_bar: MultiStatus = main_window().status_bar view = chart._vb lines = LineEditor(chart=chart, _order_lines=_order_lines) arrows = ArrowEditor(chart, {}) log.info("Opening order mode") - mode = OrderMode(chart, book, lines, arrows) + mode = OrderMode(chart, book, lines, arrows, status_bar) view.mode = mode asset_type = symbol.type_key @@ -318,7 +391,6 @@ async def start_order_mode( symbol: Symbol, brokername: str, - # task_status: TaskStatus[trio.Event] = trio.TASK_STATUS_IGNORED, started: trio.Event, ) -> None: