ui.order_mode: prioritize mkt-match on `.bs_mktid`
For backends which opt to set the new `BrokerdPosition.bs_mktid` field, give (matching logic) priority to it such that even if the `.symbol` field doesn't match the mkt currently focussed on chart, it will always match on a provider's own internal asset-mapping-id. The original fallback logic for `.fqme` matching is left as is. As an example with IB, a qqq.nasdaq.ib txn may have been filled on a non-primary venue as qqq.directedea.ib, in this case if the mkt is displayed and focused on chart we want the **entire position info** to be overlayed by the `OrderMode` UX without discrepancy. Other refinements, - improve logging and add a detailed edge-case-comment around the `.on_fill()` handler to clarify where if a benign 'error' msg is relayed from a backend it will cause the UI to operate as though the order **was not-cleared/cancelled** since the `.on_cancel()` handler will have likely been called just before, popping the `.dialogs` entry. Return `bool` to indicate whether the UI removed-lines / added-fill-arrows. - inverse the `return` branching logic in `.on_cancel()` to reduce indent. - add a very loud `log.error()` in `Status(resp='error')` case-block ensuring the console yells about the order being cancelled, also a todo for the weird msg-field recursion nonsense..qt_w_graceful_SIGINT
parent
ad3fe65bd9
commit
3f48098c55
|
@ -555,14 +555,13 @@ class OrderMode:
|
|||
|
||||
def on_fill(
|
||||
self,
|
||||
|
||||
uuid: str,
|
||||
price: float,
|
||||
time_s: float,
|
||||
|
||||
pointing: str | None = None,
|
||||
|
||||
) -> None:
|
||||
) -> bool:
|
||||
'''
|
||||
Fill msg handler.
|
||||
|
||||
|
@ -575,13 +574,33 @@ class OrderMode:
|
|||
- update fill bar size
|
||||
|
||||
'''
|
||||
dialog = self.dialogs[uuid]
|
||||
# XXX WARNING XXX
|
||||
# if a `Status(resp='error')` arrives *before* this
|
||||
# fill-status, the `.dialogs` entry may have already been
|
||||
# popped and thus the below will skipped.
|
||||
#
|
||||
# NOTE, to avoid this confusing scenario ensure that any
|
||||
# errors delivered thru from the broker-backend are not just
|
||||
# "noisy reporting" (like is very common from IB..) and are
|
||||
# instead ONLY errors-causing-order-dialog-cancellation!
|
||||
if not (dialog := self.dialogs.get(uuid)):
|
||||
log.warning(
|
||||
f'Order was already cleared from `.dialogs` ??\n'
|
||||
f'uuid: {uuid!r}\n'
|
||||
)
|
||||
return False
|
||||
|
||||
lines = dialog.lines
|
||||
chart = self.chart
|
||||
|
||||
# XXX: seems to fail on certain types of races?
|
||||
if not lines:
|
||||
log.warn("No line(s) for order {uuid}!?")
|
||||
return False
|
||||
|
||||
# update line state(s)
|
||||
#
|
||||
# ?XXX this fails on certain types of races?
|
||||
# assert len(lines) == 2
|
||||
if lines:
|
||||
flume: Flume = self.feed.flumes[chart.linked.mkt.fqme]
|
||||
_, _, ratio = flume.get_ds_info()
|
||||
|
||||
|
@ -607,28 +626,31 @@ class OrderMode:
|
|||
pointing=pointing,
|
||||
color=lines[0].color
|
||||
)
|
||||
else:
|
||||
log.warn("No line(s) for order {uuid}!?")
|
||||
|
||||
def on_cancel(
|
||||
self,
|
||||
uuid: str
|
||||
uuid: str,
|
||||
|
||||
) -> None:
|
||||
) -> bool:
|
||||
|
||||
msg: Order = self.client._sent_orders.pop(uuid, None)
|
||||
|
||||
if msg is not None:
|
||||
self.lines.remove_line(uuid=uuid)
|
||||
self.chart.linked.cursor.show_xhair()
|
||||
|
||||
dialog = self.dialogs.pop(uuid, None)
|
||||
if dialog:
|
||||
dialog.last_status_close()
|
||||
else:
|
||||
msg: Order|None = self.client._sent_orders.pop(uuid, None)
|
||||
if msg is None:
|
||||
log.warning(
|
||||
f'Received cancel for unsubmitted order {pformat(msg)}'
|
||||
)
|
||||
return False
|
||||
|
||||
# remove GUI line, show cursor.
|
||||
self.lines.remove_line(uuid=uuid)
|
||||
self.chart.linked.cursor.show_xhair()
|
||||
|
||||
# remove msg dialog (history)
|
||||
dialog: Dialog|None = self.dialogs.pop(uuid, None)
|
||||
if dialog:
|
||||
dialog.last_status_close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def cancel_orders_under_cursor(self) -> list[str]:
|
||||
return self.cancel_orders(
|
||||
|
@ -1057,13 +1079,23 @@ async def process_trade_msg(
|
|||
if name in (
|
||||
'position',
|
||||
):
|
||||
sym: MktPair = mode.chart.linked.mkt
|
||||
mkt: MktPair = mode.chart.linked.mkt
|
||||
pp_msg_symbol = msg['symbol'].lower()
|
||||
fqme = sym.fqme
|
||||
broker = sym.broker
|
||||
pp_msg_bsmktid = msg['bs_mktid']
|
||||
fqme = mkt.fqme
|
||||
broker = mkt.broker
|
||||
if (
|
||||
# match on any backed-specific(-unique)-ID first!
|
||||
(
|
||||
pp_msg_bsmktid
|
||||
and
|
||||
mkt.bs_mktid == pp_msg_bsmktid
|
||||
)
|
||||
or
|
||||
# OW try against what's provided as an FQME..
|
||||
pp_msg_symbol == fqme
|
||||
or pp_msg_symbol == fqme.removesuffix(f'.{broker}')
|
||||
or
|
||||
pp_msg_symbol == fqme.removesuffix(f'.{broker}')
|
||||
):
|
||||
log.info(
|
||||
f'Loading position for `{fqme}`:\n'
|
||||
|
@ -1086,7 +1118,7 @@ async def process_trade_msg(
|
|||
return
|
||||
|
||||
msg = Status(**msg)
|
||||
resp = msg.resp
|
||||
# resp: str = msg.resp
|
||||
oid = msg.oid
|
||||
dialog: Dialog = mode.dialogs.get(oid)
|
||||
|
||||
|
@ -1150,19 +1182,32 @@ async def process_trade_msg(
|
|||
mode.on_submit(oid)
|
||||
|
||||
case Status(resp='error'):
|
||||
|
||||
# do all the things for a cancel:
|
||||
# - drop order-msg dialog from client table
|
||||
# - delete level line from view
|
||||
mode.on_cancel(oid)
|
||||
|
||||
# TODO: parse into broker-side msg, or should we
|
||||
# expect it to just be **that** msg verbatim (since
|
||||
# we'd presumably have only 1 `Error` msg-struct)
|
||||
broker_msg: dict = msg.brokerd_msg
|
||||
|
||||
# XXX NOTE, this presumes the rxed "error" is
|
||||
# order-dialog-cancel-causing, THUS backends much ONLY
|
||||
# relay errors of this "severity"!!
|
||||
log.error(
|
||||
f'Order {oid}->{resp} with:\n{pformat(broker_msg)}'
|
||||
f'Order errored ??\n'
|
||||
f'oid: {oid!r}\n'
|
||||
f'\n'
|
||||
f'{pformat(broker_msg)}\n'
|
||||
f'\n'
|
||||
f'=> CANCELLING ORDER DIALOG <=\n'
|
||||
|
||||
# from tractor.devx.pformat import ppfmt
|
||||
# !TODO LOL, wtf the msg is causing
|
||||
# a recursion bug!
|
||||
# -[ ] get this shit on msgspec stat!
|
||||
# f'{ppfmt(broker_msg)}'
|
||||
)
|
||||
# do all the things for a cancel:
|
||||
# - drop order-msg dialog from client table
|
||||
# - delete level line from view
|
||||
mode.on_cancel(oid)
|
||||
|
||||
case Status(resp='canceled'):
|
||||
# delete level line from view
|
||||
|
@ -1178,10 +1223,10 @@ async def process_trade_msg(
|
|||
# TODO: UX for a "pending" clear/live order
|
||||
log.info(f'Dark order triggered for {fmtmsg}')
|
||||
|
||||
case Status(
|
||||
resp='triggered',
|
||||
# TODO: do the struct-msg version, blah blah..
|
||||
# req=Order(exec_mode='live', action='alert') as req,
|
||||
case Status(
|
||||
resp='triggered',
|
||||
req={
|
||||
'exec_mode': 'live',
|
||||
'action': 'alert',
|
||||
|
|
Loading…
Reference in New Issue