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 contextlib import asynccontextmanager
from contextlib import asynccontextmanager as acm
from dataclasses import dataclass, field
from decimal import Decimal
from functools import partial
@ -779,7 +779,7 @@ class OrderMode:
return maybe_dialog
@asynccontextmanager
@acm
async def open_order_mode(
feed: Feed,
godw: GodWidget,
@ -814,6 +814,7 @@ async def open_order_mode(
# spawn EMS actor-service
async with (
# tractor.trionics.collapse_eg(),
open_ems(
fqme,
loglevel=loglevel,
@ -824,11 +825,10 @@ async def open_order_mode(
brokerd_accounts,
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
lines = LineEditor(godw=godw)
@ -841,21 +841,34 @@ async def open_order_mode(
trackers: dict[str, PositionTracker] = {}
# load account names from ``brokers.toml``
broker_name: str = mkt.broker
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
# allows use of but the user also can define the accounts they'd
# like to use, in order, in their `brokers.toml` file.
accounts: dict[str, str] = {}
for name in brokerd_accounts:
# ensure name is in ``brokers.toml``
accounts[name] = accounts_def[name]
accounts: dict[str, str] = {
'paper': 'paper',
}
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.
accounts['paper'] = 'paper'
# accounts['paper'] = 'paper'
# first account listed is the one we select at startup
# (aka order based selection).
@ -940,6 +953,10 @@ async def open_order_mode(
for name, tracker in trackers.items():
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
# a namespace..
mode = OrderMode(
@ -1028,7 +1045,17 @@ async def open_order_mode(
# parse into proper ``Status`` equivalents ems-side?
# msg.setdefault('resp', msg['broker_details']['resp'])
# 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(
mode,
@ -1281,7 +1308,8 @@ async def process_trade_msg(
# that should never happen tho?
action: str = (
getattr(order, 'action', None)
or order['action']
or
order['action']
)
details: dict = msg.brokerd_msg