def test_from_json_does_not_fail_when_order_fills_not_present(self): order_json = { "client_order_id": self.client_order_id, "exchange_order_id": self.exchange_order_id, "trading_pair": self.trading_pair, "order_type": OrderType.LIMIT.name, "trade_type": TradeType.BUY.name, "price": "1.0", "amount": "1000.0", "executed_amount_base": "0", "executed_amount_quote": "0", "fee_asset": None, "fee_paid": "0", "last_state": "0", "leverage": "1", "position": PositionAction.NIL.value, "creation_timestamp": 1640001112 } expected_order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, 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"), ) order_from_json = InFlightOrder.from_json(order_json) self.assertEqual(expected_order, order_from_json) self.assertFalse(order_from_json.completely_filled_event.is_set())
def test_completed_order_recovered_from_json_has_completed_event_updated(self): order_json = { "client_order_id": self.client_order_id, "exchange_order_id": self.exchange_order_id, "trading_pair": self.trading_pair, "order_type": OrderType.LIMIT.name, "trade_type": TradeType.BUY.name, "price": "1.0", "amount": "1000.0", "executed_amount_base": "1000.0", "executed_amount_quote": "1100.0", "fee_asset": None, "fee_paid": "0", "last_state": "0", "leverage": "1", "position": "NIL", "creation_timestamp": 1640001112.0, } expected_order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, 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"), ) expected_order.executed_amount_base = Decimal("1000") expected_order.executed_amount_quote = Decimal("1100") order_from_json = InFlightOrder.from_json(order_json) self.assertEqual(expected_order, order_from_json) self.assertTrue(order_from_json.completely_filled_event.is_set())
def test_from_json(self): order_json = { "client_order_id": self.client_order_id, "exchange_order_id": self.exchange_order_id, "trading_pair": self.trading_pair, "order_type": OrderType.LIMIT.name, "trade_type": TradeType.BUY.name, "price": "1.0", "amount": "1000.0", "executed_amount_base": "0", "executed_amount_quote": "0", "fee_asset": None, "fee_paid": "0", "last_state": "0", "leverage": "1", "position": PositionAction.NIL.value, } expected_order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) order_from_json = InFlightOrder.from_json(order_json) self.assertEqual(expected_order, order_from_json)
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_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"), price=Decimal("1.0"), creation_timestamp=1640001112.223, )) 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"), price=Decimal("1.0"), creation_timestamp=1640001112.223, initial_state=BitmexPerpetualOrderStatus.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=BitmexPerpetualOrderStatus.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=BitmexPerpetualOrderStatus.FAILURE)) tracking_states = { order.client_order_id: order.to_json() for order in orders } self.exchange.restore_tracking_states(tracking_states) self.assertIn("OID1", self.exchange.in_flight_orders) self.assertNotIn("OID2", self.exchange.in_flight_orders) self.assertNotIn("OID3", self.exchange.in_flight_orders) self.assertNotIn("OID4", self.exchange.in_flight_orders)
def test_from_json(self): fee = AddedToCostTradeFee( percent=Decimal("0.5"), percent_token=self.quote_asset ) trade_update = TradeUpdate( trade_id="12345", client_order_id=self.client_order_id, exchange_order_id="EOID1", trading_pair=self.trading_pair, fill_timestamp=1640001112, fill_price=Decimal("1000.11"), fill_base_amount=Decimal("2"), fill_quote_amount=Decimal("2000.22"), fee=fee, ) order_json = { "client_order_id": self.client_order_id, "exchange_order_id": self.exchange_order_id, "trading_pair": self.trading_pair, "order_type": OrderType.LIMIT.name, "trade_type": TradeType.BUY.name, "price": "1.0", "amount": "1000.0", "executed_amount_base": "0", "executed_amount_quote": "0", "fee_asset": None, "fee_paid": "0", "last_state": "0", "leverage": "1", "position": "NIL", "creation_timestamp": 1640001112.0, "order_fills": {"1": trade_update.to_json()} } expected_order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, 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"), ) order_from_json = InFlightOrder.from_json(order_json) self.assertEqual(expected_order, order_from_json) self.assertFalse(order_from_json.completely_filled_event.is_set()) self.assertIn("1", order_from_json.order_fills) self.assertEqual(trade_update, order_from_json.order_fills["1"])
def start_tracking_order(self, order_id: str, exchange_order_id: Optional[str], trading_pair: str, trade_type: TradeType, price: Decimal, amount: Decimal, order_type: OrderType): """ Starts tracking an order by adding it to the order tracker. :param order_id: the order identifier :param exchange_order_id: the identifier for the order in the exchange :param trading_pair: the token pair for the operation :param trade_type: the type of order (buy or sell) :param price: the price for the order :param amount: the amount for the order :order type: type of execution for the order (MARKET, LIMIT, LIMIT_MAKER) """ self._order_tracker.start_tracking_order( InFlightOrder( client_order_id=order_id, exchange_order_id=exchange_order_id, trading_pair=trading_pair, order_type=order_type, trade_type=trade_type, amount=amount, price=price, ))
def test_update_with_order_update_open_order(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) open_order_update: OrderUpdate = OrderUpdate( client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, trading_pair=self.trading_pair, update_timestamp=1, new_state=OrderState.OPEN, ) self.assertTrue(order.update_with_order_update(open_order_update)) self.assertEqual(Decimal("0"), order.executed_amount_base) self.assertEqual(Decimal("0"), order.executed_amount_quote) self.assertIsNone(order.fee_asset) self.assertEqual(Decimal("0"), order.cumulative_fee_paid) self.assertEqual(Decimal("0"), order.last_filled_price) self.assertEqual(Decimal("0"), order.last_filled_amount) self.assertEqual(Decimal("0"), order.last_fee_paid) self.assertEqual(1, order.last_update_timestamp)
def test_to_json(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) order_json = order.to_json() self.assertIsInstance(order_json, dict) self.assertEqual(order_json["client_order_id"], order.client_order_id) self.assertEqual(order_json["exchange_order_id"], order.exchange_order_id) self.assertEqual(order_json["trading_pair"], order.trading_pair) self.assertEqual(order_json["order_type"], order.order_type.name) self.assertEqual(order_json["trade_type"], order.trade_type.name) self.assertEqual(order_json["price"], str(order.price)) self.assertEqual(order_json["amount"], str(order.amount)) self.assertEqual(order_json["executed_amount_base"], str(order.executed_amount_base)) self.assertEqual(order_json["executed_amount_quote"], str(order.executed_amount_quote)) self.assertEqual(order_json["fee_asset"], order.fee_asset) self.assertEqual(order_json["fee_paid"], str(order.cumulative_fee_paid)) self.assertEqual(order_json["last_state"], str(order.current_state.value)) self.assertEqual(order_json["leverage"], str(order.leverage)) self.assertEqual(order_json["position"], order.position.value)
def start_tracking_order( self, order_id: str, trading_pair: str, trade_type: TradeType, price: Decimal, amount: Decimal, order_type: OrderType, exchange_order_id: Optional[str] = None, ): """ Starts tracking an order by simply adding it into InFlightOrderTracker. """ self._in_flight_order_tracker.start_tracking_order( InFlightOrder( client_order_id=order_id, exchange_order_id=exchange_order_id, trading_pair=trading_pair, order_type=order_type, trade_type=trade_type, amount=amount, price=price, creation_timestamp=self.current_timestamp ) )
def test_to_limit_order(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) expected_limit_order: LimitOrder = LimitOrder( client_order_id=order.client_order_id, trading_pair=order.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=Decimal("1.0"), quantity=Decimal("1000.0"), filled_quantity=Decimal("0"), ) limit_order = order.to_limit_order() self.assertIsInstance(limit_order, LimitOrder) self.assertEqual(limit_order.client_order_id, expected_limit_order.client_order_id) self.assertEqual(limit_order.trading_pair, expected_limit_order.trading_pair) self.assertEqual(limit_order.is_buy, expected_limit_order.is_buy) self.assertEqual(limit_order.base_currency, expected_limit_order.base_currency) self.assertEqual(limit_order.quote_currency, expected_limit_order.quote_currency) self.assertEqual(limit_order.price, expected_limit_order.price) self.assertEqual(limit_order.quantity, expected_limit_order.quantity) self.assertEqual(limit_order.filled_quantity, expected_limit_order.filled_quantity) self.assertEqual(limit_order.creation_timestamp, expected_limit_order.creation_timestamp) self.assertEqual(limit_order.status, expected_limit_order.status)
def test_update_with_order_update_client_order_id_mismatch(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) mismatch_order_update: OrderUpdate = OrderUpdate( client_order_id="mismatchClientOrderId", exchange_order_id="mismatchExchangeOrderId", trading_pair=self.trading_pair, update_timestamp=1, new_state=OrderState.OPEN, fill_price=Decimal("1.0"), executed_amount_base=Decimal("1000.0"), executed_amount_quote=Decimal("1000.0"), fee_asset=self.base_asset, cumulative_fee_paid=self.trade_fee_percent * Decimal("1000.0"), ) self.assertFalse(order.update_with_order_update(mismatch_order_update)) self.assertEqual(Decimal("0"), order.executed_amount_base) self.assertEqual(Decimal("0"), order.executed_amount_quote) self.assertIsNone(order.fee_asset) self.assertEqual(Decimal("0"), order.cumulative_fee_paid) self.assertEqual(Decimal("0"), order.last_filled_price) self.assertEqual(Decimal("0"), order.last_filled_amount) self.assertEqual(Decimal("0"), order.last_fee_paid) self.assertEqual(-1, order.last_update_timestamp)
def test_update_with_trade_update_duplicate_trade_update(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, 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"), ) trade_update: TradeUpdate = TradeUpdate( trade_id="someTradeId", client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, trading_pair=self.trading_pair, fill_price=Decimal("1.0"), fill_base_amount=Decimal("500.0"), fill_quote_amount=Decimal("500.0"), fee=AddedToCostTradeFee( flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * Decimal("500.0"))]), fill_timestamp=1, ) self.assertTrue(order.update_with_trade_update(trade_update)) self.assertEqual(order.executed_amount_base, trade_update.fill_base_amount) self.assertEqual(order.executed_amount_quote, trade_update.fill_quote_amount) self.assertEqual(order.last_update_timestamp, trade_update.fill_timestamp) self.assertEqual(1, len(order.order_fills)) self.assertIn(trade_update.trade_id, order.order_fills) # Ignores duplicate trade update self.assertFalse(order.update_with_trade_update(trade_update))
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.CANCELLED, ) 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 cancelled 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_trade_update_does_not_change_exchange_order_id(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, 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"), ) trade_update: TradeUpdate = TradeUpdate( trade_id="someTradeId", client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, trading_pair=self.trading_pair, fill_price=Decimal("1.0"), fill_base_amount=Decimal("500.0"), fill_quote_amount=Decimal("500.0"), fee=AddedToCostTradeFee( flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * Decimal("500.0"))]), fill_timestamp=1, ) self.assertTrue(order.update_with_trade_update(trade_update)) self.assertIsNone(order.exchange_order_id) self.assertFalse(order.exchange_order_id_update_event.is_set())
def test_update_with_trade_update_trade_update_with_trade_fee_percent(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) trade_update: TradeUpdate = TradeUpdate( trade_id="someTradeId", client_order_id=self.client_order_id, exchange_order_id=self.exchange_order_id, trading_pair=self.trading_pair, fill_price=Decimal("1.0"), fill_base_amount=Decimal("500.0"), fill_quote_amount=Decimal("500.0"), fee_asset=self.base_asset, trade_fee_percent=self.trade_fee_percent, fill_timestamp=1, ) self.assertTrue(order.update_with_trade_update(trade_update)) self.assertIsNotNone(order.exchange_order_id) self.assertTrue(order.exchange_order_id_update_event.is_set()) self.assertEqual(order.executed_amount_base, trade_update.fill_base_amount) self.assertEqual(order.executed_amount_quote, trade_update.fill_quote_amount) self.assertEqual(order.fee_asset, trade_update.fee_asset) self.assertEqual(order.cumulative_fee_paid, self.trade_fee_percent * Decimal("500.0")) self.assertEqual(order.last_filled_price, trade_update.fill_price) self.assertEqual(order.last_filled_amount, trade_update.fill_base_amount) self.assertEqual(order.last_update_timestamp, trade_update.fill_timestamp) self.assertEqual(1, len(order.order_fills)) self.assertIn(trade_update.trade_id, order.order_fills)
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 restore_tracking_states(self, tracking_states: Dict[str, any]): """ Restore in-flight orders from saved tracking states. :param tracking_states: a dictionary associating order ids with the serialized order (JSON format). """ for serialized_order in tracking_states.values(): order = InFlightOrder.from_json(serialized_order) if order.is_open: self.start_tracking_order(order)
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_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_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"), price=Decimal("1.0"), ) self.tracker._cached_orders[order.client_order_id] = order self.assertIn(order.client_order_id, self.tracker._cached_orders)
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"), 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, ) self.tracker.process_order_update(order_creation_update) 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.connector.event_logs)) event_logged = self.connector.event_logs[0] self.assertIsInstance(event_logged, BuyOrderCreatedEvent) 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_to_json(self): fee = AddedToCostTradeFee(percent=Decimal("0.5"), percent_token=self.quote_asset) trade_update = TradeUpdate( trade_id="12345", client_order_id=self.client_order_id, exchange_order_id="EOID1", trading_pair=self.trading_pair, fill_timestamp=1640001112, fill_price=Decimal("1000.11"), fill_base_amount=Decimal("2"), fill_quote_amount=Decimal("2000.22"), fee=fee, ) order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, 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"), ) order.order_fills["1"] = trade_update order_json = order.to_json() self.assertIsInstance(order_json, dict) self.assertEqual(order_json["client_order_id"], order.client_order_id) self.assertEqual(order_json["exchange_order_id"], order.exchange_order_id) self.assertEqual(order_json["trading_pair"], order.trading_pair) self.assertEqual(order_json["order_type"], order.order_type.name) self.assertEqual(order_json["trade_type"], order.trade_type.name) self.assertEqual(order_json["price"], str(order.price)) self.assertEqual(order_json["amount"], str(order.amount)) self.assertEqual(order_json["executed_amount_base"], str(order.executed_amount_base)) self.assertEqual(order_json["executed_amount_quote"], str(order.executed_amount_quote)) self.assertEqual(order_json["last_state"], str(order.current_state.value)) self.assertEqual(order_json["leverage"], str(order.leverage)) self.assertEqual(order_json["position"], order.position.value) self.assertEqual(order_json["creation_timestamp"], order.creation_timestamp) self.assertEqual(order_json["order_fills"], {"1": trade_update.to_json()})
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"), 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_asset=self.base_asset, fee_paid=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.connector.event_logs)) order_filled_event: OrderFilledEvent = self.connector.event_logs[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.base_asset, fee_paid)]))
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)
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"), price=Decimal("1.0"), ) self.tracker.start_tracking_order(order) self.assertEqual(1, len(self.tracker.active_orders))
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_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"), 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_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)