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
parent
5387538ba9
commit
e05c258914
|
|
@ -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,110 +953,124 @@ async def open_order_mode(
|
|||
for name, tracker in trackers.items():
|
||||
order_pane.update_account_icons({name: tracker.live_pp})
|
||||
|
||||
# top level abstraction which wraps all this crazyness into
|
||||
# a namespace..
|
||||
mode = OrderMode(
|
||||
godw,
|
||||
feed,
|
||||
chart,
|
||||
hist_chart,
|
||||
tn,
|
||||
client,
|
||||
lines,
|
||||
arrows,
|
||||
multistatus,
|
||||
pane=order_pane,
|
||||
trackers=trackers,
|
||||
|
||||
)
|
||||
# XXX: MUST be set
|
||||
order_pane.order_mode = mode
|
||||
|
||||
# select a pp to track
|
||||
tracker: PositionTracker = trackers[pp_account]
|
||||
mode.current_pp = tracker
|
||||
tracker.nav.show()
|
||||
tracker.nav.hide_info()
|
||||
|
||||
# XXX: would love to not have to do this separate from edit
|
||||
# fields (which are done in an async loop - see below)
|
||||
# connect selection signals (from drop down widgets)
|
||||
# to order sync pane handler
|
||||
for key in ('account', 'size_unit',):
|
||||
w = form.fields[key]
|
||||
w.currentTextChanged.connect(
|
||||
partial(
|
||||
order_pane.on_selection_change,
|
||||
key=key,
|
||||
)
|
||||
)
|
||||
|
||||
# make fill bar and positioning snapshot
|
||||
order_pane.update_status_ui(tracker)
|
||||
|
||||
# TODO: create a mode "manager" of sorts?
|
||||
# -> probably just call it "UxModes" err sumthin?
|
||||
# so that view handlers can access it
|
||||
chart.view.order_mode = mode
|
||||
hist_chart.view.order_mode = mode
|
||||
|
||||
order_pane.on_ui_settings_change('account', pp_account)
|
||||
mode.pane.display_pnl(mode.current_pp)
|
||||
|
||||
# Begin order-response streaming
|
||||
done()
|
||||
|
||||
# Pack position messages by account, should only be one-to-one.
|
||||
# NOTE: requires the backend exactly specifies
|
||||
# the expected symbol key in its positions msg.
|
||||
for (
|
||||
(broker, acctid),
|
||||
pps_by_fqme
|
||||
) in position_msgs.items():
|
||||
for msg in pps_by_fqme.values():
|
||||
await process_trade_msg(
|
||||
mode,
|
||||
client,
|
||||
msg,
|
||||
)
|
||||
|
||||
async with (
|
||||
|
||||
# pp pane kb inputs
|
||||
open_form_input_handling(
|
||||
form,
|
||||
focus_next=chart.linked.godwidget,
|
||||
on_value_change=order_pane.on_ui_settings_change,
|
||||
),
|
||||
|
||||
tractor.trionics.collapse_eg(),
|
||||
trio.open_nursery() as tn,
|
||||
):
|
||||
# signal to top level symbol loading task we're ready
|
||||
# to handle input since the ems connection is ready
|
||||
started.set()
|
||||
# top level abstraction which wraps all this crazyness into
|
||||
# a namespace..
|
||||
mode = OrderMode(
|
||||
godw,
|
||||
feed,
|
||||
chart,
|
||||
hist_chart,
|
||||
tn,
|
||||
client,
|
||||
lines,
|
||||
arrows,
|
||||
multistatus,
|
||||
pane=order_pane,
|
||||
trackers=trackers,
|
||||
|
||||
for oid, msg in ems_dialog_msgs.items():
|
||||
)
|
||||
# XXX: MUST be set
|
||||
order_pane.order_mode = mode
|
||||
|
||||
# HACK ALERT: ensure a resp field is filled out since
|
||||
# techincally the call below expects a ``Status``. TODO:
|
||||
# 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
|
||||
# select a pp to track
|
||||
tracker: PositionTracker = trackers[pp_account]
|
||||
mode.current_pp = tracker
|
||||
tracker.nav.show()
|
||||
tracker.nav.hide_info()
|
||||
|
||||
await process_trade_msg(
|
||||
mode,
|
||||
client,
|
||||
msg,
|
||||
# XXX: would love to not have to do this separate from edit
|
||||
# fields (which are done in an async loop - see below)
|
||||
# connect selection signals (from drop down widgets)
|
||||
# to order sync pane handler
|
||||
for key in ('account', 'size_unit',):
|
||||
w = form.fields[key]
|
||||
w.currentTextChanged.connect(
|
||||
partial(
|
||||
order_pane.on_selection_change,
|
||||
key=key,
|
||||
)
|
||||
)
|
||||
|
||||
tn.start_soon(
|
||||
process_trades_and_update_ui,
|
||||
trades_stream,
|
||||
mode,
|
||||
client,
|
||||
)
|
||||
# make fill bar and positioning snapshot
|
||||
order_pane.update_status_ui(tracker)
|
||||
|
||||
yield mode
|
||||
# TODO: create a mode "manager" of sorts?
|
||||
# -> probably just call it "UxModes" err sumthin?
|
||||
# so that view handlers can access it
|
||||
chart.view.order_mode = mode
|
||||
hist_chart.view.order_mode = mode
|
||||
|
||||
order_pane.on_ui_settings_change('account', pp_account)
|
||||
mode.pane.display_pnl(mode.current_pp)
|
||||
|
||||
# Begin order-response streaming
|
||||
done()
|
||||
|
||||
# Pack position messages by account, should only be one-to-one.
|
||||
# NOTE: requires the backend exactly specifies
|
||||
# the expected symbol key in its positions msg.
|
||||
for (
|
||||
(broker, acctid),
|
||||
pps_by_fqme
|
||||
) in position_msgs.items():
|
||||
for msg in pps_by_fqme.values():
|
||||
await process_trade_msg(
|
||||
mode,
|
||||
client,
|
||||
msg,
|
||||
)
|
||||
|
||||
async with (
|
||||
|
||||
# pp pane kb inputs
|
||||
open_form_input_handling(
|
||||
form,
|
||||
focus_next=chart.linked.godwidget,
|
||||
on_value_change=order_pane.on_ui_settings_change,
|
||||
),
|
||||
|
||||
):
|
||||
# signal to top level symbol loading task we're ready
|
||||
# to handle input since the ems connection is ready
|
||||
started.set()
|
||||
|
||||
for oid, msg in ems_dialog_msgs.items():
|
||||
|
||||
# HACK ALERT: ensure a resp field is filled out since
|
||||
# techincally the call below expects a ``Status``. TODO:
|
||||
# parse into proper ``Status`` equivalents ems-side?
|
||||
# msg.setdefault('resp', msg['broker_details']['resp'])
|
||||
# msg.setdefault('oid', msg['broker_details']['oid'])
|
||||
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,
|
||||
client,
|
||||
msg,
|
||||
)
|
||||
|
||||
tn.start_soon(
|
||||
process_trades_and_update_ui,
|
||||
trades_stream,
|
||||
mode,
|
||||
client,
|
||||
)
|
||||
|
||||
yield mode
|
||||
|
||||
|
||||
async def process_trades_and_update_ui(
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue