Don't override `Account.pps: dict` entries..

Despite a `.bs_mktid` ideally being a bijection with `MktPair.fqme`
values, apparently some backends (cough IB) will switch the .<venue>`
part in txn records resulting in multiple account-conf-file sections for
the same dst asset. Obviously that means we can't allocate new
`Position` entries keyed by that `bs_mktid`, instead be sure to **update
them instead**!

Deats,
- add case logic to avoid pp overwrites using a `pp_objs.get()` check.
- warn on duplicated pos entries whenever the current account-file
  entry's `mkt` doesn't match the pre-existing position's.
- mk `Position.add_clear()` return a `bool` indicating if the record was
  newly added, warn when it was already existing/added prior.

Also,
- drop the already deprecated `open_pps()`, also from sub-pkg exports.
- draft TODO for `Position.summary()` idea as a replacement for
  `BrokerdPosition`-msgs.
qt_w_graceful_SIGINT
Tyler Goodlet 2025-09-26 15:17:41 -04:00
parent 6cc3518143
commit b0f273f091
2 changed files with 35 additions and 31 deletions

View File

@ -33,7 +33,6 @@ from ._pos import (
Account, Account,
load_account, load_account,
load_account_from_ledger, load_account_from_ledger,
open_pps,
open_account, open_account,
Position, Position,
) )
@ -68,7 +67,6 @@ __all__ = [
'load_account_from_ledger', 'load_account_from_ledger',
'mk_allocator', 'mk_allocator',
'open_account', 'open_account',
'open_pps',
'open_trade_ledger', 'open_trade_ledger',
'unpack_fqme', 'unpack_fqme',
'DerivTypes', 'DerivTypes',

View File

@ -353,13 +353,12 @@ class Position(Struct):
) -> bool: ) -> bool:
''' '''
Update clearing table by calculating the rolling ppu and Update clearing table by calculating the rolling ppu and
(accumulative) size in both the clears entry and local (accumulative) size in both the clears entry and local attrs
attrs state. state.
Inserts are always done in datetime sorted order. Inserts are always done in datetime sorted order.
''' '''
# added: bool = False
tid: str = t.tid tid: str = t.tid
if tid in self._events: if tid in self._events:
log.debug( log.debug(
@ -367,7 +366,7 @@ class Position(Struct):
f'\n' f'\n'
f'{t}\n' f'{t}\n'
) )
# return added return False
# TODO: apparently this IS possible with a dict but not # TODO: apparently this IS possible with a dict but not
# common and probably not that beneficial unless we're also # common and probably not that beneficial unless we're also
@ -448,6 +447,12 @@ class Position(Struct):
# def suggest_split(self) -> float: # def suggest_split(self) -> float:
# ... # ...
# ?TODO, for sending rendered state over the wire?
# def summary(self) -> PositionSummary:
# do minimal conversion to a subset of fields
# currently defined in `.clearing._messages.BrokerdPosition`
class Account(Struct): class Account(Struct):
''' '''
@ -491,9 +496,9 @@ class Account(Struct):
def update_from_ledger( def update_from_ledger(
self, self,
ledger: TransactionLedger | dict[str, Transaction], ledger: TransactionLedger|dict[str, Transaction],
cost_scalar: float = 2, cost_scalar: float = 2,
symcache: SymbologyCache | None = None, symcache: SymbologyCache|None = None,
_mktmap_table: dict[str, MktPair] | None = None, _mktmap_table: dict[str, MktPair] | None = None,
@ -714,7 +719,7 @@ class Account(Struct):
# XXX WTF: if we use a tomlkit.Integer here we get this # XXX WTF: if we use a tomlkit.Integer here we get this
# super weird --1 thing going on for cumsize!?1! # super weird --1 thing going on for cumsize!?1!
# NOTE: the fix was to always float() the size value loaded # NOTE: the fix was to always float() the size value loaded
# in open_pps() below! # in open_account() below!
config.write( config.write(
config=self.conf, config=self.conf,
path=self.conf_path, path=self.conf_path,
@ -898,7 +903,6 @@ def open_account(
clears_table['dt'] = dt clears_table['dt'] = dt
trans.append(Transaction( trans.append(Transaction(
fqme=bs_mktid, fqme=bs_mktid,
# sym=mkt,
bs_mktid=bs_mktid, bs_mktid=bs_mktid,
tid=tid, tid=tid,
# XXX: not sure why sometimes these are loaded as # XXX: not sure why sometimes these are loaded as
@ -921,11 +925,22 @@ def open_account(
): ):
expiry: pendulum.DateTime = pendulum.parse(expiry) expiry: pendulum.DateTime = pendulum.parse(expiry)
pp = pp_objs[bs_mktid] = Position( # !XXX, should never be duplicates over
mkt, # a backend-(broker)-system's unique market-IDs!
split_ratio=split_ratio, if pos := pp_objs.get(bs_mktid):
bs_mktid=bs_mktid, if mkt != pos.mkt:
) log.warning(
f'Duplicated position but diff `MktPair.fqme` ??\n'
f'bs_mktid: {bs_mktid!r}\n'
f'pos.mkt: {pos.mkt}\n'
f'mkt: {mkt}\n'
)
else:
pos = pp_objs[bs_mktid] = Position(
mkt,
split_ratio=split_ratio,
bs_mktid=bs_mktid,
)
# XXX: super critical, we need to be sure to include # XXX: super critical, we need to be sure to include
# all pps.toml clears to avoid reusing clears that were # all pps.toml clears to avoid reusing clears that were
@ -933,8 +948,13 @@ def open_account(
# state, since today's records may have already been # state, since today's records may have already been
# processed! # processed!
for t in trans: for t in trans:
pp.add_clear(t) added: bool = pos.add_clear(t)
if not added:
log.warning(
f'Txn already recorded in pp ??\n'
f'\n'
f'{t}\n'
)
try: try:
yield acnt yield acnt
finally: finally:
@ -942,20 +962,6 @@ def open_account(
acnt.write_config() acnt.write_config()
# TODO: drop the old name and THIS!
@cm
def open_pps(
*args,
**kwargs,
) -> Generator[Account, None, None]:
log.warning(
'`open_pps()` is now deprecated!\n'
'Please use `with open_account() as cnt:`'
)
with open_account(*args, **kwargs) as acnt:
yield acnt
def load_account_from_ledger( def load_account_from_ledger(
brokername: str, brokername: str,