From 41a1e2d9fe3174bf7d8e9814ae7bd9e4cc63c414 Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 28 Mar 2025 20:05:52 -0400 Subject: [PATCH] MIN_SLIPPAGE epsilon leeway --- src/dexorder/base/orderlib.py | 1 + src/dexorder/order/triggers.py | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/dexorder/base/orderlib.py b/src/dexorder/base/orderlib.py index 33dbef5..e17c932 100644 --- a/src/dexorder/base/orderlib.py +++ b/src/dexorder/base/orderlib.py @@ -284,6 +284,7 @@ DISTANT_FUTURE = 4294967295 # max uint32 MAX_FRACTION = 65535 # max uint16 MIN_SLIPPAGE = 0.0001 # one bip +MIN_SLIPPAGE_EPSILON = 0.000000000003 @dataclass diff --git a/src/dexorder/order/triggers.py b/src/dexorder/order/triggers.py index 51df348..c1dde4b 100644 --- a/src/dexorder/order/triggers.py +++ b/src/dexorder/order/triggers.py @@ -9,7 +9,8 @@ from typing import Optional, Sequence, Union import numpy as np from sortedcontainers import SortedList -from dexorder.base.orderlib import SwapOrderState, PriceProof, DISTANT_FUTURE, DISTANT_PAST, Line, MIN_SLIPPAGE +from dexorder.base.orderlib import SwapOrderState, PriceProof, DISTANT_FUTURE, DISTANT_PAST, Line, MIN_SLIPPAGE, \ + MIN_SLIPPAGE_EPSILON from dexorder.blockstate import BlockDict from .orderstate import Order from .. import dec, order_log, timestamp, from_timestamp, config @@ -72,6 +73,10 @@ class OrderTriggers: def open(self): return not self.closed + @property + def error(self): + return any(t.error for t in self.triggers) + def check_complete(self): if self.closed: final_state = SwapOrderState.Filled if self.order.remaining == 0 or self.order.remaining < self.order.min_fill_amount else SwapOrderState.Expired @@ -503,7 +508,8 @@ async def activate_order(order: Order): triggers = await OrderTriggers.create(order) if triggers.closed: log.debug(f'order {order.key} was immediately closed') - final_state = SwapOrderState.Filled if order.remaining == 0 or order.remaining < order.min_fill_amount \ + final_state = SwapOrderState.Error if triggers.error \ + else SwapOrderState.Filled if order.remaining == 0 or order.remaining < order.min_fill_amount \ else SwapOrderState.Expired order.complete(final_state) @@ -528,17 +534,15 @@ class TrancheTrigger: activation_trigger = TimeTrigger.create(True, tk, ts.activationTime, time) expiration_trigger = TimeTrigger.create(False, tk, ts.endTime, time) if tranche.marketOrder: - slippage = tranche.minLine.intercept min_trigger = max_trigger = None else: # tranche minLine and maxLine are relative to the pool and will be flipped from the orderspec if the # order is buying the base and selling the quote. price = pool_prices[pool['address']] * dec(10) ** -pool['decimals'] inverted = order.order.inverted - slippage = 0 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, tranche.marketOrder, slippage) + 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, @@ -546,7 +550,7 @@ class TrancheTrigger: expiration_trigger: Optional[TimeTrigger], min_trigger: Optional[PriceLineTrigger], max_trigger: Optional[PriceLineTrigger], - market_order: bool, slippage: float, + market_order: bool, ): assert order.key.vault == tk.vault and order.key.order_index == tk.order_index tranche = order.order.tranches[tk.tranche_index] @@ -566,7 +570,7 @@ class TrancheTrigger: tranche_remaining = tranche.fraction_of(order.amount) - order.tranche_filled(self.tk.tranche_index) self.status = \ - TrancheState.Error if self.market_order and slippage < MIN_SLIPPAGE else \ + TrancheState.Error if self.market_order and self.slippage < MIN_SLIPPAGE - MIN_SLIPPAGE_EPSILON else \ TrancheState.Filled if tranche_remaining == 0 or tranche_remaining < self.order.min_fill_amount else \ TrancheState.Expired if self.expiration_trigger is not None and not self.expiration_trigger else \ TrancheState.Early if self.activation_trigger is None and not self.activation_trigger else \ @@ -646,11 +650,13 @@ class TrancheTrigger: def deactivate_until(self, until): # Temporarily deactivate the tranche due to a rate limit. Use disable() to permanently halt the trigger. - log.debug(f'deactivating tranche {self.tk} until {from_timestamp(until)}') + now = current_clock.get().timestamp + if until < now: + return if self.activation_trigger is None: self.activation_trigger = TimeTrigger.create(True, self.tk, until) else: - self.activation_trigger.time = until + self.activation_trigger.time = max(until, self.activation_trigger.time) try: del active_tranches[self.tk] except KeyError: @@ -692,6 +698,10 @@ class TrancheTrigger: def open(self): return not self.closed + @property + def error(self): + return self.status == TrancheState.Error + def __str__(self): trigs = [] if self.balance_trigger is not None: