Nest nursery scope inside EMS ctx in order mode

Move the `trio.open_nursery()` + `collapse_eg()`
block to be nested inside `open_ems()` instead
of as a sibling — nursery tasks (order handling,
trade processing) depend on the EMS connection
staying alive.

Also,
- Default `'paper'` entry upfront in `accounts`
  dict and raise `ConfigurationError` with an
  actionable msg when a `brokerd_accounts` fqan
  isn't found in `accounts_def`.
- Use `msg.setdefault('brokerd_msg', msg)` for
  ems dialog msgs instead of blind assignment;
  warn if already set.
- Alias `asynccontextmanager as acm`.
- Use `fqan` var naming for fully-qualified
  account names, `broker_name` from `mkt.broker`.
- Tighten log msg formatting (`fqme!r`).

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
nested_order_mode_block
Gud Boi 2026-04-05 14:00:15 -04:00
parent 5387538ba9
commit e05c258914
1 changed files with 138 additions and 110 deletions

View File

@ -19,7 +19,7 @@ Chart trading, the only way to scalp.
""" """
from __future__ import annotations from __future__ import annotations
from contextlib import asynccontextmanager from contextlib import asynccontextmanager as acm
from dataclasses import dataclass, field from dataclasses import dataclass, field
from decimal import Decimal from decimal import Decimal
from functools import partial from functools import partial
@ -779,7 +779,7 @@ class OrderMode:
return maybe_dialog return maybe_dialog
@asynccontextmanager @acm
async def open_order_mode( async def open_order_mode(
feed: Feed, feed: Feed,
godw: GodWidget, godw: GodWidget,
@ -814,6 +814,7 @@ async def open_order_mode(
# spawn EMS actor-service # spawn EMS actor-service
async with ( async with (
# tractor.trionics.collapse_eg(),
open_ems( open_ems(
fqme, fqme,
loglevel=loglevel, loglevel=loglevel,
@ -824,11 +825,10 @@ async def open_order_mode(
brokerd_accounts, brokerd_accounts,
ems_dialog_msgs, ems_dialog_msgs,
), ),
tractor.trionics.collapse_eg(),
trio.open_nursery() as tn,
): ):
log.info(f'Opening order mode for {fqme}') log.info(
f'Opening order-mode for {fqme!r}'
)
# annotations editors # annotations editors
lines = LineEditor(godw=godw) lines = LineEditor(godw=godw)
@ -841,21 +841,34 @@ async def open_order_mode(
trackers: dict[str, PositionTracker] = {} trackers: dict[str, PositionTracker] = {}
# load account names from ``brokers.toml`` # load account names from ``brokers.toml``
broker_name: str = mkt.broker
accounts_def: bidict[str, str | None] = config.load_accounts( accounts_def: bidict[str, str | None] = config.load_accounts(
providers=[mkt.broker], providers=[broker_name],
) )
# XXX: ``brokerd`` delivers a set of account names that it # XXX: ``brokerd`` delivers a set of account names that it
# allows use of but the user also can define the accounts they'd # allows use of but the user also can define the accounts they'd
# like to use, in order, in their `brokers.toml` file. # like to use, in order, in their `brokers.toml` file.
accounts: dict[str, str] = {} accounts: dict[str, str] = {
for name in brokerd_accounts: 'paper': 'paper',
# ensure name is in ``brokers.toml`` }
accounts[name] = accounts_def[name] fqan: str # ex. 'kraken.spot'
for fqan in brokerd_accounts:
# ensure fully-qualified-account-name declared in `brokers.toml`
# always add a paper entry so that paper cleared try:
accounts[fqan] = accounts_def[fqan]
except KeyError as ke:
raise config.ConfigurationError(
f'The {broker_name!r} account {fqan!r} could not be found?\n'
f'\n'
f'Did you forget to define the correct account name in your `brokers.toml`?\n'
f'{ppfmt(accounts_def)}\n'
) from ke
# NOTE, always add a paper entry so that paper cleared
# order dialogs can be tracked in the order mode UIs. # order dialogs can be tracked in the order mode UIs.
accounts['paper'] = 'paper' # accounts['paper'] = 'paper'
# first account listed is the one we select at startup # first account listed is the one we select at startup
# (aka order based selection). # (aka order based selection).
@ -940,6 +953,10 @@ async def open_order_mode(
for name, tracker in trackers.items(): for name, tracker in trackers.items():
order_pane.update_account_icons({name: tracker.live_pp}) order_pane.update_account_icons({name: tracker.live_pp})
async with (
tractor.trionics.collapse_eg(),
trio.open_nursery() as tn,
):
# top level abstraction which wraps all this crazyness into # top level abstraction which wraps all this crazyness into
# a namespace.. # a namespace..
mode = OrderMode( mode = OrderMode(
@ -1028,7 +1045,17 @@ async def open_order_mode(
# parse into proper ``Status`` equivalents ems-side? # parse into proper ``Status`` equivalents ems-side?
# msg.setdefault('resp', msg['broker_details']['resp']) # msg.setdefault('resp', msg['broker_details']['resp'])
# msg.setdefault('oid', msg['broker_details']['oid']) # msg.setdefault('oid', msg['broker_details']['oid'])
msg['brokerd_msg'] = msg ya_msg: dict = msg.setdefault(
'brokerd_msg',
msg,
)
if msg is not ya_msg:
log.warning(
f'A `.brokerd_msg` was already set for ems-dialog msg?\n'
f'oid: {oid!r}\n'
f'ya_msg: {ya_msg!r}\n'
f'msg: {ya_msg!r}\n'
)
await process_trade_msg( await process_trade_msg(
mode, mode,
@ -1281,7 +1308,8 @@ async def process_trade_msg(
# that should never happen tho? # that should never happen tho?
action: str = ( action: str = (
getattr(order, 'action', None) getattr(order, 'action', None)
or order['action'] or
order['action']
) )
details: dict = msg.brokerd_msg details: dict = msg.brokerd_msg