Support re-processing a filtered ledger entry set
This makes it possible to refresh a single fqsn-position in one's `pps.toml` by simply deleting the file entry, in which case, if there is new trade records passed to `load_pps_from_toml()` via the new `reload_records` kwarg, then the backend ledger entries matching that symbol will be filtered and used to recompute a fresh position. This turns out to be super handy when you have crashes that prevent a `pps.toml` entry from being updated correctly but where the ledger does have all the data necessary to calculate a fresh correct entry.lifo_pps_ib
							parent
							
								
									f32b4d37cb
								
							
						
					
					
						commit
						4fdfb81876
					
				
							
								
								
									
										116
									
								
								piker/pp.py
								
								
								
								
							
							
						
						
									
										116
									
								
								piker/pp.py
								
								
								
								
							|  | @ -33,6 +33,7 @@ from typing import ( | |||
| from msgspec import Struct | ||||
| import pendulum | ||||
| from pendulum import datetime, now | ||||
| # import tomli | ||||
| import toml | ||||
| 
 | ||||
| from . import config | ||||
|  | @ -357,6 +358,9 @@ def load_pps_from_ledger( | |||
|     brokername: str, | ||||
|     acctname: str, | ||||
| 
 | ||||
|     # post normalization filter on ledger entries to be processed | ||||
|     filter_by: Optional[list[Transaction]] = None, | ||||
| 
 | ||||
| ) -> dict[str, Position]: | ||||
|     ''' | ||||
|     Open a ledger file by broker name and account and read in and | ||||
|  | @ -369,14 +373,17 @@ def load_pps_from_ledger( | |||
|         brokername, | ||||
|         acctname, | ||||
|     ) as ledger: | ||||
|         pass  # readonly | ||||
| 
 | ||||
|     if not ledger: | ||||
|         # null case, no ledger file with content | ||||
|         return {} | ||||
|         if not ledger: | ||||
|             # null case, no ledger file with content | ||||
|             return {} | ||||
| 
 | ||||
|     brokermod = get_brokermod(brokername) | ||||
|     records = brokermod.norm_trade_records(ledger) | ||||
| 
 | ||||
|     if filter_by: | ||||
|         bsuids = set(r.bsuid for r in filter_by) | ||||
|         records = filter(lambda r: r.bsuid in bsuids, records) | ||||
| 
 | ||||
|     return update_pps(records) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -551,7 +558,15 @@ class PpsEncoder(toml.TomlEncoder): | |||
| 
 | ||||
| def load_pps_from_toml( | ||||
|     brokername: str, | ||||
|     acctid: str | ||||
|     acctid: str, | ||||
| 
 | ||||
|     # XXX: there is an edge case here where we may want to either audit | ||||
|     # the retrieved ``pps.toml`` output or reprocess it since there was | ||||
|     # an error on write on the last attempt to update the state file | ||||
|     # even though the ledger *was* updated. For this cases we allow the | ||||
|     # caller to pass in a symbol set they'd like to reload from the | ||||
|     # underlying ledger to be reprocessed in computing pps state. | ||||
|     reload_records: Optional[list[Transaction]] = None, | ||||
| 
 | ||||
| ) -> tuple[dict, dict[str, Position]]: | ||||
|     ''' | ||||
|  | @ -563,6 +578,7 @@ def load_pps_from_toml( | |||
|     conf, path = config.load('pps') | ||||
|     brokersection = conf.setdefault(brokername, {}) | ||||
|     pps = brokersection.setdefault(acctid, {}) | ||||
|     pp_objs = {} | ||||
| 
 | ||||
|     if not pps: | ||||
|         # no pps entry yet for this broker/account so parse | ||||
|  | @ -571,46 +587,59 @@ def load_pps_from_toml( | |||
|             brokername, | ||||
|             acctid, | ||||
|         ) | ||||
|         if not pps: | ||||
|             log.warning( | ||||
|                 f'No trade history could be loaded for {brokername}:{acctid}' | ||||
|             ) | ||||
| 
 | ||||
|     else: | ||||
|         # unmarshal/load ``pps.toml`` config entries into object form. | ||||
|         pp_objs = {} | ||||
|         for fqsn, entry in pps.items(): | ||||
|     # Reload symbol specific ledger entries if requested by the | ||||
|     # caller **AND** none exist in the current pps state table. | ||||
|     elif ( | ||||
|         pps and reload_records and | ||||
|         not any(r.fqsn in pps for r in reload_records) | ||||
|     ): | ||||
|         # no pps entry yet for this broker/account so parse | ||||
|         # any available ledgers to build a pps state. | ||||
|         pp_objs = load_pps_from_ledger( | ||||
|             brokername, | ||||
|             acctid, | ||||
|             filter_by=reload_records, | ||||
|         ) | ||||
| 
 | ||||
|             # convert clears sub-tables (only in this form | ||||
|             # for toml re-presentation) back into a master table. | ||||
|             clears_list = entry['clears'] | ||||
|     if not pps: | ||||
|         log.warning( | ||||
|             f'No trade history could be loaded for {brokername}:{acctid}' | ||||
|         ) | ||||
| 
 | ||||
|             # index clears entries in "object" form by tid in a top | ||||
|             # level dict instead of a list (as is presented in our | ||||
|             # ``pps.toml``). | ||||
|             clears = {} | ||||
|             for clears_table in clears_list: | ||||
|                 tid = clears_table.pop('tid') | ||||
|                 clears[tid] = clears_table | ||||
|     # unmarshal/load ``pps.toml`` config entries into object form. | ||||
|     for fqsn, entry in pps.items(): | ||||
| 
 | ||||
|             expiry = entry.get('expiry') | ||||
|             if expiry: | ||||
|                 expiry = pendulum.parse(expiry) | ||||
|         # convert clears sub-tables (only in this form | ||||
|         # for toml re-presentation) back into a master table. | ||||
|         clears_list = entry['clears'] | ||||
| 
 | ||||
|             pp_objs[fqsn] = Position( | ||||
|                 Symbol.from_fqsn(fqsn, info={}), | ||||
|                 size=entry['size'], | ||||
|                 be_price=entry['be_price'], | ||||
|                 expiry=expiry, | ||||
|                 bsuid=entry['bsuid'], | ||||
|         # index clears entries in "object" form by tid in a top | ||||
|         # level dict instead of a list (as is presented in our | ||||
|         # ``pps.toml``). | ||||
|         clears = {} | ||||
|         for clears_table in clears_list: | ||||
|             tid = clears_table.pop('tid') | ||||
|             clears[tid] = clears_table | ||||
| 
 | ||||
|                 # XXX: super critical, we need to be sure to include | ||||
|                 # all pps.toml clears to avoid reusing clears that were | ||||
|                 # already included in the current incremental update | ||||
|                 # state, since today's records may have already been | ||||
|                 # processed! | ||||
|                 clears=clears, | ||||
|             ) | ||||
|         expiry = entry.get('expiry') | ||||
|         if expiry: | ||||
|             expiry = pendulum.parse(expiry) | ||||
| 
 | ||||
|         pp_objs[fqsn] = Position( | ||||
|             Symbol.from_fqsn(fqsn, info={}), | ||||
|             size=entry['size'], | ||||
|             be_price=entry['be_price'], | ||||
|             expiry=expiry, | ||||
|             bsuid=entry['bsuid'], | ||||
| 
 | ||||
|             # XXX: super critical, we need to be sure to include | ||||
|             # all pps.toml clears to avoid reusing clears that were | ||||
|             # already included in the current incremental update | ||||
|             # state, since today's records may have already been | ||||
|             # processed! | ||||
|             clears=clears, | ||||
|         ) | ||||
| 
 | ||||
|     return conf, pp_objs | ||||
| 
 | ||||
|  | @ -618,12 +647,17 @@ def load_pps_from_toml( | |||
| def update_pps_conf( | ||||
|     brokername: str, | ||||
|     acctid: str, | ||||
| 
 | ||||
|     trade_records: Optional[list[Transaction]] = None, | ||||
|     key_by: Optional[str] = None, | ||||
| 
 | ||||
| ) -> dict[str, Position]: | ||||
| 
 | ||||
|     conf, pp_objs = load_pps_from_toml(brokername, acctid) | ||||
|     conf, pp_objs = load_pps_from_toml( | ||||
|         brokername, | ||||
|         acctid, | ||||
|         reload_records=trade_records, | ||||
|     ) | ||||
| 
 | ||||
|     # update all pp objects from any (new) trade records which | ||||
|     # were passed in (aka incremental update case). | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue