Change `Position.clearsdict()` -> `.clearsitems()`
Since apparently rendering to dict from a sorted generator func clearly doesn't preserve the order when using a `dict`-comprehension.. Further, there's really no reason to strictly return a `dict`. Adjust `.calc.ppu()` to make the return value instead a `list[tuple[str, dict]]`; this results in the current df cumsum values matching the original impl and the existing `binance.paper` unit tests now passing XD Other details that fix a variety of nonsense.. - adjust all `.clearsitems()` consumers to the new list output. - use `str(pendulum.now())` in `Position.from_msg()` since adding multiples with an `unknown` str will obviously discard them, facepalm. - fix `.calc.ppu()` to NOT short circuit when `accum_size` is 0; it's been causing all sorts of incorrect size outputs in the clearing table.. lel, this is what fixed the unit test!account_tests
parent
fe78277948
commit
8a10cbf6ab
|
@ -211,9 +211,16 @@ class TransactionLedger(UserDict):
|
||||||
Return entire output from ``.iter_txns()`` in a ``dict``.
|
Return entire output from ``.iter_txns()`` in a ``dict``.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
return {
|
txns: dict[str, Transaction] = {}
|
||||||
t.tid: t for t in self.iter_txns(symcache=symcache)
|
for t in self.iter_txns(symcache=symcache):
|
||||||
}
|
|
||||||
|
if not t:
|
||||||
|
log.warning(f'{self.mod.name}:{self.account} TXN is -> {t}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
txns[t.tid] = t
|
||||||
|
|
||||||
|
return txns
|
||||||
|
|
||||||
def write_config(self) -> None:
|
def write_config(self) -> None:
|
||||||
'''
|
'''
|
||||||
|
@ -386,7 +393,7 @@ def open_trade_ledger(
|
||||||
account=account,
|
account=account,
|
||||||
mod=mod,
|
mod=mod,
|
||||||
symcache=symcache,
|
symcache=symcache,
|
||||||
tx_sort=tx_sort,
|
tx_sort=getattr(mod, 'tx_sort', tx_sort),
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
yield ledger
|
yield ledger
|
||||||
|
|
|
@ -144,12 +144,11 @@ class Position(Struct):
|
||||||
# def bep() -> float:
|
# def bep() -> float:
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
def clearsdict(self) -> dict[str, dict]:
|
def clearsitems(self) -> list[(str, dict)]:
|
||||||
clears: dict[str, dict] = ppu(
|
return ppu(
|
||||||
self.iter_by_type('clear'),
|
self.iter_by_type('clear'),
|
||||||
as_ledger=True
|
as_ledger=True
|
||||||
)
|
)
|
||||||
return clears
|
|
||||||
|
|
||||||
def iter_by_type(
|
def iter_by_type(
|
||||||
self,
|
self,
|
||||||
|
@ -195,7 +194,7 @@ class Position(Struct):
|
||||||
cumsize: float = 0
|
cumsize: float = 0
|
||||||
clears_since_zero: list[dict] = []
|
clears_since_zero: list[dict] = []
|
||||||
|
|
||||||
for tid, cleardict in self.clearsdict().items():
|
for tid, cleardict in self.clearsitems():
|
||||||
cumsize = float(
|
cumsize = float(
|
||||||
# self.mkt.quantize(cumsize + cleardict['tx'].size
|
# self.mkt.quantize(cumsize + cleardict['tx'].size
|
||||||
self.mkt.quantize(cleardict['cumsize'])
|
self.mkt.quantize(cleardict['cumsize'])
|
||||||
|
@ -295,6 +294,8 @@ class Position(Struct):
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
mkt: MktPair = self.mkt
|
mkt: MktPair = self.mkt
|
||||||
|
now_dt: pendulum.DateTime = now()
|
||||||
|
now_str: str = str(now_dt)
|
||||||
|
|
||||||
# NOTE WARNING XXX: we summarize the pos with a single
|
# NOTE WARNING XXX: we summarize the pos with a single
|
||||||
# summary transaction (for now) until we either pass THIS
|
# summary transaction (for now) until we either pass THIS
|
||||||
|
@ -303,13 +304,16 @@ class Position(Struct):
|
||||||
t = Transaction(
|
t = Transaction(
|
||||||
fqme=mkt.fqme,
|
fqme=mkt.fqme,
|
||||||
bs_mktid=mkt.bs_mktid,
|
bs_mktid=mkt.bs_mktid,
|
||||||
tid='unknown',
|
|
||||||
size=msg['size'],
|
size=msg['size'],
|
||||||
price=msg['avg_price'],
|
price=msg['avg_price'],
|
||||||
cost=0,
|
cost=0,
|
||||||
|
|
||||||
|
# NOTE: special provisions required!
|
||||||
|
# - tid needs to be unique or this txn will be ignored!!
|
||||||
|
tid=now_str,
|
||||||
|
|
||||||
# TODO: also figure out how to avoid this!
|
# TODO: also figure out how to avoid this!
|
||||||
dt=now(),
|
dt=now_dt,
|
||||||
)
|
)
|
||||||
self.add_clear(t)
|
self.add_clear(t)
|
||||||
|
|
||||||
|
@ -342,11 +346,11 @@ class Position(Struct):
|
||||||
Inserts are always done in datetime sorted order.
|
Inserts are always done in datetime sorted order.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
added: bool = False
|
# added: bool = False
|
||||||
tid: str = t.tid
|
tid: str = t.tid
|
||||||
if tid in self._events:
|
if tid in self._events:
|
||||||
log.warning(f'{t} is already added?!')
|
log.warning(f'{t} is already added?!')
|
||||||
return added
|
# return added
|
||||||
|
|
||||||
# TODO: apparently this IS possible with a dict but not
|
# TODO: apparently this IS possible with a dict but not
|
||||||
# common and probably not that beneficial unless we're also
|
# common and probably not that beneficial unless we're also
|
||||||
|
@ -390,9 +394,9 @@ class Position(Struct):
|
||||||
if self.expired():
|
if self.expired():
|
||||||
return 0.
|
return 0.
|
||||||
|
|
||||||
clears: list[dict] = list(self.clearsdict().values())
|
clears: list[(str, dict)] = self.clearsitems()
|
||||||
if clears:
|
if clears:
|
||||||
return clears[-1]['cumsize']
|
return clears[-1][1]['cumsize']
|
||||||
else:
|
else:
|
||||||
return 0.
|
return 0.
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ if TYPE_CHECKING:
|
||||||
TransactionLedger,
|
TransactionLedger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ppu(
|
def ppu(
|
||||||
clears: Iterator[Transaction],
|
clears: Iterator[Transaction],
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ def ppu(
|
||||||
# new position fields inserted alongside each entry.
|
# new position fields inserted alongside each entry.
|
||||||
as_ledger: bool = False,
|
as_ledger: bool = False,
|
||||||
|
|
||||||
) -> float:
|
) -> float | list[(str, dict)]:
|
||||||
'''
|
'''
|
||||||
Compute the "price-per-unit" price for the given non-zero sized
|
Compute the "price-per-unit" price for the given non-zero sized
|
||||||
rolling position.
|
rolling position.
|
||||||
|
@ -86,7 +87,8 @@ def ppu(
|
||||||
'''
|
'''
|
||||||
asize_h: list[float] = [] # historical accumulative size
|
asize_h: list[float] = [] # historical accumulative size
|
||||||
ppu_h: list[float] = [] # historical price-per-unit
|
ppu_h: list[float] = [] # historical price-per-unit
|
||||||
ledger: dict[str, dict] = {}
|
# ledger: dict[str, dict] = {}
|
||||||
|
ledger: list[dict] = []
|
||||||
|
|
||||||
t: Transaction
|
t: Transaction
|
||||||
for t in clears:
|
for t in clears:
|
||||||
|
@ -95,16 +97,10 @@ def ppu(
|
||||||
is_clear: bool = not isinstance(clear_price, str)
|
is_clear: bool = not isinstance(clear_price, str)
|
||||||
|
|
||||||
last_accum_size = asize_h[-1] if asize_h else 0
|
last_accum_size = asize_h[-1] if asize_h else 0
|
||||||
accum_size = last_accum_size + clear_size
|
accum_size: float = last_accum_size + clear_size
|
||||||
accum_sign = copysign(1, accum_size)
|
accum_sign = copysign(1, accum_size)
|
||||||
|
|
||||||
sign_change: bool = False
|
sign_change: bool = False
|
||||||
|
|
||||||
if accum_size == 0:
|
|
||||||
ppu_h.append(0)
|
|
||||||
asize_h.append(0)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# on transfers we normally write some non-valid
|
# on transfers we normally write some non-valid
|
||||||
# price since withdrawal to another account/wallet
|
# price since withdrawal to another account/wallet
|
||||||
# has nothing to do with inter-asset-market prices.
|
# has nothing to do with inter-asset-market prices.
|
||||||
|
@ -170,9 +166,6 @@ def ppu(
|
||||||
else:
|
else:
|
||||||
ppu = cost_basis / abs_new_size
|
ppu = cost_basis / abs_new_size
|
||||||
|
|
||||||
# ppu_h.append(ppu)
|
|
||||||
# asize_h.append(accum_size)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# TODO: for PPU we should probably handle txs out
|
# TODO: for PPU we should probably handle txs out
|
||||||
# (aka withdrawals) similarly by simply not having
|
# (aka withdrawals) similarly by simply not having
|
||||||
|
@ -185,8 +178,6 @@ def ppu(
|
||||||
# need to be updated since the ppu remains constant
|
# need to be updated since the ppu remains constant
|
||||||
# and gets weighted by the new size.
|
# and gets weighted by the new size.
|
||||||
ppu: float = ppu_h[-1] # set to previous value
|
ppu: float = ppu_h[-1] # set to previous value
|
||||||
# ppu_h.append(ppu_h[-1])
|
|
||||||
# asize_h.append(accum_size)
|
|
||||||
|
|
||||||
# extend with new rolling metric for this step
|
# extend with new rolling metric for this step
|
||||||
ppu_h.append(ppu)
|
ppu_h.append(ppu)
|
||||||
|
@ -194,13 +185,17 @@ def ppu(
|
||||||
|
|
||||||
# ledger[t.tid] = {
|
# ledger[t.tid] = {
|
||||||
# 'txn': t,
|
# 'txn': t,
|
||||||
ledger[t.tid] = t.to_dict() | {
|
# ledger[t.tid] = t.to_dict() | {
|
||||||
|
ledger.append((
|
||||||
|
t.tid,
|
||||||
|
t.to_dict() | {
|
||||||
'ppu': ppu,
|
'ppu': ppu,
|
||||||
'cumsize': accum_size,
|
'cumsize': accum_size,
|
||||||
'sign_change': sign_change,
|
'sign_change': sign_change,
|
||||||
|
|
||||||
# TODO: cum_pnl, bep
|
# TODO: cum_pnl, bep
|
||||||
}
|
}
|
||||||
|
))
|
||||||
|
|
||||||
final_ppu = ppu_h[-1] if ppu_h else 0
|
final_ppu = ppu_h[-1] if ppu_h else 0
|
||||||
# TODO: once we have etypes in all ledger entries..
|
# TODO: once we have etypes in all ledger entries..
|
||||||
|
|
Loading…
Reference in New Issue