Skip to content

Commit 45bef0c

Browse files
committed
Add non-state-incremented calculation methods
Since we're going to need them anyway for desired features, add 2 new `Position` methods: - `.calc_be_price()` which computes the breakeven cost basis price from the entries in the clears table. - `.calc_size()` which just sums the clear sizes. Add a `cost_scalar: float` control to the `.update_from_trans()` method to allow manual adjustment of the cost weighting for the case where a "non-symmetrical" model is wanted. Go back to always trying to write the backing ledger files on exit, even when there's an error (obvs without the `return` in the `finally:` block f$#% up).
1 parent a3d46f7 commit 45bef0c

File tree

1 file changed

+94
-36
lines changed

1 file changed

+94
-36
lines changed

piker/pp.py

+94-36
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,18 @@ def open_trade_ledger(
8383
print(f'Ledger load took {time.time() - start}s')
8484
cpy = ledger.copy()
8585

86-
yield cpy
86+
try:
87+
yield cpy
88+
finally:
89+
if cpy != ledger:
90+
# TODO: show diff output?
91+
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
92+
print(f'Updating ledger for {tradesfile}:\n')
93+
ledger.update(cpy)
8794

88-
if cpy != ledger:
89-
# TODO: show diff output?
90-
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
91-
print(f'Updating ledger for {tradesfile}:\n')
92-
ledger.update(cpy)
93-
94-
# we write on close the mutated ledger data
95-
with open(tradesfile, 'w') as cf:
96-
toml.dump(ledger, cf)
95+
# we write on close the mutated ledger data
96+
with open(tradesfile, 'w') as cf:
97+
toml.dump(ledger, cf)
9798

9899

99100
class Transaction(Struct, frozen=True):
@@ -276,6 +277,62 @@ def lifo_update(
276277

277278
return new_size, self.be_price
278279

280+
def calc_be_price(self) -> float:
281+
282+
size: float = 0
283+
cb_tot_size: float = 0
284+
cost_basis: float = 0
285+
be_price: float = 0
286+
287+
for tid, entry in self.clears.items():
288+
clear_size = entry['size']
289+
clear_price = entry['price']
290+
new_size = size + clear_size
291+
292+
# old size minus the new size gives us size diff with
293+
# +ve -> increase in pp size
294+
# -ve -> decrease in pp size
295+
size_diff = abs(new_size) - abs(size)
296+
297+
if new_size == 0:
298+
cost_basis = 0
299+
cb_tot_size = 0
300+
be_price = 0
301+
302+
elif size_diff > 0:
303+
# only an increaze in size of the position contributes
304+
# the breakeven price, a decrease does not.
305+
306+
cost_basis += (
307+
# weighted price per unit of
308+
clear_price * abs(clear_size)
309+
+
310+
# transaction cost
311+
(copysign(1, new_size) * entry['cost'] * 2)
312+
)
313+
cb_tot_size += abs(clear_size)
314+
be_price = cost_basis / cb_tot_size
315+
316+
size = new_size
317+
318+
# print(
319+
# f'cb: {cost_basis}\n'
320+
# f'size: {size}\n'
321+
# f'clear_size: {clear_size}\n'
322+
# f'clear_price: {clear_price}\n\n'
323+
324+
# f'cb_tot_size: {cb_tot_size}\n'
325+
# f'be_price: {be_price}\n\n'
326+
# )
327+
328+
return be_price
329+
330+
def calc_size(self) -> float:
331+
size: float = 0
332+
for tid, entry in self.clears.items():
333+
size += entry['size']
334+
return size
335+
279336
def minimize_clears(
280337
self,
281338

@@ -310,6 +367,8 @@ class PpTable(Struct):
310367
def update_from_trans(
311368
self,
312369
trans: dict[str, Transaction],
370+
cost_scalar: float = 2,
371+
313372
) -> dict[str, Position]:
314373

315374
pps = self.pps
@@ -354,7 +413,7 @@ def update_from_trans(
354413
# and presume the worst case of the same cost
355414
# to exit this transaction (even though in reality
356415
# it will be dynamic based on exit stratetgy).
357-
cost=2*r.cost,
416+
cost=cost_scalar*r.cost,
358417
)
359418

360419
# track clearing data
@@ -764,31 +823,30 @@ def open_pps(
764823
clears=clears,
765824
)
766825

767-
yield table
768-
769-
if not write_on_exit:
770-
return
771-
772-
# TODO: show diff output?
773-
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
774-
print(f'Updating ``pps.toml`` for {path}:\n')
775-
776-
pp_entries, closed_pp_objs = table.dump_active(brokername)
777-
conf[brokername][acctid] = pp_entries
778-
779-
# TODO: why tf haven't they already done this for inline
780-
# tables smh..
781-
enc = PpsEncoder(preserve=True)
782-
# table_bs_type = type(toml.TomlDecoder().get_empty_inline_table())
783-
enc.dump_funcs[
784-
toml.decoder.InlineTableDict
785-
] = enc.dump_inline_table
786-
787-
config.write(
788-
conf,
789-
'pps',
790-
encoder=enc,
791-
)
826+
try:
827+
yield table
828+
finally:
829+
if write_on_exit:
830+
# TODO: show diff output?
831+
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
832+
print(f'Updating ``pps.toml`` for {path}:\n')
833+
834+
pp_entries, closed_pp_objs = table.dump_active(brokername)
835+
conf[brokername][acctid] = pp_entries
836+
837+
# TODO: why tf haven't they already done this for inline
838+
# tables smh..
839+
enc = PpsEncoder(preserve=True)
840+
# table_bs_type = type(toml.TomlDecoder().get_empty_inline_table())
841+
enc.dump_funcs[
842+
toml.decoder.InlineTableDict
843+
] = enc.dump_inline_table
844+
845+
config.write(
846+
conf,
847+
'pps',
848+
encoder=enc,
849+
)
792850

793851

794852
def update_pps_conf(

0 commit comments

Comments
 (0)