trigger bugfixes
This commit is contained in:
@@ -81,8 +81,8 @@ class OrderTriggers:
|
||||
|
||||
def fill(self, tx: str, time: int, tranche_index, amount_in, amount_out, fee, next_activation_time):
|
||||
self.order.add_fill(tx, time, tranche_index, amount_in, amount_out, fee, next_activation_time)
|
||||
if self.triggers[tranche_index].fill(amount_in, amount_out, next_activation_time):
|
||||
self.check_complete()
|
||||
self.triggers[tranche_index].fill(amount_in, amount_out, next_activation_time)
|
||||
self.check_complete()
|
||||
|
||||
def expire_tranche(self, tranche_index):
|
||||
self.triggers[tranche_index].expire()
|
||||
@@ -119,28 +119,24 @@ async def end_trigger_updates():
|
||||
Call once after all updates have been handled. This updates the active_tranches array based on final trigger state.
|
||||
"""
|
||||
PriceLineTrigger.end_updates(current_clock.get().timestamp)
|
||||
# dirty can change
|
||||
global _dirty
|
||||
while _dirty:
|
||||
working_set = _dirty
|
||||
_dirty = set()
|
||||
for tk in working_set:
|
||||
log.debug(f'check dirty tranche {tk}')
|
||||
if _trigger_state.get(tk,0) == 0:
|
||||
# all clear for execution. add to active list with any necessary proofs
|
||||
active_tranches[tk] = PriceProof(0)
|
||||
else:
|
||||
# blocked by one or more triggers being False (nonzero mask)
|
||||
# check expiry constraint
|
||||
try:
|
||||
TrancheTrigger.all[tk].check_expire()
|
||||
except KeyError:
|
||||
pass
|
||||
# delete from active list.
|
||||
try:
|
||||
del active_tranches[tk]
|
||||
except KeyError:
|
||||
pass
|
||||
tk = _dirty.pop()
|
||||
log.debug(f'check dirty tranche {tk}')
|
||||
if _trigger_state.get(tk,0) == 0:
|
||||
# all clear for execution. add to active list with any necessary proofs
|
||||
active_tranches[tk] = PriceProof(0)
|
||||
else:
|
||||
# blocked by one or more triggers being False (nonzero mask)
|
||||
# check expiry constraint
|
||||
try:
|
||||
TrancheTrigger.all[tk].check_expire()
|
||||
except KeyError:
|
||||
pass
|
||||
# delete from active list.
|
||||
try:
|
||||
del active_tranches[tk]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def _Order__disable_triggers(order):
|
||||
@@ -187,17 +183,14 @@ class Trigger:
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
state = _trigger_state.get(self.tk)
|
||||
if state is not None and value == (state & (1 << self.position) == 0): # NOTE: state is inverted
|
||||
return
|
||||
if state is None:
|
||||
state = 0
|
||||
_dirty.add(self.tk)
|
||||
state = _trigger_state.get(self.tk, 0)
|
||||
if not value: # this conditional is inverted
|
||||
_trigger_state[self.tk] = state | (1 << self.position) # set
|
||||
else:
|
||||
_trigger_state[self.tk] = state & ~(1 << self.position) # clear
|
||||
self._value_changed()
|
||||
_dirty.add(self.tk)
|
||||
if value != (state & (1 << self.position) == 0):
|
||||
self._value_changed()
|
||||
|
||||
|
||||
def _value_changed(self): pass
|
||||
@@ -315,7 +308,7 @@ class TimeTrigger (Trigger):
|
||||
time = self._time
|
||||
next_active = time_now < time
|
||||
activate = not self.active and next_active
|
||||
log.debug(f'update_active {self} | {self.active} => {next_active} = {activate}')
|
||||
# log.debug(f'update_active {self} | {self.active} => {next_active} = {activate}')
|
||||
if activate:
|
||||
# log.debug(f'adding time trigger {self}')
|
||||
TimeTrigger.all.add(self)
|
||||
@@ -359,6 +352,7 @@ class TimeTrigger (Trigger):
|
||||
|
||||
class PriceLineTrigger (Trigger):
|
||||
by_pool: dict[str,set['PriceLineTrigger']] = defaultdict(set)
|
||||
diagonals: set['PriceLineTrigger'] = set()
|
||||
|
||||
@staticmethod
|
||||
def create(tk: TrancheKey, inverted: bool, price: dec, line: Line, is_min: bool, is_barrier: bool):
|
||||
@@ -382,7 +376,11 @@ class PriceLineTrigger (Trigger):
|
||||
self.is_barrier = is_barrier
|
||||
self.pool_address = Order.of(tk).pool_address
|
||||
self.index: Optional[int] = None
|
||||
self.active = True
|
||||
self.last_price = price_now
|
||||
PriceLineTrigger.by_pool[self.pool_address].add(self)
|
||||
if self.line.slope != 0:
|
||||
PriceLineTrigger.diagonals.add(self)
|
||||
|
||||
# lines that need evaluating add their data to these arrays, which are then sent to SIMD for evaluation. each
|
||||
# array must always have the same size as the others.
|
||||
@@ -406,21 +404,32 @@ class PriceLineTrigger (Trigger):
|
||||
# oldPrice = price
|
||||
if self.inverted:
|
||||
price = 1/price
|
||||
self.last_price = price
|
||||
log.debug(f'price trigger {price}')
|
||||
if self not in PriceLineTrigger.triggers_set:
|
||||
self.index = len(PriceLineTrigger.y)
|
||||
PriceLineTrigger.y.append(price)
|
||||
PriceLineTrigger.m.append(self.line.slope)
|
||||
PriceLineTrigger.b.append(self.line.intercept)
|
||||
PriceLineTrigger.sign.append(1 if self.is_min else -1)
|
||||
PriceLineTrigger.triggers.append(self)
|
||||
PriceLineTrigger.triggers_set.add(self)
|
||||
self.add_computation(price)
|
||||
else:
|
||||
# update an existing equation's price
|
||||
PriceLineTrigger.y[self.index] = price
|
||||
|
||||
def touch(self):
|
||||
if self not in PriceLineTrigger.triggers_set:
|
||||
self.add_computation(self.last_price)
|
||||
|
||||
def add_computation(self, price):
|
||||
self.index = len(PriceLineTrigger.y)
|
||||
PriceLineTrigger.y.append(price)
|
||||
PriceLineTrigger.m.append(self.line.slope)
|
||||
PriceLineTrigger.b.append(self.line.intercept)
|
||||
PriceLineTrigger.sign.append(1 if self.is_min else -1)
|
||||
PriceLineTrigger.triggers.append(self)
|
||||
PriceLineTrigger.triggers_set.add(self)
|
||||
|
||||
@staticmethod
|
||||
def end_updates(time: int):
|
||||
for t in PriceLineTrigger.diagonals:
|
||||
t.touch() # always evaluate any line with a slope
|
||||
|
||||
# here we use numpy to compute all dirty lines using SIMD
|
||||
y, m, b, sign = map(np.array,
|
||||
(PriceLineTrigger.y, PriceLineTrigger.m, PriceLineTrigger.b, PriceLineTrigger.sign))
|
||||
@@ -434,14 +443,19 @@ class PriceLineTrigger (Trigger):
|
||||
PriceLineTrigger.clear_data()
|
||||
|
||||
def handle_result(self, value: bool):
|
||||
if not self.is_barrier or value: # barriers that are False do not update their values to False
|
||||
if self.active and (not self.is_barrier or value): # barriers that are False do not update their values to False
|
||||
self.value = value
|
||||
|
||||
def remove(self):
|
||||
self.active = False
|
||||
try:
|
||||
PriceLineTrigger.by_pool[self.pool_address].remove(self)
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
PriceLineTrigger.diagonals.remove(self)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
async def activate_orders():
|
||||
@@ -501,7 +515,7 @@ class TrancheTrigger:
|
||||
inverted = order.order.inverted
|
||||
min_trigger = PriceLineTrigger.create(tk, inverted, price, tranche.minLine, True, tranche.minIsBarrier)
|
||||
max_trigger = PriceLineTrigger.create(tk, inverted, price, tranche.maxLine, False, tranche.maxIsBarrier)
|
||||
return TrancheTrigger(order, tk, balance_trigger, activation_trigger, expiration_trigger, min_trigger, max_trigger)
|
||||
return TrancheTrigger(order, tk, balance_trigger, activation_trigger, expiration_trigger, min_trigger, max_trigger, tranche.marketOrder)
|
||||
|
||||
def __init__(self, order: Order, tk: TrancheKey,
|
||||
balance_trigger: BalanceTrigger,
|
||||
@@ -509,6 +523,7 @@ class TrancheTrigger:
|
||||
expiration_trigger: Optional[TimeTrigger],
|
||||
min_trigger: Optional[PriceLineTrigger],
|
||||
max_trigger: Optional[PriceLineTrigger],
|
||||
market_order: bool,
|
||||
):
|
||||
assert order.key.vault == tk.vault and order.key.order_index == tk.order_index
|
||||
tranche = order.order.tranches[tk.tranche_index]
|
||||
@@ -521,6 +536,7 @@ class TrancheTrigger:
|
||||
self.expiration_trigger = expiration_trigger
|
||||
self.min_trigger = min_trigger
|
||||
self.max_trigger = max_trigger
|
||||
self.market_order = market_order
|
||||
|
||||
self.slippage = tranche.minLine.intercept if tranche.marketOrder else 0
|
||||
self.slash_count = 0
|
||||
@@ -556,8 +572,9 @@ class TrancheTrigger:
|
||||
self.disable()
|
||||
else:
|
||||
order_log.debug(f'tranche part-filled {self.tk} in:{_amount_in} out:{_amount_out} remaining:{remaining}')
|
||||
if self.market_order:
|
||||
self.expire()
|
||||
self.slash_count = 0 # reset slash count
|
||||
return filled
|
||||
|
||||
def touch(self):
|
||||
_dirty.add(self.tk)
|
||||
|
||||
Reference in New Issue
Block a user