Implement updates and write to config: `pps.toml`
Begins the position tracking incremental update API which supports both constructing a `pps.toml` both from trade ledgers as well diff-oriented incremental update from an existing config assumed to be previously generated from some prior ledger. New set of routines includes: - `_split_active()` a helper to split a position table into the active and closed positions (aka pps of size 0) for determining entry updates in the `pps.toml`. - `update_pps_conf()` to maybe load a `pps.toml` and update it from an input trades ledger including necessary (de)serialization to and from `Position` object form(s). - `load_pps_from_ledger()` a ledger parser-loader which constructs a table of pps strictly from the broker-account ledger data without any consideration for any existing pps file. Each "entry" in `pps.toml` also contains a `fills: list` attr (name may change) which references the set of trade records which make up its state since the last net-zero position in the instrument.lifo_pps_ib
parent
2a641ab8b4
commit
dd05ed1371
119
piker/pp.py
119
piker/pp.py
|
@ -144,24 +144,28 @@ class Position(Struct):
|
||||||
|
|
||||||
def update_pps(
|
def update_pps(
|
||||||
brokername: str,
|
brokername: str,
|
||||||
ledger: dict[str, Union[str, float]],
|
records: dict[str, TradeRecord],
|
||||||
pps: Optional[dict[str, TradeRecord]] = None
|
|
||||||
|
|
||||||
) -> dict[str, TradeRecord]:
|
pps: Optional[dict[str, Position]] = None
|
||||||
|
|
||||||
|
) -> dict[str, Position]:
|
||||||
'''
|
'''
|
||||||
Compile a set of positions from a trades ledger.
|
Compile a set of positions from a trades ledger.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
brokermod = get_brokermod(brokername)
|
|
||||||
|
|
||||||
pps: dict[str, Position] = pps or {}
|
pps: dict[str, Position] = pps or {}
|
||||||
records = brokermod.norm_trade_records(ledger)
|
|
||||||
|
# lifo update all pps from records
|
||||||
for r in records:
|
for r in records:
|
||||||
key = r.symkey or r.fqsn
|
key = r.symkey or r.fqsn
|
||||||
pp = pps.setdefault(
|
pp = pps.setdefault(
|
||||||
key,
|
key,
|
||||||
Position(
|
Position(
|
||||||
Symbol.from_fqsn(r.fqsn, info={}),
|
Symbol.from_fqsn(
|
||||||
|
r.fqsn,
|
||||||
|
info={},
|
||||||
|
),
|
||||||
size=0.0,
|
size=0.0,
|
||||||
avg_price=0.0,
|
avg_price=0.0,
|
||||||
)
|
)
|
||||||
|
@ -171,10 +175,30 @@ def update_pps(
|
||||||
pp.lifo_update(r.size, r.price)
|
pp.lifo_update(r.size, r.price)
|
||||||
pp.fills.append(r.tid)
|
pp.fills.append(r.tid)
|
||||||
|
|
||||||
|
assert len(set(pp.fills)) == len(pp.fills)
|
||||||
return pps
|
return pps
|
||||||
|
|
||||||
|
|
||||||
async def load_pps_from_ledger(
|
def _split_active(
|
||||||
|
pps: dict[str, Position],
|
||||||
|
|
||||||
|
) -> tuple[dict, dict]:
|
||||||
|
|
||||||
|
active = {}
|
||||||
|
closed = {}
|
||||||
|
|
||||||
|
for k, pp in pps.items():
|
||||||
|
fqsn = pp.symbol.front_fqsn()
|
||||||
|
asdict = pp.to_dict()
|
||||||
|
if pp.size == 0:
|
||||||
|
closed[fqsn] = asdict
|
||||||
|
else:
|
||||||
|
active[fqsn] = asdict
|
||||||
|
|
||||||
|
return active, closed
|
||||||
|
|
||||||
|
|
||||||
|
def load_pps_from_ledger(
|
||||||
|
|
||||||
brokername: str,
|
brokername: str,
|
||||||
acctname: str,
|
acctname: str,
|
||||||
|
@ -187,31 +211,72 @@ async def load_pps_from_ledger(
|
||||||
) as ledger:
|
) as ledger:
|
||||||
pass # readonly
|
pass # readonly
|
||||||
|
|
||||||
pps = update_pps(brokername, ledger)
|
brokermod = get_brokermod(brokername)
|
||||||
|
records = brokermod.norm_trade_records(ledger)
|
||||||
active_pps = {}
|
pps = update_pps(
|
||||||
for k, pp in pps.items():
|
brokername,
|
||||||
|
records,
|
||||||
if pp.size == 0:
|
)
|
||||||
continue
|
return _split_active(pps)
|
||||||
|
|
||||||
active_pps[pp.symbol.front_fqsn()] = pp.to_dict()
|
|
||||||
# pprint({pp.symbol.front_fqsn(): pp.to_dict() for k, pp in pps.items()})
|
|
||||||
|
|
||||||
from pprint import pprint
|
|
||||||
pprint(active_pps)
|
|
||||||
# pprint({pp.symbol.front_fqsn(): pp.to_dict() for k, pp in pps.items()})
|
|
||||||
|
|
||||||
|
|
||||||
def update_pps_conf(
|
def update_pps_conf(
|
||||||
trade_records: list[TradeRecord],
|
brokername: str,
|
||||||
|
acctid: str,
|
||||||
|
trade_records: Optional[list[TradeRecord]] = None,
|
||||||
):
|
):
|
||||||
conf, path = config.load('pp')
|
conf, path = config.load('pps')
|
||||||
|
brokersection = conf.setdefault(brokername, {})
|
||||||
|
entries = brokersection.setdefault(acctid, {})
|
||||||
|
|
||||||
|
if not entries:
|
||||||
|
|
||||||
|
# no pps entry yet for this broker/account
|
||||||
|
active, closed = load_pps_from_ledger(
|
||||||
|
brokername,
|
||||||
|
acctid,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif trade_records:
|
||||||
|
|
||||||
|
# table for map-back to object form
|
||||||
|
pps = {}
|
||||||
|
|
||||||
|
# load ``pps.toml`` config entries back into object form.
|
||||||
|
for fqsn, entry in entries.items():
|
||||||
|
pps[fqsn] = Position(
|
||||||
|
Symbol.from_fqsn(fqsn, info={}),
|
||||||
|
size=entry['size'],
|
||||||
|
avg_price=entry['avg_price'],
|
||||||
|
)
|
||||||
|
|
||||||
|
pps = update_pps(
|
||||||
|
brokername,
|
||||||
|
trade_records,
|
||||||
|
pps=pps,
|
||||||
|
)
|
||||||
|
active, closed = _split_active(pps)
|
||||||
|
|
||||||
|
for fqsn in closed:
|
||||||
|
print(f'removing closed pp: {fqsn}')
|
||||||
|
entries.pop(fqsn, None)
|
||||||
|
|
||||||
|
for fqsn, pp_dict in active.items():
|
||||||
|
print(f'Updating active pp: {fqsn}')
|
||||||
|
|
||||||
|
# normalize to a simpler flat dict format
|
||||||
|
_ = pp_dict.pop('symbol')
|
||||||
|
entries[fqsn.rstrip(f'.{brokername}')] = pp_dict
|
||||||
|
|
||||||
|
config.write(
|
||||||
|
conf,
|
||||||
|
'pps',
|
||||||
|
encoder=config.toml.Encoder(preserve=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(conf)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import trio
|
update_pps_conf('ib', 'algopaper')
|
||||||
trio.run(
|
|
||||||
load_pps_from_ledger, 'ib', 'algopaper',
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in New Issue