Skip to content

Commit 8ded483

Browse files
committed
Extend trade-record tools, add ledger to pps extraction
Add a `TradeRecord` struct which holds the minimal field set to build out position entries. Add `.update_pps()` to convert a set of records into LIFO position entries, optionally allowing for an update to some existing pp input set. Add `load_pps_from_ledger()` which does a full ledger extraction to pp objects, ready for writing a `pps.toml`.
1 parent 227506d commit 8ded483

File tree

1 file changed

+82
-42
lines changed

1 file changed

+82
-42
lines changed

piker/pp.py

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@
3131
from . import config
3232
from .clearing._messages import BrokerdPosition, Status
3333
from .data._source import Symbol
34+
from .brokers import get_brokermod
35+
36+
37+
class TradeRecord(Struct):
38+
fqsn: str # normally fqsn
39+
tid: Union[str, int]
40+
size: float
41+
price: float
42+
cost: float # commisions or other additional costs
43+
44+
# optional key normally derived from the broker
45+
# backend which ensures the instrument-symbol this record
46+
# is for is truly unique.
47+
symkey: Optional[Union[str, int]] = None
3448

3549

3650
class Position(Struct):
@@ -47,7 +61,13 @@ class Position(Struct):
4761
avg_price: float # TODO: contextual pricing
4862

4963
# ordered record of known constituent trade messages
50-
fills: list[Status] = []
64+
fills: list[Union[str, int, Status]] = []
65+
66+
def to_dict(self):
67+
return {
68+
f: getattr(self, f)
69+
for f in self.__struct_fields__
70+
}
5171

5272
def update_from_msg(
5373
self,
@@ -122,56 +142,76 @@ def lifo_update(
122142
return new_size, self.avg_price
123143

124144

125-
def parse_pps(
145+
def update_pps(
146+
brokername: str,
147+
ledger: dict[str, Union[str, float]],
148+
pps: Optional[dict[str, TradeRecord]] = None
149+
150+
) -> dict[str, TradeRecord]:
151+
'''
152+
Compile a set of positions from a trades ledger.
153+
154+
'''
155+
brokermod = get_brokermod(brokername)
156+
157+
pps: dict[str, Position] = pps or {}
158+
records = brokermod.norm_trade_records(ledger)
159+
for r in records:
160+
key = r.symkey or r.fqsn
161+
pp = pps.setdefault(
162+
key,
163+
Position(
164+
Symbol.from_fqsn(r.fqsn, info={}),
165+
size=0.0,
166+
avg_price=0.0,
167+
)
168+
)
169+
170+
# lifo style average price calc
171+
pp.lifo_update(r.size, r.price)
172+
pp.fills.append(r.tid)
173+
174+
return pps
175+
176+
177+
async def load_pps_from_ledger(
126178

127179
brokername: str,
128180
acctname: str,
129181

130-
ledger: Optional[dict[str, Union[str, float]]] = None,
131-
132182
) -> dict[str, Any]:
133183

134-
pps: dict[str, Position] = {}
135-
136-
if not ledger:
137-
with config.open_trade_ledger(
138-
brokername,
139-
acctname,
140-
) as ledger:
141-
pass # readonly
142-
143-
by_date = ledger[brokername]
144-
145-
for date, by_id in by_date.items():
146-
for tid, record in by_id.items():
147-
148-
# ib specific record parsing
149-
# date, time = record['dateTime']
150-
# conid = record['condid']
151-
# cost = record['cost']
152-
# comms = record['ibCommission']
153-
symbol = record['symbol']
154-
price = record['tradePrice']
155-
# action = record['buySell']
156-
157-
# NOTE: can be -ve on sells
158-
size = float(record['quantity'])
159-
160-
pp = pps.setdefault(
161-
symbol,
162-
Position(
163-
Symbol(key=symbol),
164-
size=0.0,
165-
avg_price=0.0,
166-
)
167-
)
184+
with config.open_trade_ledger(
185+
brokername,
186+
acctname,
187+
) as ledger:
188+
pass # readonly
168189

169-
# LOFI style average price calc
170-
pp.lifo_update(size, price)
190+
pps = update_pps(brokername, ledger)
191+
192+
active_pps = {}
193+
for k, pp in pps.items():
194+
195+
if pp.size == 0:
196+
continue
197+
198+
active_pps[pp.symbol.front_fqsn()] = pp.to_dict()
199+
# pprint({pp.symbol.front_fqsn(): pp.to_dict() for k, pp in pps.items()})
171200

172201
from pprint import pprint
173-
pprint(pps)
202+
pprint(active_pps)
203+
# pprint({pp.symbol.front_fqsn(): pp.to_dict() for k, pp in pps.items()})
204+
205+
206+
def update_pps_conf(
207+
trade_records: list[TradeRecord],
208+
):
209+
conf, path = config.load('pp')
210+
174211

175212

176213
if __name__ == '__main__':
177-
parse_pps('ib', 'algopaper')
214+
import trio
215+
trio.run(
216+
load_pps_from_ledger, 'ib', 'algopaper',
217+
)

0 commit comments

Comments
 (0)