3131from . import config
3232from .clearing ._messages import BrokerdPosition , Status
3333from .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
3650class 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
176213if __name__ == '__main__' :
177- parse_pps ('ib' , 'algopaper' )
214+ import trio
215+ trio .run (
216+ load_pps_from_ledger , 'ib' , 'algopaper' ,
217+ )
0 commit comments