Allow accounting (file) dir override via kwarg
For testing (and probably hacking) it's handy to be able to point somewhere other the default user-config dir for a ledger or account file to test offline processing apis from `.accounting` subsystems. For now it's a private optional named-arg: `_fp: Path` and it's obviously passed down into the `load_account()` config getter. Note that in the non-paper account case `Account.update_from_ledger()` will use the ledger's `.symcache` and `.iter_txns()` method to acquite actual txn-structs to compute positions.account_tests
parent
803f4a6354
commit
b9fec091ca
|
@ -51,7 +51,7 @@ from ._mktinfo import (
|
||||||
)
|
)
|
||||||
from .calc import (
|
from .calc import (
|
||||||
ppu,
|
ppu,
|
||||||
iter_by_dt,
|
# iter_by_dt,
|
||||||
)
|
)
|
||||||
from .. import config
|
from .. import config
|
||||||
from ..clearing._messages import (
|
from ..clearing._messages import (
|
||||||
|
@ -145,6 +145,7 @@ class Position(Struct):
|
||||||
def iter_by_type(
|
def iter_by_type(
|
||||||
self,
|
self,
|
||||||
etype: str,
|
etype: str,
|
||||||
|
|
||||||
) -> Iterator[dict | Transaction]:
|
) -> Iterator[dict | Transaction]:
|
||||||
'''
|
'''
|
||||||
Iterate the internally managed ``._events: dict`` table in
|
Iterate the internally managed ``._events: dict`` table in
|
||||||
|
@ -152,15 +153,15 @@ class Position(Struct):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# sort on the expected datetime field
|
# sort on the expected datetime field
|
||||||
for event in iter_by_dt(
|
# for event in iter_by_dt(
|
||||||
|
for event in sorted(
|
||||||
self._events.values(),
|
self._events.values(),
|
||||||
key=lambda entry:
|
key=lambda entry: entry.dt
|
||||||
getattr(entry, 'dt', None)
|
|
||||||
or entry.get('dt'),
|
|
||||||
):
|
):
|
||||||
|
# if event.etype == etype:
|
||||||
match event:
|
match event:
|
||||||
case (
|
case (
|
||||||
{ 'etype': _etype} |
|
{'etype': _etype} |
|
||||||
Transaction(etype=str(_etype))
|
Transaction(etype=str(_etype))
|
||||||
):
|
):
|
||||||
assert _etype == etype
|
assert _etype == etype
|
||||||
|
@ -465,7 +466,7 @@ class Account(Struct):
|
||||||
|
|
||||||
def update_from_ledger(
|
def update_from_ledger(
|
||||||
self,
|
self,
|
||||||
ledger: TransactionLedger,
|
ledger: TransactionLedger | dict[str, Transaction],
|
||||||
cost_scalar: float = 2,
|
cost_scalar: float = 2,
|
||||||
symcache: SymbologyCache | None = None,
|
symcache: SymbologyCache | None = None,
|
||||||
|
|
||||||
|
@ -478,31 +479,34 @@ class Account(Struct):
|
||||||
'''
|
'''
|
||||||
if (
|
if (
|
||||||
not isinstance(ledger, TransactionLedger)
|
not isinstance(ledger, TransactionLedger)
|
||||||
and symcache is None
|
|
||||||
):
|
):
|
||||||
|
if symcache is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'No ledger provided!\n'
|
'No ledger provided!\n'
|
||||||
'We can not determine the `MktPair`s without a symcache..\n'
|
'We can not determine the `MktPair`s without a symcache..\n'
|
||||||
'Please provide `symcache: SymbologyCache` when '
|
'Please provide `symcache: SymbologyCache` when '
|
||||||
'processing NEW positions!'
|
'processing NEW positions!'
|
||||||
)
|
)
|
||||||
|
itertxns = sorted(
|
||||||
|
ledger.values(),
|
||||||
|
key=lambda t: t.dt,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
itertxns = ledger.iter_txns()
|
||||||
|
symcache = ledger.symcache
|
||||||
|
|
||||||
pps = self.pps
|
pps = self.pps
|
||||||
updated: dict[str, Position] = {}
|
updated: dict[str, Position] = {}
|
||||||
|
|
||||||
# lifo update all pps from records, ensuring
|
# lifo update all pps from records, ensuring
|
||||||
# we compute the PPU and size sorted in time!
|
# we compute the PPU and size sorted in time!
|
||||||
for tid, txn in ledger.iter_txns():
|
for txn in itertxns:
|
||||||
# for t in sorted(
|
|
||||||
# trans.values(),
|
|
||||||
# key=lambda t: t.dt,
|
|
||||||
# ):
|
|
||||||
fqme: str = txn.fqme
|
fqme: str = txn.fqme
|
||||||
bs_mktid: str = txn.bs_mktid
|
bs_mktid: str = txn.bs_mktid
|
||||||
|
|
||||||
# template the mkt-info presuming a legacy market ticks
|
# template the mkt-info presuming a legacy market ticks
|
||||||
# if no info exists in the transactions..
|
# if no info exists in the transactions..
|
||||||
mkt: MktPair = ledger._symcache.mktmaps[fqme]
|
mkt: MktPair = symcache.mktmaps[fqme]
|
||||||
|
|
||||||
if not (pos := pps.get(bs_mktid)):
|
if not (pos := pps.get(bs_mktid)):
|
||||||
|
|
||||||
|
@ -522,12 +526,13 @@ class Account(Struct):
|
||||||
|
|
||||||
# update clearing acnt!
|
# update clearing acnt!
|
||||||
# NOTE: likely you'll see repeats of the same
|
# NOTE: likely you'll see repeats of the same
|
||||||
# ``Transaction`` passed in here if/when you are restarting
|
# ``Transaction`` passed in here if/when you are
|
||||||
# a ``brokerd.ib`` where the API will re-report trades from
|
# restarting a ``brokerd.ib`` where the API will
|
||||||
# the current session, so we need to make sure we don't
|
# re-report trades from the current session, so we need
|
||||||
# "double count" these in pp calculations;
|
# to make sure we don't "double count" these in pp
|
||||||
# `Position.add_clear()` stores txs in a `dict[tid,
|
# calculations; `Position.add_clear()` stores txs in
|
||||||
# tx]` which should always ensure this is true B)
|
# a `._events: dict[tid, tx]` which should always
|
||||||
|
# ensure this is true!
|
||||||
pos.add_clear(txn)
|
pos.add_clear(txn)
|
||||||
updated[txn.bs_mktid] = pos
|
updated[txn.bs_mktid] = pos
|
||||||
|
|
||||||
|
@ -679,6 +684,8 @@ def load_account(
|
||||||
brokername: str,
|
brokername: str,
|
||||||
acctid: str,
|
acctid: str,
|
||||||
|
|
||||||
|
dirpath: Path | None = None,
|
||||||
|
|
||||||
) -> tuple[dict, Path]:
|
) -> tuple[dict, Path]:
|
||||||
'''
|
'''
|
||||||
Load a accounting (with positions) file from
|
Load a accounting (with positions) file from
|
||||||
|
@ -692,7 +699,7 @@ def load_account(
|
||||||
legacy_fn: str = f'pps.{brokername}.{acctid}.toml'
|
legacy_fn: str = f'pps.{brokername}.{acctid}.toml'
|
||||||
fn: str = f'account.{brokername}.{acctid}.toml'
|
fn: str = f'account.{brokername}.{acctid}.toml'
|
||||||
|
|
||||||
dirpath: Path = config._config_dir / 'accounting'
|
dirpath: Path = dirpath or (config._config_dir / 'accounting')
|
||||||
if not dirpath.is_dir():
|
if not dirpath.is_dir():
|
||||||
dirpath.mkdir()
|
dirpath.mkdir()
|
||||||
|
|
||||||
|
@ -743,6 +750,9 @@ def open_account(
|
||||||
acctid: str,
|
acctid: str,
|
||||||
write_on_exit: bool = False,
|
write_on_exit: bool = False,
|
||||||
|
|
||||||
|
# for testing or manual load from file
|
||||||
|
_fp: Path | None = None,
|
||||||
|
|
||||||
) -> Generator[Account, None, None]:
|
) -> Generator[Account, None, None]:
|
||||||
'''
|
'''
|
||||||
Read out broker-specific position entries from
|
Read out broker-specific position entries from
|
||||||
|
@ -751,7 +761,11 @@ def open_account(
|
||||||
'''
|
'''
|
||||||
conf: dict
|
conf: dict
|
||||||
conf_path: Path
|
conf_path: Path
|
||||||
conf, conf_path = load_account(brokername, acctid)
|
conf, conf_path = load_account(
|
||||||
|
brokername,
|
||||||
|
acctid,
|
||||||
|
dirpath=_fp,
|
||||||
|
)
|
||||||
|
|
||||||
if brokername in conf:
|
if brokername in conf:
|
||||||
log.warning(
|
log.warning(
|
||||||
|
@ -909,6 +923,7 @@ def load_account_from_ledger(
|
||||||
filter_by_ids: dict[str, list[str]] | None = None,
|
filter_by_ids: dict[str, list[str]] | None = None,
|
||||||
|
|
||||||
ledger: TransactionLedger | None = None,
|
ledger: TransactionLedger | None = None,
|
||||||
|
**kwargs,
|
||||||
|
|
||||||
) -> Account:
|
) -> Account:
|
||||||
'''
|
'''
|
||||||
|
@ -919,9 +934,10 @@ def load_account_from_ledger(
|
||||||
|
|
||||||
'''
|
'''
|
||||||
acnt: Account
|
acnt: Account
|
||||||
with open_pps(
|
with open_account(
|
||||||
brokername,
|
brokername,
|
||||||
acctname,
|
acctname,
|
||||||
|
**kwargs,
|
||||||
) as acnt:
|
) as acnt:
|
||||||
if ledger is not None:
|
if ledger is not None:
|
||||||
acnt.update_from_ledger(ledger)
|
acnt.update_from_ledger(ledger)
|
||||||
|
|
Loading…
Reference in New Issue