From 5d24b5defbbc5a4e4dd5cc034e93b7c9309485d7 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 31 Jul 2023 17:32:49 -0400 Subject: [PATCH] Swap branch order for enter/exit Also fix bug since we always need to reset cum_pos_pnl after a `exit_to_zero` case. --- piker/accounting/calc.py | 90 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/piker/accounting/calc.py b/piker/accounting/calc.py index 79e47850..c60a0db9 100644 --- a/piker/accounting/calc.py +++ b/piker/accounting/calc.py @@ -511,6 +511,7 @@ def open_ledger_dfs( last_cumsize: float = 0 last_ledger_pnl: float = 0 last_pos_pnl: float = 0 + last_is_enter: bool = False # imperatively compute the PPU (price per unit) and BEP # (break even price) iteratively over the ledger, oriented @@ -519,7 +520,59 @@ def open_ledger_dfs( cumsize: float = row['cumsize'] is_enter: bool = row['is_enter'] - if not is_enter: + # ALWAYS reset per-position cum PnL + if last_cumsize == 0: + last_pos_pnl: float = 0 + + # a "position size INCREASING" transaction which "makes + # larger", in src asset unit terms, the trade's + # side-size of the destination asset: + # - "buying" (more) units of the dst asset + # - "selling" (more short) units of the dst asset + if is_enter: + + ppu: float = ( + ( + (last_ppu * last_cumsize) + + + (row['price'] * row['size']) + ) / + cumsize + ) + + pos_bep: float = ppu + # When we "enter more" dst asset units (increase + # position state) AFTER having exitted some units + # the bep needs to be RECOMPUTED based on new ppu + # such that liquidation of the cumsize at the bep + # price results in a zero-pnl for the existing + # position (since the last one). + if ( + not last_is_enter + and last_cumsize != 0 + ): + + pos_bep: float = ( + ( + (ppu * cumsize) + - + last_pos_pnl + ) + / + cumsize + ) + + df[i, 'ledger_bep'] = df[i, 'pos_bep'] = pos_bep + + # a "position size DECREASING" transaction which "makes + # smaller" the trade's side-size of the destination + # asset: + # - selling previously bought units of the dst asset + # (aka 'closing' a long position). + # - buying previously borrowed and sold (short) units + # of the dst asset (aka 'covering'/'closing' a short + # position). + else: pnl = df[i, 'per_exit_pnl'] = ( (last_ppu - row['price']) @@ -529,11 +582,7 @@ def open_ledger_dfs( last_ledger_pnl = df[i, 'cum_ledger_pnl'] = last_ledger_pnl + pnl - # reset per-position cum PnL - if last_cumsize != 0: - last_pos_pnl = df[i, 'cum_pos_pnl'] = last_pos_pnl + pnl - else: - last_pos_pnl: float = 0 + last_pos_pnl = df[i, 'cum_pos_pnl'] = last_pos_pnl + pnl if cumsize == 0: ppu: float = 0 @@ -541,6 +590,7 @@ def open_ledger_dfs( else: ppu: float = last_ppu + if abs(cumsize) > 0: # compute the "break even price" that # when the remaining cumsize is liquidated at @@ -564,33 +614,9 @@ def open_ledger_dfs( ) df[i, 'pos_bep'] = pos_bep - elif is_enter: - - ppu: float = ( - ( - (last_ppu * last_cumsize) - + - (row['price'] * row['size']) - ) - / - cumsize - ) - - last_ppu: float = ppu - - # TODO: case where we "enter more" dst asset units - # (increase position state) -> the bep needs to be - # recomputed based on new ppu.. - pos_bep: float = ( - ( - (ppu * cumsize) - - - last_pos_pnl - ) / cumsize - ) - df[i, 'ledger_bep'] = df[i, 'pos_bep'] = pos_bep - df[i, 'pos_ppu'] = ppu + last_ppu: float = ppu last_cumsize: float = cumsize + last_is_enter: bool = is_enter yield dfs, ledger