def setUp(self) -> None:
        super().setUp()
        self.log_records = []

        self.connector = ConnectorBase()
        self.connector._set_current_timestamp(1640000000.0)
        self.tracker = ClientOrderTracker(connector=self.connector)

        self.tracker.logger().setLevel(1)
        self.tracker.logger().addHandler(self)
Example #2
0
    def setUp(self) -> None:
        super().setUp()
        self.log_records = []

        self.connector = MockExchange()
        self.connector._set_current_timestamp(1640000000.0)
        self.tracker = ClientOrderTracker(connector=self.connector)

        self.tracker.logger().setLevel(1)
        self.tracker.logger().addHandler(self)

        self._initialize_event_loggers()
    def __init__(
        self,
        client_config_map: "ClientConfigAdapter",
        bitmex_api_key: str = None,
        bitmex_api_secret: str = None,
        trading_pairs: Optional[List[str]] = None,
        trading_required: bool = True,
        domain: str = CONSTANTS.DOMAIN,
    ):

        self._bitmex_time_synchronizer = TimeSynchronizer()
        self._auth: BitmexAuth = BitmexAuth(api_key=bitmex_api_key,
                                            api_secret=bitmex_api_secret)

        self._trading_pairs = trading_pairs
        self._trading_required = trading_required
        self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        self._domain = domain
        self._api_factory = web_utils.build_api_factory(auth=self._auth)
        self._rest_assistant: Optional[RESTAssistant] = None
        self._ws_assistant: Optional[WSAssistant] = None

        ExchangeBase.__init__(self, client_config_map=client_config_map)

        self._user_stream_tracker = BitmexUserStreamTracker(
            auth=self._auth,
            domain=self._domain,
            throttler=self._throttler,
            api_factory=self._api_factory,
            time_synchronizer=self._bitmex_time_synchronizer)
        self._order_book_tracker = BitmexOrderBookTracker(
            trading_pairs=trading_pairs,
            domain=self._domain,
            throttler=self._throttler,
            api_factory=self._api_factory)
        self._ev_loop = asyncio.get_event_loop()
        self._poll_notifier = asyncio.Event()
        self._order_not_found_records = defaultdict(int)
        self._last_timestamp = 0
        self._trading_rules = {}
        self._in_flight_orders = {}
        self._status_polling_task = None
        self._user_stream_event_listener_task = None
        self._trading_rules_polling_task = None
        self._user_stream_tracker_task = None
        self._last_poll_timestamp = 0
        self._client_order_tracker: ClientOrderTracker = ClientOrderTracker(
            connector=self)
        self._trading_pair_to_multipliers = {}
        self._trading_pair_price_estimate_for_quantize = {}
        self._token_multiplier = {}
Example #4
0
 def __init__(self,
              binance_api_key: str,
              binance_api_secret: str,
              trading_pairs: Optional[List[str]] = None,
              trading_required: bool = True,
              domain: str = CONSTANTS.DEFAULT_DOMAIN,
              ):
     self._domain = domain
     self._binance_time_synchronizer = TimeSynchronizer()
     super().__init__()
     self._trading_required = trading_required
     self._auth = BinanceAuth(
         api_key=binance_api_key,
         secret_key=binance_api_secret,
         time_provider=self._binance_time_synchronizer)
     self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
     self._api_factory = web_utils.build_api_factory(
         throttler=self._throttler,
         time_synchronizer=self._binance_time_synchronizer,
         domain=self._domain,
         auth=self._auth)
     self._rest_assistant = None
     self._order_book_tracker = OrderBookTracker(
         data_source=BinanceAPIOrderBookDataSource(
             trading_pairs=trading_pairs,
             domain=self._domain,
             api_factory=self._api_factory,
             throttler=self._throttler),
         trading_pairs=trading_pairs,
         domain=self._domain)
     self._user_stream_tracker = UserStreamTracker(
         data_source=BinanceAPIUserStreamDataSource(
             auth=self._auth,
             domain=self._domain,
             throttler=self._throttler,
             api_factory=self._api_factory,
             time_synchronizer=self._binance_time_synchronizer))
     self._ev_loop = asyncio.get_event_loop()
     self._poll_notifier = asyncio.Event()
     self._last_timestamp = 0
     self._order_not_found_records = {}  # Dict[client_order_id:str, count:int]
     self._trading_rules = {}  # Dict[trading_pair:str, TradingRule]
     self._trade_fees = {}  # Dict[trading_pair:str, (maker_fee_percent:Decimal, taken_fee_percent:Decimal)]
     self._last_update_trade_fees_timestamp = 0
     self._status_polling_task = None
     self._user_stream_tracker_task = None
     self._user_stream_event_listener_task = None
     self._trading_rules_polling_task = None
     self._last_poll_timestamp = 0
     self._last_trades_poll_binance_timestamp = 0
     self._order_tracker: ClientOrderTracker = ClientOrderTracker(connector=self)
Example #5
0
    def __init__(self,
                 kucoin_api_key: str,
                 kucoin_passphrase: str,
                 kucoin_secret_key: str,
                 trading_pairs: Optional[List[str]] = None,
                 trading_required: bool = True,
                 domain: str = CONSTANTS.DEFAULT_DOMAIN):

        self._domain = domain
        self._time_synchronizer = TimeSynchronizer()
        super().__init__()
        self._auth = KucoinAuth(
            api_key=kucoin_api_key,
            passphrase=kucoin_passphrase,
            secret_key=kucoin_secret_key,
            time_provider=self._time_synchronizer)
        self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        self._api_factory = web_utils.build_api_factory(
            throttler=self._throttler,
            time_synchronizer=self._time_synchronizer,
            domain=self._domain,
            auth=self._auth)
        self._last_poll_timestamp = 0
        self._last_timestamp = 0
        self._trading_pairs = trading_pairs
        self._order_book_tracker = OrderBookTracker(
            data_source=KucoinAPIOrderBookDataSource(
                trading_pairs=trading_pairs,
                domain=self._domain,
                api_factory=self._api_factory,
                throttler=self._throttler,
                time_synchronizer=self._time_synchronizer),
            trading_pairs=trading_pairs,
            domain=self._domain)
        self._user_stream_tracker = UserStreamTracker(
            data_source=KucoinAPIUserStreamDataSource(
                domain=self._domain,
                api_factory=self._api_factory,
                throttler=self._throttler))
        self._poll_notifier = asyncio.Event()
        self._status_polling_task = None
        self._user_stream_tracker_task = None
        self._user_stream_event_listener_task = None
        self._trading_rules_polling_task = None
        self._trading_fees_polling_task = None
        self._trading_required = trading_required
        self._trading_rules = {}
        self._trading_fees = {}
        self._order_tracker: ClientOrderTracker = ClientOrderTracker(connector=self)
Example #6
0
 def __init__(
     self,
     client_config_map: "ClientConfigAdapter",
     bybit_api_key: str,
     bybit_api_secret: str,
     trading_pairs: Optional[List[str]] = None,
     trading_required: bool = True,
     domain: str = CONSTANTS.DEFAULT_DOMAIN,
 ):
     self._domain = domain
     self._time_synchronizer = TimeSynchronizer()
     super().__init__(client_config_map)
     self._trading_required = trading_required
     self._auth = BybitAuth(api_key=bybit_api_key,
                            secret_key=bybit_api_secret,
                            time_provider=self._time_synchronizer)
     self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
     self._api_factory = web_utils.build_api_factory(
         throttler=self._throttler,
         time_synchronizer=self._time_synchronizer,
         domain=self._domain,
         auth=self._auth)
     self._rest_assistant = None
     self._order_book_tracker = OrderBookTracker(
         data_source=BybitAPIOrderBookDataSource(
             trading_pairs=trading_pairs,
             domain=self._domain,
             api_factory=self._api_factory,
             throttler=self._throttler),
         trading_pairs=trading_pairs,
         domain=self._domain)
     self._user_stream_tracker = UserStreamTracker(
         data_source=BybitAPIUserStreamDataSource(
             auth=self._auth,
             domain=self._domain,
             throttler=self._throttler,
             api_factory=self._api_factory,
             time_synchronizer=self._time_synchronizer))
     self._poll_notifier = asyncio.Event()
     self._last_timestamp = 0
     self._trading_rules = {}
     self._trade_fees = {}
     self._status_polling_task = None
     self._user_stream_tracker_task = None
     self._user_stream_event_listener_task = None
     self._trading_rules_polling_task = None
     self._last_poll_timestamp = 0
     self._order_tracker: ClientOrderTracker = ClientOrderTracker(
         connector=self)
    def test_cached_order_ttl_exceeded(self):
        tracker = ClientOrderTracker(self.connector)
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            price=Decimal("1.0"),
        )
        tracker._cached_orders[order.client_order_id] = order

        self.ev_loop.run_until_complete(asyncio.sleep(0.2))

        self.assertNotIn(order.client_order_id, tracker.cached_orders)
Example #8
0
 def __init__(
         self,
         client_config_map: "ClientConfigAdapter",
         connector_name: str,
         chain: str,
         network: str,
         wallet_address: str,
         trading_pairs: List[str] = [],
         additional_spenders: List[str] = [],  # not implemented
         trading_required: bool = True):
     """
     :param connector_name: name of connector on gateway
     :param chain: refers to a block chain, e.g. ethereum or avalanche
     :param network: refers to a network of a particular blockchain e.g. mainnet or kovan
     :param wallet_address: the address of the eth wallet which has been added on gateway
     :param trading_pairs: a list of trading pairs
     :param trading_required: Whether actual trading is needed. Useful for some functionalities or commands like the balance command
     """
     self._connector_name = connector_name
     self._name = "_".join([connector_name, chain, network])
     super().__init__(client_config_map)
     self._chain = chain
     self._network = network
     self._trading_pairs = trading_pairs
     self._tokens = set()
     [
         self._tokens.update(set(trading_pair.split("-")))
         for trading_pair in trading_pairs
     ]
     self._wallet_address = wallet_address
     self._trading_required = trading_required
     self._ev_loop = asyncio.get_event_loop()
     self._last_poll_timestamp = 0.0
     self._last_balance_poll_timestamp = time.time()
     self._last_est_gas_cost_reported = 0
     self._allowances = {}
     self._chain_info = {}
     self._status_polling_task = None
     self._get_chain_info_task = None
     self._auto_approve_task = None
     self._get_gas_estimate_task = None
     self._poll_notifier = None
     self._native_currency = None
     self._network_transaction_fee: Optional[TokenAmount] = None
     self._order_tracker: ClientOrderTracker = ClientOrderTracker(
         connector=self)
    def __init__(
            self,
            ascend_ex_api_key: str,
            ascend_ex_secret_key: str,
            trading_pairs: Optional[List[str]] = None,
            trading_required: bool = True,
    ):
        """
        :param ascend_ex_api_key: The API key to connect to private AscendEx APIs.
        :param ascend_ex_secret_key: The API secret.
        :param trading_pairs: The market trading pairs which to track order book data.
        :param trading_required: Whether actual trading is needed.
        """
        super().__init__()
        self._trading_required = trading_required
        self._trading_pairs = trading_pairs
        self._shared_client = aiohttp.ClientSession()
        self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        self._order_book_tracker = AscendExOrderBookTracker(
            shared_client=self._shared_client, throttler=self._throttler, trading_pairs=self._trading_pairs
        )
        self._ascend_ex_auth = AscendExAuth(ascend_ex_api_key, ascend_ex_secret_key)
        self._user_stream_tracker = AscendExUserStreamTracker(
            shared_client=self._shared_client,
            throttler=self._throttler,
            ascend_ex_auth=self._ascend_ex_auth,
            trading_pairs=self._trading_pairs,
        )
        self._poll_notifier = asyncio.Event()
        self._last_timestamp = 0
        self._trading_rules = {}  # Dict[trading_pair:str, AscendExTradingRule]
        self._status_polling_task = None
        self._user_stream_tracker_task = None
        self._user_stream_event_listener_task = None
        self._trading_rules_polling_task = None
        self._last_poll_timestamp = 0
        self._account_group = None  # required in order to make post requests
        self._account_uid = None  # required in order to produce deterministic order ids
        self._throttler = AsyncThrottler(rate_limits=CONSTANTS.RATE_LIMITS)

        self._in_flight_order_tracker: ClientOrderTracker = ClientOrderTracker(connector=self)
        self._order_without_exchange_id_records = {}
Example #10
0
 def __init__(self,
              coinflex_api_key: str,
              coinflex_api_secret: str,
              trading_pairs: Optional[List[str]] = None,
              trading_required: bool = True,
              domain: str = CONSTANTS.DEFAULT_DOMAIN):
     self._domain = domain
     super().__init__()
     self._trading_required = trading_required
     self._auth = CoinflexAuth(api_key=coinflex_api_key,
                               secret_key=coinflex_api_secret)
     self._throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
     self._api_factory = web_utils.build_api_factory(auth=self._auth)
     self._order_book_tracker = CoinflexOrderBookTracker(
         trading_pairs=trading_pairs,
         domain=domain,
         api_factory=self._api_factory,
         throttler=self._throttler)
     self._user_stream_tracker = CoinflexUserStreamTracker(
         auth=self._auth,
         domain=domain,
         throttler=self._throttler,
         api_factory=self._api_factory)
     self._ev_loop = asyncio.get_event_loop()
     self._poll_notifier = asyncio.Event()
     self._last_timestamp = 0
     self._order_not_found_records = {
     }  # Dict[client_order_id:str, count:int]
     self._trading_rules = {}  # Dict[trading_pair:str, TradingRule]
     self._trade_fees = {
     }  # Dict[trading_pair:str, (maker_fee_percent:Decimal, taken_fee_percent:Decimal)]
     self._last_update_trade_fees_timestamp = 0
     self._status_polling_task = None
     self._user_stream_event_listener_task = None
     self._trading_rules_polling_task = None
     self._last_poll_timestamp = 0
     self._last_trades_poll_coinflex_timestamp = 0
     self._order_tracker: ClientOrderTracker = ClientOrderTracker(
         connector=self)
Example #11
0
    def __init__(self, client_config_map: "ClientConfigAdapter"):
        super().__init__(client_config_map)

        self._last_poll_timestamp = 0
        self._last_timestamp = 0
        self._trading_rules = {}
        self._trading_fees = {}

        self._status_polling_task = None
        self._user_stream_tracker_task = None
        self._user_stream_event_listener_task = None
        self._trading_rules_polling_task = None
        self._trading_fees_polling_task = None

        self._time_synchronizer = TimeSynchronizer()
        self._throttler = AsyncThrottler(self.rate_limits_rules)
        self._poll_notifier = asyncio.Event()

        # init Auth and Api factory
        self._auth: AuthBase = self.authenticator
        self._web_assistants_factory: WebAssistantsFactory = self._create_web_assistants_factory(
        )

        # init OrderBook Data Source and Tracker
        self._orderbook_ds: OrderBookTrackerDataSource = self._create_order_book_data_source(
        )
        self._set_order_book_tracker(
            OrderBookTracker(data_source=self._orderbook_ds,
                             trading_pairs=self.trading_pairs,
                             domain=self.domain))

        # init UserStream Data Source and Tracker
        self._userstream_ds = self._create_user_stream_data_source()
        self._user_stream_tracker = UserStreamTracker(
            data_source=self._userstream_ds)

        self._order_tracker: ClientOrderTracker = ClientOrderTracker(
            connector=self)
class ClientOrderTrackerUnitTest(unittest.TestCase):
    # logging.Level required to receive logs from the exchange
    level = 0

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        cls.ev_loop = asyncio.get_event_loop()

        cls.base_asset = "COINALPHA"
        cls.quote_asset = "HBOT"
        cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}"
        cls.trade_fee_percent = Decimal("0.001")

    def setUp(self) -> None:
        super().setUp()
        self.log_records = []

        self.connector = MockExchange(
            client_config_map=ClientConfigAdapter(ClientConfigMap()))
        self.connector._set_current_timestamp(1640000000.0)
        self.tracker = ClientOrderTracker(connector=self.connector)

        self.tracker.logger().setLevel(1)
        self.tracker.logger().addHandler(self)

        self._initialize_event_loggers()

    def _initialize_event_loggers(self):
        self.buy_order_completed_logger = EventLogger()
        self.buy_order_created_logger = EventLogger()
        self.order_cancelled_logger = EventLogger()
        self.order_failure_logger = EventLogger()
        self.order_filled_logger = EventLogger()
        self.sell_order_completed_logger = EventLogger()
        self.sell_order_created_logger = EventLogger()

        events_and_loggers = [
            (MarketEvent.BuyOrderCompleted, self.buy_order_completed_logger),
            (MarketEvent.BuyOrderCreated, self.buy_order_created_logger),
            (MarketEvent.OrderCancelled, self.order_cancelled_logger),
            (MarketEvent.OrderFailure, self.order_failure_logger),
            (MarketEvent.OrderFilled, self.order_filled_logger),
            (MarketEvent.SellOrderCompleted, self.sell_order_completed_logger),
            (MarketEvent.SellOrderCreated, self.sell_order_created_logger)
        ]

        for event, logger in events_and_loggers:
            self.connector.add_listener(event, logger)

    def handle(self, record):
        self.log_records.append(record)

    def _is_logged(self, log_level: str, message: str) -> bool:
        return any(
            record.levelname == log_level and record.getMessage() == message
            for record in self.log_records)

    def async_run_with_timeout(self, coroutine: Awaitable, timeout: float = 1):
        ret = self.ev_loop.run_until_complete(
            asyncio.wait_for(coroutine, timeout))
        return ret

    def test_start_tracking_order(self):
        self.assertEqual(0, len(self.tracker.active_orders))

        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        self.tracker.start_tracking_order(order)

        self.assertEqual(1, len(self.tracker.active_orders))

    def test_stop_tracking_order(self):
        self.assertEqual(0, len(self.tracker.active_orders))

        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)
        self.assertEqual(1, len(self.tracker.active_orders))

        self.tracker.stop_tracking_order(order.client_order_id)

        self.assertEqual(0, len(self.tracker.active_orders))
        self.assertEqual(1, len(self.tracker.cached_orders))

    def test_cached_order_max_cache_size(self):
        for i in range(ClientOrderTracker.MAX_CACHE_SIZE + 1):
            order: InFlightOrder = InFlightOrder(
                client_order_id=f"someClientOrderId_{i}",
                trading_pair=self.trading_pair,
                order_type=OrderType.LIMIT,
                trade_type=TradeType.BUY,
                amount=Decimal("1000.0"),
                creation_timestamp=1640001112.0,
                price=Decimal("1.0"),
            )
            self.tracker._cached_orders[order.client_order_id] = order

        self.assertEqual(ClientOrderTracker.MAX_CACHE_SIZE,
                         len(self.tracker.cached_orders))

        # First entry gets removed when the no. of cached order exceeds MAX_CACHE_SIZE
        self.assertNotIn("someClientOrderId_0", self.tracker._cached_orders)

    def test_cached_order_ttl_not_exceeded(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker._cached_orders[order.client_order_id] = order

        self.assertIn(order.client_order_id, self.tracker._cached_orders)

    @patch(
        "hummingbot.connector.client_order_tracker.ClientOrderTracker.CACHED_ORDER_TTL",
        0.1)
    def test_cached_order_ttl_exceeded(self):
        tracker = ClientOrderTracker(self.connector)
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        tracker._cached_orders[order.client_order_id] = order

        self.ev_loop.run_until_complete(asyncio.sleep(0.2))

        self.assertNotIn(order.client_order_id, tracker.cached_orders)

    def test_fetch_tracked_order_not_found(self):
        self.assertIsNone(
            self.tracker.fetch_tracked_order("someNonExistantOrderId"))

    def test_fetch_tracked_order(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)
        self.assertEqual(1, len(self.tracker.active_orders))

        fetched_order: InFlightOrder = self.tracker.fetch_tracked_order(
            order.client_order_id)

        self.assertTrue(fetched_order == order)

    def test_fetch_cached_order_not_found(self):
        self.assertIsNone(
            self.tracker.fetch_cached_order("someNonExistantOrderId"))

    def test_fetch_cached_order(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker._cached_orders[order.client_order_id] = order
        self.assertEqual(1, len(self.tracker.cached_orders))

        fetched_order: InFlightOrder = self.tracker.fetch_cached_order(
            order.client_order_id)

        self.assertTrue(fetched_order == order)

    def test_fetch_order_by_client_order_id(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)
        self.assertEqual(1, len(self.tracker.active_orders))

        fetched_order: InFlightOrder = self.tracker.fetch_order(
            order.client_order_id)

        self.assertTrue(fetched_order == order)

    def test_fetch_order_by_exchange_order_id(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)
        self.assertEqual(1, len(self.tracker.active_orders))

        fetched_order: InFlightOrder = self.tracker.fetch_order(
            exchange_order_id=order.exchange_order_id)

        self.assertTrue(fetched_order == order)

    def test_fetch_order_does_not_match_orders_with_undefined_exchange_id(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)
        self.assertEqual(1, len(self.tracker.active_orders))

        fetched_order = self.tracker.fetch_order("invalid_order_id")

        self.assertIsNone(fetched_order)

    def test_process_order_update_invalid_order_update(self):

        order_creation_update: OrderUpdate = OrderUpdate(
            # client_order_id="someClientOrderId",  # client_order_id intentionally omitted
            # exchange_order_id="someExchangeOrderId",  # client_order_id intentionally omitted
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(update_future)

        self.assertTrue(
            self._is_logged(
                "ERROR",
                "OrderUpdate does not contain any client_order_id or exchange_order_id",
            ))

    def test_process_order_update_order_not_found(self):

        order_creation_update: OrderUpdate = OrderUpdate(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(update_future)

        self.assertTrue(
            self._is_logged(
                "DEBUG",
                f"Order is not/no longer being tracked ({order_creation_update})",
            ))

    def test_process_order_update_trigger_order_creation_event(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)

        order_creation_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(update_future)

        updated_order: InFlightOrder = self.tracker.fetch_tracked_order(
            order.client_order_id)

        # Check order update has been successfully applied
        self.assertEqual(updated_order.exchange_order_id,
                         order_creation_update.exchange_order_id)
        self.assertTrue(updated_order.exchange_order_id_update_event.is_set())
        self.assertEqual(updated_order.current_state,
                         order_creation_update.new_state)
        self.assertTrue(updated_order.is_open)

        # Check that Logger has logged the correct log
        self.assertTrue(
            self._is_logged(
                "INFO",
                f"Created {order.order_type.name} {order.trade_type.name} order {order.client_order_id} for "
                f"{order.amount} {order.trading_pair}.",
            ))

        # Check that Buy/SellOrderCreatedEvent has been triggered.
        self.assertEqual(1, len(self.buy_order_created_logger.event_log))
        event_logged = self.buy_order_created_logger.event_log[0]
        self.assertEqual(event_logged.amount, order.amount)
        self.assertEqual(event_logged.exchange_order_id,
                         order_creation_update.exchange_order_id)
        self.assertEqual(event_logged.order_id, order.client_order_id)
        self.assertEqual(event_logged.price, order.price)
        self.assertEqual(event_logged.trading_pair, order.trading_pair)
        self.assertEqual(event_logged.type, order.order_type)

    def test_process_order_update_trigger_order_creation_event_without_client_order_id(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id=
            "someExchangeOrderId",  # exchange_order_id is provided when initialized. See AscendEx.
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)

        order_creation_update: OrderUpdate = OrderUpdate(
            # client_order_id=order.client_order_id,  # client_order_id purposefully ommited
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(update_future)

        updated_order: InFlightOrder = self.tracker.fetch_tracked_order(
            order.client_order_id)

        # Check order update has been successfully applied
        self.assertEqual(updated_order.exchange_order_id,
                         order_creation_update.exchange_order_id)
        self.assertTrue(updated_order.exchange_order_id_update_event.is_set())
        self.assertEqual(updated_order.current_state,
                         order_creation_update.new_state)
        self.assertTrue(updated_order.is_open)

        # Check that Logger has logged the correct log
        self.assertTrue(
            self._is_logged(
                "INFO",
                f"Created {order.order_type.name} {order.trade_type.name} order {order.client_order_id} for "
                f"{order.amount} {order.trading_pair}.",
            ))

        # Check that Buy/SellOrderCreatedEvent has been triggered.
        self.assertEqual(1, len(self.buy_order_created_logger.event_log))
        event_logged = self.buy_order_created_logger.event_log[0]

        self.assertEqual(event_logged.amount, order.amount)
        self.assertEqual(event_logged.exchange_order_id,
                         order_creation_update.exchange_order_id)
        self.assertEqual(event_logged.order_id, order.client_order_id)
        self.assertEqual(event_logged.price, order.price)
        self.assertEqual(event_logged.trading_pair, order.trading_pair)
        self.assertEqual(event_logged.type, order.order_type)

    def test_process_order_update_trigger_order_cancelled_event(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )

        self.tracker.start_tracking_order(order)

        order_cancelled_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.CANCELED,
        )

        update_future = self.tracker.process_order_update(
            order_cancelled_update)
        self.async_run_with_timeout(update_future)

        self.assertTrue(
            self._is_logged(
                "INFO",
                f"Successfully canceled order {order.client_order_id}."))
        self.assertEqual(0, len(self.tracker.active_orders))
        self.assertEqual(1, len(self.tracker.cached_orders))
        self.assertEqual(1, len(self.order_cancelled_logger.event_log))

        event_triggered = self.order_cancelled_logger.event_log[0]
        self.assertIsInstance(event_triggered, OrderCancelledEvent)
        self.assertEqual(event_triggered.exchange_order_id,
                         order.exchange_order_id)
        self.assertEqual(event_triggered.order_id, order.client_order_id)

    def test_process_order_update_trigger_order_failure_event(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )

        self.tracker.start_tracking_order(order)

        order_failure_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.FAILED,
        )

        update_future = self.tracker.process_order_update(order_failure_update)
        self.async_run_with_timeout(update_future)

        self.assertTrue(
            self._is_logged(
                "INFO",
                f"Order {order.client_order_id} has failed. Order Update: {order_failure_update}"
            ))
        self.assertEqual(0, len(self.tracker.active_orders))
        self.assertEqual(1, len(self.tracker.cached_orders))
        self.assertEqual(1, len(self.order_failure_logger.event_log))

        event_triggered = self.order_failure_logger.event_log[0]
        self.assertIsInstance(event_triggered, MarketOrderFailureEvent)
        self.assertEqual(event_triggered.order_id, order.client_order_id)
        self.assertEqual(event_triggered.order_type, order.order_type)

    def test_process_order_update_trigger_completed_event_and_not_fill_event(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )
        self.tracker.start_tracking_order(order)

        initial_order_filled_amount = order.amount / Decimal("2.0")
        order_update_1: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.PARTIALLY_FILLED,
        )

        update_future = self.tracker.process_order_update(order_update_1)
        self.async_run_with_timeout(update_future)

        # Check order update has been successfully applied
        updated_order: InFlightOrder = self.tracker.fetch_tracked_order(
            order.client_order_id)
        self.assertEqual(updated_order.exchange_order_id,
                         order_update_1.exchange_order_id)
        self.assertTrue(updated_order.exchange_order_id_update_event.is_set())
        self.assertEqual(updated_order.current_state, order_update_1.new_state)
        self.assertTrue(updated_order.is_open)

        subsequent_order_filled_amount = order.amount - initial_order_filled_amount
        order_update_2: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=2,
            new_state=OrderState.FILLED,
        )

        # Force order to not wait for filled events from TradeUpdate objects
        order.completely_filled_event.set()
        update_future = self.tracker.process_order_update(order_update_2)
        self.async_run_with_timeout(update_future)

        # Check order is not longer being actively tracked
        self.assertIsNone(
            self.tracker.fetch_tracked_order(order.client_order_id))

        cached_order: InFlightOrder = self.tracker.fetch_cached_order(
            order.client_order_id)
        self.assertEqual(cached_order.current_state, order_update_2.new_state)
        self.assertTrue(cached_order.is_done)

        # Check that Logger has logged the appropriate logs
        self.assertFalse(
            self._is_logged(
                "INFO",
                f"The {order.trade_type.name.upper()} order {order.client_order_id} amounting to "
                f"{initial_order_filled_amount}/{order.amount} {order.base_asset} has been filled.",
            ))
        self.assertFalse(
            self._is_logged(
                "INFO",
                f"The {order.trade_type.name.upper()} order {order.client_order_id} amounting to "
                f"{initial_order_filled_amount + subsequent_order_filled_amount}/{order.amount} {order.base_asset} "
                f"has been filled.",
            ))
        self.assertTrue(
            self._is_logged(
                "INFO",
                f"{order.trade_type.name.upper()} order {order.client_order_id} completely filled."
            ))

        self.assertEqual(0, len(self.order_filled_logger.event_log))
        self.assertEqual(1, len(self.buy_order_completed_logger.event_log))

    def test_process_trade_update_trigger_filled_event_flat_fee(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )
        self.tracker.start_tracking_order(order)

        trade_filled_price: Decimal = Decimal("0.5")
        trade_filled_amount: Decimal = order.amount / Decimal("2.0")
        fee_paid: Decimal = self.trade_fee_percent * trade_filled_amount
        trade_update: TradeUpdate = TradeUpdate(
            trade_id=1,
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=order.trading_pair,
            fill_price=trade_filled_price,
            fill_base_amount=trade_filled_amount,
            fill_quote_amount=trade_filled_price * trade_filled_amount,
            fee=AddedToCostTradeFee(flat_fees=[
                TokenAmount(token=self.quote_asset, amount=fee_paid)
            ]),
            fill_timestamp=1,
        )

        self.tracker.process_trade_update(trade_update)

        self.assertTrue(
            self._is_logged(
                "INFO",
                f"The {order.trade_type.name.upper()} order {order.client_order_id} amounting to "
                f"{trade_filled_amount}/{order.amount} {order.base_asset} has been filled.",
            ))

        self.assertEqual(1, len(self.order_filled_logger.event_log))
        order_filled_event: OrderFilledEvent = self.order_filled_logger.event_log[
            0]

        self.assertEqual(order_filled_event.order_id, order.client_order_id)
        self.assertEqual(order_filled_event.price, trade_update.fill_price)
        self.assertEqual(order_filled_event.amount,
                         trade_update.fill_base_amount)
        self.assertEqual(
            order_filled_event.trade_fee,
            AddedToCostTradeFee(
                flat_fees=[TokenAmount(self.quote_asset, fee_paid)]))

    def test_process_trade_update_does_not_trigger_filled_event_update_status_when_completely_filled(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )
        self.tracker.start_tracking_order(order)

        fee_paid: Decimal = self.trade_fee_percent * order.amount
        trade_update: TradeUpdate = TradeUpdate(
            trade_id=1,
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=order.trading_pair,
            fill_price=order.price,
            fill_base_amount=order.amount,
            fill_quote_amount=order.price * order.amount,
            fee=AddedToCostTradeFee(flat_fees=[
                TokenAmount(token=self.quote_asset, amount=fee_paid)
            ]),
            fill_timestamp=1,
        )

        self.tracker.process_trade_update(trade_update)

        fetched_order: InFlightOrder = self.tracker.fetch_order(
            order.client_order_id)
        self.assertTrue(fetched_order.is_filled)
        self.assertIn(fetched_order.client_order_id,
                      self.tracker.active_orders)
        self.assertNotIn(fetched_order.client_order_id,
                         self.tracker.cached_orders)

        self.assertTrue(
            self._is_logged(
                "INFO",
                f"The {order.trade_type.name.upper()} order {order.client_order_id} amounting to "
                f"{order.amount}/{order.amount} {order.base_asset} has been filled.",
            ))

        self.assertEqual(1, len(self.order_filled_logger.event_log))
        self.assertEqual(0, len(self.buy_order_completed_logger.event_log))

        order_filled_event: OrderFilledEvent = self.order_filled_logger.event_log[
            0]
        self.assertIsNotNone(order_filled_event)

        self.assertEqual(order_filled_event.order_id, order.client_order_id)
        self.assertEqual(order_filled_event.price, trade_update.fill_price)
        self.assertEqual(order_filled_event.amount,
                         trade_update.fill_base_amount)
        self.assertEqual(
            order_filled_event.trade_fee,
            AddedToCostTradeFee(
                flat_fees=[TokenAmount(self.quote_asset, fee_paid)]))

    def test_updating_order_states_with_both_process_order_update_and_process_trade_update(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        self.tracker.start_tracking_order(order)

        order_creation_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(update_future)

        open_order: InFlightOrder = self.tracker.fetch_tracked_order(
            order.client_order_id)

        # Check order_creation_update has been successfully applied
        self.assertEqual(open_order.exchange_order_id,
                         order_creation_update.exchange_order_id)
        self.assertTrue(open_order.exchange_order_id_update_event.is_set())
        self.assertEqual(open_order.current_state,
                         order_creation_update.new_state)
        self.assertTrue(open_order.is_open)
        self.assertEqual(0, len(open_order.order_fills))

        trade_filled_price: Decimal = order.price
        trade_filled_amount: Decimal = order.amount
        fee_paid: Decimal = self.trade_fee_percent * trade_filled_amount
        trade_update: TradeUpdate = TradeUpdate(
            trade_id=1,
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=order.trading_pair,
            fill_price=trade_filled_price,
            fill_base_amount=trade_filled_amount,
            fill_quote_amount=trade_filled_price * trade_filled_amount,
            fee=AddedToCostTradeFee(flat_fees=[
                TokenAmount(token=self.quote_asset, amount=fee_paid)
            ]),
            fill_timestamp=2,
        )

        self.tracker.process_trade_update(trade_update)
        self.assertEqual(1, len(self.tracker.active_orders))
        self.assertEqual(0, len(self.tracker.cached_orders))

    def test_process_order_not_found_invalid_order(self):
        self.assertEqual(0, len(self.tracker.active_orders))

        unknown_order_id = "UNKNOWN_ORDER_ID"
        self.async_run_with_timeout(
            self.tracker.process_order_not_found(unknown_order_id))

        self._is_logged(
            "DEBUG",
            f"Order is not/no longer being tracked ({unknown_order_id})")

    def test_process_order_not_found_does_not_exceed_limit(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )
        self.tracker.start_tracking_order(order)

        self.async_run_with_timeout(
            self.tracker.process_order_not_found(order.client_order_id))

        self.assertIn(order.client_order_id, self.tracker.active_orders)
        self.assertIn(order.client_order_id,
                      self.tracker._order_not_found_records)
        self.assertEqual(
            1, self.tracker._order_not_found_records[order.client_order_id])

    def test_process_order_not_found_exceeded_limit(self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
            initial_state=OrderState.OPEN,
        )
        self.tracker.start_tracking_order(order)

        self.tracker._order_not_found_records[order.client_order_id] = 10
        self.async_run_with_timeout(
            self.tracker.process_order_not_found(order.client_order_id))

        self.assertNotIn(order.client_order_id, self.tracker.active_orders)

    def test_restore_tracking_states_only_registers_open_orders(self):
        orders = []
        orders.append(
            InFlightOrder(
                client_order_id="OID1",
                exchange_order_id="EOID1",
                trading_pair=self.trading_pair,
                order_type=OrderType.LIMIT,
                trade_type=TradeType.BUY,
                amount=Decimal("1000.0"),
                creation_timestamp=1640001112.223,
                price=Decimal("1.0"),
            ))
        orders.append(
            InFlightOrder(client_order_id="OID2",
                          exchange_order_id="EOID2",
                          trading_pair=self.trading_pair,
                          order_type=OrderType.LIMIT,
                          trade_type=TradeType.BUY,
                          amount=Decimal("1000.0"),
                          creation_timestamp=1640001112.223,
                          price=Decimal("1.0"),
                          initial_state=OrderState.CANCELED))
        orders.append(
            InFlightOrder(client_order_id="OID3",
                          exchange_order_id="EOID3",
                          trading_pair=self.trading_pair,
                          order_type=OrderType.LIMIT,
                          trade_type=TradeType.BUY,
                          amount=Decimal("1000.0"),
                          price=Decimal("1.0"),
                          creation_timestamp=1640001112.223,
                          initial_state=OrderState.FILLED))
        orders.append(
            InFlightOrder(client_order_id="OID4",
                          exchange_order_id="EOID4",
                          trading_pair=self.trading_pair,
                          order_type=OrderType.LIMIT,
                          trade_type=TradeType.BUY,
                          amount=Decimal("1000.0"),
                          price=Decimal("1.0"),
                          creation_timestamp=1640001112.223,
                          initial_state=OrderState.FAILED))

        tracking_states = {
            order.client_order_id: order.to_json()
            for order in orders
        }

        self.tracker.restore_tracking_states(tracking_states)

        self.assertIn("OID1", self.tracker.active_orders)
        self.assertNotIn("OID2", self.tracker.all_orders)
        self.assertNotIn("OID3", self.tracker.all_orders)
        self.assertNotIn("OID4", self.tracker.all_orders)

    def test_update_to_close_order_is_not_processed_until_order_completelly_filled(
            self):
        order: InFlightOrder = InFlightOrder(
            client_order_id="someClientOrderId",
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            price=Decimal("1.0"),
            creation_timestamp=1640001112.223,
        )
        self.tracker.start_tracking_order(order)

        order_creation_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=1,
            new_state=OrderState.OPEN,
        )

        trade_update: TradeUpdate = TradeUpdate(
            trade_id="1",
            client_order_id=order.client_order_id,
            exchange_order_id=order.exchange_order_id,
            trading_pair=order.trading_pair,
            fill_price=Decimal("1100"),
            fill_base_amount=order.amount,
            fill_quote_amount=order.amount * Decimal("1100"),
            fee=AddedToCostTradeFee(flat_fees=[
                TokenAmount(token=self.quote_asset, amount=Decimal("10"))
            ]),
            fill_timestamp=10,
        )

        order_completion_update: OrderUpdate = OrderUpdate(
            client_order_id=order.client_order_id,
            exchange_order_id="someExchangeOrderId",
            trading_pair=self.trading_pair,
            update_timestamp=2,
            new_state=OrderState.FILLED,
        )

        # We invert the orders update processing on purpose, to force the test scenario without using sleeps
        self.connector._set_current_timestamp(1640001100)
        completion_update_future = self.tracker.process_order_update(
            order_completion_update)

        self.connector._set_current_timestamp(1640001105)
        creation_update_future = self.tracker.process_order_update(
            order_creation_update)
        self.async_run_with_timeout(creation_update_future)

        order: InFlightOrder = self.tracker.fetch_order(
            client_order_id=order.client_order_id)

        # Check order_creation_update has been successfully applied
        self.assertFalse(order.is_done)
        self.assertFalse(order.is_filled)
        self.assertFalse(order.completely_filled_event.is_set())

        fill_timetamp = 1640001115
        self.connector._set_current_timestamp(fill_timetamp)
        self.tracker.process_trade_update(trade_update)
        self.assertTrue(order.completely_filled_event.is_set())

        self.connector._set_current_timestamp(1640001120)
        self.async_run_with_timeout(completion_update_future)

        self.assertTrue(order.is_filled)
        fill_event: OrderFilledEvent = self.order_filled_logger.event_log[0]
        self.assertEqual(fill_timetamp, fill_event.timestamp)

        complete_event: BuyOrderCompletedEvent = self.buy_order_completed_logger.event_log[
            0]
        self.assertGreaterEqual(complete_event.timestamp, 1640001120)