From abc5c382ae4dde81bd323fccd4de080a4d663028 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sat, 28 Aug 2021 12:58:08 -0400 Subject: [PATCH] Use a better exit slot heuristic When exiting a pp toward net-zero, we may sometimes run into the issue of having a "fractional slot" worth of units in allocator limit terms. This is further nuanced by live orders which are submitted above the current clearing price which get allocated a size (based on that staged but non-cleared price) according to their limit size unit which can be calculated to be less then the size that would have been allocated at the actual clearing price. In the short term cope with this discrepancy by simply using a "slot and a half" as the decision point of whether to exit a slot's worth or the remaining pp's worth of units. In other words if you can exit 1.5x a slot's worth or less, exit the remaining pp, otherwise exit a slot's worth. This is a stop gap until we have a better solution to limiting staged orders to (some range around) the currently computed clear-able price. --- piker/ui/_position.py | 44 ++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/piker/ui/_position.py b/piker/ui/_position.py index 3b8d19ba..8b1e716e 100644 --- a/piker/ui/_position.py +++ b/piker/ui/_position.py @@ -147,17 +147,18 @@ class Allocator(BaseModel): sym = self.symbol ld = sym.lot_size_digits + size_unit = self.size_unit live_size = live_pp.size abs_live_size = abs(live_size) abs_startup_size = abs(startup_pp.size) u_per_slot, currency_per_slot = self.step_sizes() - if self.size_unit == 'units': + if size_unit == 'units': enter_step = u_per_slot l_sub_pp = self.units_limit - abs_live_size - elif self.size_unit == 'currency': + elif size_unit == 'currency': live_cost_basis = abs_live_size * live_pp.avg_price enter_step = currency_per_slot / price l_sub_pp = (self.currency_limit - live_cost_basis) / price @@ -173,26 +174,41 @@ class Allocator(BaseModel): # an exit (removing-from or going to net-zero pp) else: - # when exiting a pp we always slot the position + # when exiting a pp we always try to slot the position # in the instrument's units, since doing so in a derived # size measure (eg. currency value, percent of port) would # result in a mis-mapping of slots sizes in unit terms # (i.e. it would take *more* slots to exit at a profit and # *less* slots to exit at a loss). - slot_size = abs_startup_size / self.slots + pp_size = max(abs_startup_size, abs_live_size) + slotted_pp = pp_size / self.slots + + if size_unit == 'currency': + # compute the "projected" limit's worth of units at the + # current pp (weighted) price: + slot_size = currency_per_slot / live_pp.avg_price + + else: + slot_size = u_per_slot + + # if our position is greater then our limit setting + # we'll want to use slot sizes which are larger then what + # the limit would normally determine + order_size = max(slotted_pp, slot_size) if ( abs_live_size < slot_size or - slot_size < abs_live_size < (2*slot_size) - ): - # the remaining pp is in between 0-2 slots - # so dump the whole position in this last exit - # therefore conducting so called "back loading" - # but **without** going past a net-zero pp. - order_size = abs_live_size - else: - order_size = slot_size + # NOTE: front/back "loading" heurstic: + # if the remaining pp is in between 0-1.5x a slot's + # worth, dump the whole position in this last exit + # therefore conducting so called "back loading" but + # **without** going past a net-zero pp. if the pp is + # > 1.5x a slot size, then front load: exit a slot's and + # expect net-zero to be acquired on the final exit. + slot_size < pp_size < round((1.5*slot_size), ndigits=ld) + ): + order_size = abs_live_size return { 'size': abs(round(order_size, ndigits=ld)), @@ -385,6 +401,8 @@ class SettingsPane: # TODO: what should we do for fractional slot pps? self.fill_bar.set_slots( slots, + + # TODO: how to show "partial" slots? # min(round(prop * slots), slots) min(ceil(prop * slots), slots) )