Skip to content

Commit

Permalink
Extend trade-record tools, add ledger to pps extraction
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
goodboy committed Jun 10, 2022
1 parent 227506d commit 8ded483
Showing 1 changed file with 82 additions and 42 deletions.
124 changes: 82 additions & 42 deletions piker/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@
from . import config
from .clearing._messages import BrokerdPosition, Status
from .data._source import Symbol
from .brokers import get_brokermod


class TradeRecord(Struct):
fqsn: str # normally fqsn
tid: Union[str, int]
size: float
price: float
cost: float # commisions or other additional costs

# optional key normally derived from the broker
# backend which ensures the instrument-symbol this record
# is for is truly unique.
symkey: Optional[Union[str, int]] = None


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

# ordered record of known constituent trade messages
fills: list[Status] = []
fills: list[Union[str, int, Status]] = []

def to_dict(self):
return {
f: getattr(self, f)
for f in self.__struct_fields__
}

def update_from_msg(
self,
Expand Down Expand Up @@ -122,56 +142,76 @@ def lifo_update(
return new_size, self.avg_price


def parse_pps(
def update_pps(
brokername: str,
ledger: dict[str, Union[str, float]],
pps: Optional[dict[str, TradeRecord]] = None

) -> dict[str, TradeRecord]:
'''
Compile a set of positions from a trades ledger.
'''
brokermod = get_brokermod(brokername)

pps: dict[str, Position] = pps or {}
records = brokermod.norm_trade_records(ledger)
for r in records:
key = r.symkey or r.fqsn
pp = pps.setdefault(
key,
Position(
Symbol.from_fqsn(r.fqsn, info={}),
size=0.0,
avg_price=0.0,
)
)

# lifo style average price calc
pp.lifo_update(r.size, r.price)
pp.fills.append(r.tid)

return pps


async def load_pps_from_ledger(

brokername: str,
acctname: str,

ledger: Optional[dict[str, Union[str, float]]] = None,

) -> dict[str, Any]:

pps: dict[str, Position] = {}

if not ledger:
with config.open_trade_ledger(
brokername,
acctname,
) as ledger:
pass # readonly

by_date = ledger[brokername]

for date, by_id in by_date.items():
for tid, record in by_id.items():

# ib specific record parsing
# date, time = record['dateTime']
# conid = record['condid']
# cost = record['cost']
# comms = record['ibCommission']
symbol = record['symbol']
price = record['tradePrice']
# action = record['buySell']

# NOTE: can be -ve on sells
size = float(record['quantity'])

pp = pps.setdefault(
symbol,
Position(
Symbol(key=symbol),
size=0.0,
avg_price=0.0,
)
)
with config.open_trade_ledger(
brokername,
acctname,
) as ledger:
pass # readonly

# LOFI style average price calc
pp.lifo_update(size, price)
pps = update_pps(brokername, ledger)

active_pps = {}
for k, pp in pps.items():

if pp.size == 0:
continue

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(pps)
pprint(active_pps)
# pprint({pp.symbol.front_fqsn(): pp.to_dict() for k, pp in pps.items()})


def update_pps_conf(
trade_records: list[TradeRecord],
):
conf, path = config.load('pp')



if __name__ == '__main__':
parse_pps('ib', 'algopaper')
import trio
trio.run(
load_pps_from_ledger, 'ib', 'algopaper',
)

0 comments on commit 8ded483

Please sign in to comment.