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: