|
|
|
|
@@ -2,8 +2,9 @@ import asyncio
|
|
|
|
|
import logging
|
|
|
|
|
from abc import abstractmethod
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
from enum import Enum, auto
|
|
|
|
|
from typing import Optional, Sequence
|
|
|
|
|
from typing import Optional, Sequence, Union
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
from sortedcontainers import SortedList
|
|
|
|
|
@@ -99,7 +100,8 @@ def start_trigger_updates():
|
|
|
|
|
PriceLineTrigger.clear_data()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def update_balance_triggers(vault: str, token: str, balance: int):
|
|
|
|
|
async def update_balance_triggers(vault: str, token: str):
|
|
|
|
|
balance = vault_balances.get(vault, {}).get(token)
|
|
|
|
|
updates = [bt.update(balance) for bt in BalanceTrigger.by_vault_token.get((vault, token), [])]
|
|
|
|
|
await asyncio.gather(*updates)
|
|
|
|
|
|
|
|
|
|
@@ -226,20 +228,20 @@ async def has_funds(tk: TrancheKey):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def input_amount_is_sufficient(order, token_balance):
|
|
|
|
|
log.debug(f'input is sufficient? {order.min_fill_amount}')
|
|
|
|
|
# log.debug(f'input is sufficient? {order.min_fill_amount}')
|
|
|
|
|
if order.amount_is_input:
|
|
|
|
|
log.debug(f'amount is input: {token_balance} >= {order.min_fill_amount}')
|
|
|
|
|
# log.debug(f'amount is input: {token_balance} >= {order.min_fill_amount}')
|
|
|
|
|
return token_balance >= order.min_fill_amount
|
|
|
|
|
# amount is an output amount, so we need to know the price
|
|
|
|
|
price = pool_prices.get(order.pool_address)
|
|
|
|
|
log.debug(f'amount is output amount. price={price}')
|
|
|
|
|
# log.debug(f'amount is output amount. price={price}')
|
|
|
|
|
if price is None:
|
|
|
|
|
return token_balance > 0 # we don't know the price so we allow any nonzero amount to be sufficient
|
|
|
|
|
pool = await get_pool(order.pool_address)
|
|
|
|
|
price *= dec(10) ** -pool['decimals']
|
|
|
|
|
inverted = order.order.tokenIn != pool['base']
|
|
|
|
|
minimum = dec(order.min_fill_amount)*price if inverted else dec(order.min_fill_amount)/price
|
|
|
|
|
log.debug(f'order minimum amount is {order.min_fill_amount} '+ ("input" if order.amount_is_input else f"output @ {price} = {minimum} ")+f'< {token_balance} balance')
|
|
|
|
|
# log.debug(f'order minimum amount is {order.min_fill_amount} '+ ("input" if order.amount_is_input else f"output @ {price} = {minimum} ")+f'< {token_balance} balance')
|
|
|
|
|
return token_balance >= minimum
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -261,7 +263,7 @@ class BalanceTrigger (Trigger):
|
|
|
|
|
|
|
|
|
|
async def update(self, balance):
|
|
|
|
|
self.value = await input_amount_is_sufficient(self.order, balance)
|
|
|
|
|
log.debug(f'update balance {balance} was sufficient? {self.value} {self.order.key}')
|
|
|
|
|
# log.debug(f'update balance {balance} was sufficient? {self.value} {self.order.key}')
|
|
|
|
|
|
|
|
|
|
def remove(self):
|
|
|
|
|
try:
|
|
|
|
|
@@ -316,8 +318,8 @@ class TimeTrigger (Trigger):
|
|
|
|
|
if time == self._time:
|
|
|
|
|
return
|
|
|
|
|
self._time = time
|
|
|
|
|
self.remove()
|
|
|
|
|
self.update_active(time_now)
|
|
|
|
|
in_future = time_now >= time
|
|
|
|
|
self.value = in_future is self.is_start
|
|
|
|
|
|
|
|
|
|
def update_active(self, time_now: int = None, time: int = None):
|
|
|
|
|
if time_now is None:
|
|
|
|
|
@@ -599,7 +601,8 @@ class TrancheTrigger:
|
|
|
|
|
else:
|
|
|
|
|
order_log.debug(f'tranche part-filled {self.tk} in:{_amount_in} out:{_amount_out} remaining:{remaining}')
|
|
|
|
|
if self.market_order:
|
|
|
|
|
self.expire()
|
|
|
|
|
order_log.debug(f'tranche {self.tk} delayed {config.slippage_control_delay} seconds due to slippage control')
|
|
|
|
|
self.deactivate(config.slippage_control_delay)
|
|
|
|
|
self.slash_count = 0 # reset slash count
|
|
|
|
|
|
|
|
|
|
def touch(self):
|
|
|
|
|
@@ -631,15 +634,24 @@ class TrancheTrigger:
|
|
|
|
|
self.kill()
|
|
|
|
|
else:
|
|
|
|
|
delay = round(config.slash_delay_base * config.slash_delay_mul ** (self.slash_count-1))
|
|
|
|
|
self.deactivate(timestamp()+delay)
|
|
|
|
|
self.deactivate(delay)
|
|
|
|
|
|
|
|
|
|
def deactivate(self, until):
|
|
|
|
|
def deactivate(self, interval: Union[timedelta, int, float]):
|
|
|
|
|
# todo this timestamp should be consistent with the trigger time which is blockchain
|
|
|
|
|
now = current_clock.get().timestamp
|
|
|
|
|
self.deactivate_until(now + (interval.total_seconds() if isinstance(interval, timedelta) else interval))
|
|
|
|
|
|
|
|
|
|
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)}')
|
|
|
|
|
if self.activation_trigger is None:
|
|
|
|
|
self.activation_trigger = TimeTrigger.create(True, self.tk, until)
|
|
|
|
|
else:
|
|
|
|
|
self.activation_trigger.time = until
|
|
|
|
|
try:
|
|
|
|
|
del active_tranches[self.tk]
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def disable(self):
|
|
|
|
|
# permanently stop this trigger and deconstruct
|
|
|
|
|
|