diff --git a/src/dexorder/base/orderlib.py b/src/dexorder/base/orderlib.py index 38a5d92..33dbef5 100644 --- a/src/dexorder/base/orderlib.py +++ b/src/dexorder/base/orderlib.py @@ -283,6 +283,8 @@ DISTANT_FUTURE = 4294967295 # max uint32 MAX_FRACTION = 65535 # max uint16 +MIN_SLIPPAGE = 0.0001 # one bip + @dataclass class Tranche: diff --git a/src/dexorder/order/executionhandler.py b/src/dexorder/order/executionhandler.py index f7fdb94..1ad8e4c 100644 --- a/src/dexorder/order/executionhandler.py +++ b/src/dexorder/order/executionhandler.py @@ -3,10 +3,11 @@ from dataclasses import dataclass from typing import Optional, Union, Any from uuid import UUID +from triton.profiler import deactivate from web3.exceptions import ContractPanicError, ContractLogicError from web3.types import EventData -from dexorder import db, metric +from dexorder import db, metric, config from dexorder.accounting import accounting_transaction_gas from dexorder.base import TransactionReceiptDict, TransactionRequest, transaction_request_deserializers from dexorder.base.order import TrancheKey, OrderKey @@ -121,6 +122,11 @@ async def finish_execution_request(tk: TrancheKey, error: Optional[str]=None): if trig is not None: trig.touch() + def delay(secs=None): + trig = get_trigger() + if trig is not None: + trig.deactivate(secs if secs is not None else config.slippage_control_delay) + if error is None: metric.executions.inc() else: @@ -162,6 +168,7 @@ async def finish_execution_request(tk: TrancheKey, error: Optional[str]=None): retry() elif error == 'RL': log.debug(f'tranche {tk} execution failed due to "RL" rate limit') + delay() retry() elif error == 'TE': log.debug(f'tranche {tk} execution failed due to "TE" too early') diff --git a/src/dexorder/order/triggers.py b/src/dexorder/order/triggers.py index 69e2d33..51df348 100644 --- a/src/dexorder/order/triggers.py +++ b/src/dexorder/order/triggers.py @@ -9,7 +9,7 @@ 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 +from dexorder.base.orderlib import SwapOrderState, PriceProof, DISTANT_FUTURE, DISTANT_PAST, Line, MIN_SLIPPAGE from dexorder.blockstate import BlockDict from .orderstate import Order from .. import dec, order_log, timestamp, from_timestamp, config @@ -528,15 +528,17 @@ 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) + return TrancheTrigger(order, tk, balance_trigger, activation_trigger, expiration_trigger, min_trigger, max_trigger, tranche.marketOrder, slippage) def __init__(self, order: Order, tk: TrancheKey, balance_trigger: BalanceTrigger, @@ -544,7 +546,7 @@ class TrancheTrigger: expiration_trigger: Optional[TimeTrigger], min_trigger: Optional[PriceLineTrigger], max_trigger: Optional[PriceLineTrigger], - market_order: bool, + market_order: bool, slippage: float, ): assert order.key.vault == tk.vault and order.key.order_index == tk.order_index tranche = order.order.tranches[tk.tranche_index] @@ -564,6 +566,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.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 \