def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder, timestamp: float = 0):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency

        trade_event: OrderBookTradeEvent = OrderBookTradeEvent(
            trading_pair=limit_order.trading_pair,
            timestamp=timestamp,
            type=TradeType.BUY if limit_order.is_buy else TradeType.SELL,
            price=limit_order.price,
            amount=limit_order.quantity
        )

        market.get_order_book(limit_order.trading_pair).apply_trade(trade_event)

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                OrderType.LIMIT
            ))
    def simulate_limit_order_fill(market: Market, limit_order: LimitOrder):
        quote_currency_traded: float = float(
            float(limit_order.price) * float(limit_order.quantity))
        base_currency_traded: float = float(limit_order.quantity)
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency
        config: MarketConfig = market.config

        if limit_order.is_buy:
            market.set_balance(
                quote_currency,
                market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(
                base_currency,
                market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(
                MarketEvent.OrderFilled,
                OrderFilledEvent(
                    market.current_timestamp,
                    limit_order.client_order_id,
                    limit_order.symbol,
                    TradeType.BUY,
                    OrderType.LIMIT,
                    float(limit_order.price),
                    float(limit_order.quantity),
                    TradeFee(0.0)  # No fee in backtest market
                ))
            market.trigger_event(
                MarketEvent.BuyOrderCompleted,
                BuyOrderCompletedEvent(
                    market.current_timestamp, limit_order.client_order_id,
                    base_currency, quote_currency, base_currency
                    if config.buy_fees_asset is AssetType.BASE_CURRENCY else
                    quote_currency, base_currency_traded,
                    quote_currency_traded, 0.0, OrderType.LIMIT))
        else:
            market.set_balance(
                quote_currency,
                market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(
                base_currency,
                market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(
                MarketEvent.OrderFilled,
                OrderFilledEvent(market.current_timestamp,
                                 limit_order.client_order_id,
                                 limit_order.symbol, TradeType.SELL,
                                 OrderType.LIMIT, float(limit_order.price),
                                 float(limit_order.quantity), TradeFee(0.0)))
            market.trigger_event(
                MarketEvent.SellOrderCompleted,
                SellOrderCompletedEvent(
                    market.current_timestamp, limit_order.client_order_id,
                    base_currency, quote_currency, base_currency
                    if config.sell_fees_asset is AssetType.BASE_CURRENCY else
                    quote_currency, base_currency_traded,
                    quote_currency_traded, 0.0, OrderType.LIMIT))
Example #3
0
    def simulate_limit_order_fill(market: Market, limit_order: LimitOrder):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency
        config: MarketConfig = market.config

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                TradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency if config.buy_fees_asset is AssetType.BASE_CURRENCY else quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                TradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency if config.sell_fees_asset is AssetType.BASE_CURRENCY else quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
    def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
Example #5
0
    def test_create_order_and_completed(self):
        recorder = MarketsRecorder(sql=self.manager,
                                   markets=[self],
                                   config_file_path=self.config_file_path,
                                   strategy_name=self.strategy_name)

        create_event = BuyOrderCreatedEvent(
            timestamp=1642010000,
            type=OrderType.LIMIT,
            trading_pair=self.trading_pair,
            amount=Decimal(1),
            price=Decimal(1000),
            order_id="OID1-1642010000000000",
            creation_timestamp=1640001112.223,
            exchange_order_id="EOID1",
        )

        recorder._did_create_order(MarketEvent.BuyOrderCreated.value, self,
                                   create_event)

        complete_event = BuyOrderCompletedEvent(
            timestamp=1642020000,
            order_id=create_event.order_id,
            base_asset=self.base,
            quote_asset=self.quote,
            base_asset_amount=create_event.amount,
            quote_asset_amount=create_event.amount * create_event.price,
            order_type=create_event.type)

        recorder._did_complete_order(MarketEvent.BuyOrderCompleted.value, self,
                                     complete_event)

        with self.manager.get_new_session() as session:
            query = session.query(Order)
            orders = query.all()
            order = orders[0]
            order_status = order.status
            trade_fills = order.trade_fills

        self.assertEqual(1, len(orders))
        self.assertEqual(self.config_file_path, orders[0].config_file_path)
        self.assertEqual(create_event.order_id, orders[0].id)
        self.assertEqual(2, len(order_status))
        self.assertEqual(MarketEvent.BuyOrderCreated.name,
                         order_status[0].status)
        self.assertEqual(MarketEvent.BuyOrderCompleted.name,
                         order_status[1].status)
        self.assertEqual(0, len(trade_fills))
Example #6
0
    def simulate_limit_order_fill(market: Market, limit_order: LimitOrder, timestamp: float = 0):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency
        config: MarketConfig = market.config

        trade_event: OrderBookTradeEvent = OrderBookTradeEvent(
            trading_pair=limit_order.trading_pair,
            timestamp=timestamp,
            type=TradeType.BUY if limit_order.is_buy else TradeType.SELL,
            price=limit_order.price,
            amount=limit_order.quantity
        )

        market.get_order_book(limit_order.trading_pair).apply_trade(trade_event)

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                TradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency if config.buy_fees_asset is AssetType.BASE_CURRENCY else quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal(0.0),
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                TradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency if config.sell_fees_asset is AssetType.BASE_CURRENCY else quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal(0.0),
                OrderType.LIMIT
            ))
Example #7
0
    async def _process_order_message(self, stream_message: Dict[str, Any]):
        client_order_id = stream_message["data"]["clientOrderId"]
        # trading_pair = convert_from_exchange_trading_pair(stream_message["symbol"])
        # 1:NEW,2:FILLED,3:PARTIALLY_FILLED,4:CANCELED,5:PARTIALLY_CANCELED
        order_status = ws_order_status_convert_to_str(
            stream_message["data"]["status"])
        tracked_order = self._in_flight_orders.get(client_order_id, None)
        if tracked_order is None:
            return
        # Update balance in time
        await self._update_balances()

        if order_status in {"FILLED", "PARTIALLY_FILLED"}:
            executed_amount = Decimal(str(
                stream_message["data"]['quantity'])) - Decimal(
                    str(stream_message["data"]['remainQuantity']))
            execute_price = Decimal(str(stream_message["data"]['price']))
            execute_amount_diff = executed_amount - tracked_order.executed_amount_base
            if execute_amount_diff > s_decimal_0:
                tracked_order.executed_amount_base = executed_amount
                tracked_order.executed_amount_quote = Decimal(
                    str(stream_message["data"]['amount'])) - Decimal(
                        str(stream_message["data"]['remainAmount']))

                current_fee = self.get_fee(tracked_order.base_asset,
                                           tracked_order.quote_asset,
                                           tracked_order.order_type,
                                           tracked_order.trade_type,
                                           execute_amount_diff, execute_price)
                self.logger().info(
                    f"Filled {execute_amount_diff} out of {tracked_order.amount} of "
                )
                self.trigger_event(
                    self.MARKET_ORDER_FILLED_EVENT_TAG,
                    OrderFilledEvent(
                        self.current_timestamp,
                        tracked_order.client_order_id,
                        tracked_order.trading_pair,
                        tracked_order.trade_type,
                        tracked_order.order_type,
                        execute_price,
                        execute_amount_diff,
                        current_fee,
                        exchange_trade_id=tracked_order.exchange_order_id))
        if order_status == "FILLED":
            fee_paid, fee_currency = await self.get_deal_detail_fee(
                tracked_order.exchange_order_id)
            tracked_order.fee_paid = fee_paid
            tracked_order.fee_asset = fee_currency
            tracked_order.last_state = order_status
            if tracked_order.trade_type is TradeType.BUY:
                self.logger().info(
                    f"The BUY {tracked_order.order_type} order {tracked_order.client_order_id} has completed "
                    f"according to order delta websocket API.")
                self.trigger_event(
                    self.MARKET_BUY_ORDER_COMPLETED_EVENT_TAG,
                    BuyOrderCompletedEvent(
                        self.current_timestamp, tracked_order.client_order_id,
                        tracked_order.base_asset, tracked_order.quote_asset,
                        tracked_order.fee_asset or tracked_order.quote_asset,
                        tracked_order.executed_amount_base,
                        tracked_order.executed_amount_quote,
                        tracked_order.fee_paid, tracked_order.order_type))
            elif tracked_order.trade_type is TradeType.SELL:
                self.logger().info(
                    f"The SELL {tracked_order.order_type} order {tracked_order.client_order_id} has completed "
                    f"according to order delta websocket API.")
                self.trigger_event(
                    self.MARKET_SELL_ORDER_COMPLETED_EVENT_TAG,
                    SellOrderCompletedEvent(
                        self.current_timestamp, tracked_order.client_order_id,
                        tracked_order.base_asset, tracked_order.quote_asset,
                        tracked_order.fee_asset or tracked_order.quote_asset,
                        tracked_order.executed_amount_base,
                        tracked_order.executed_amount_quote,
                        tracked_order.fee_paid, tracked_order.order_type))
            self.stop_tracking_order(tracked_order.client_order_id)
            return

        if order_status == "CANCELED" or order_status == "PARTIALLY_CANCELED":
            tracked_order.last_state = order_status
            self.logger().info(
                f"Order {tracked_order.client_order_id} has been cancelled "
                f"according to order delta websocket API.")
            self.trigger_event(
                self.MARKET_ORDER_CANCELLED_EVENT_TAG,
                OrderCancelledEvent(self.current_timestamp,
                                    tracked_order.client_order_id))
            self.stop_tracking_order(tracked_order.client_order_id)
Example #8
0
    async def _update_order_status(self):
        last_tick = int(self._last_poll_timestamp /
                        self.UPDATE_ORDERS_INTERVAL)
        current_tick = int(self.current_timestamp /
                           self.UPDATE_ORDERS_INTERVAL)
        if current_tick > last_tick and len(self._in_flight_orders) > 0:
            tracked_orders = list(self._in_flight_orders.values())
            for tracked_order in tracked_orders:
                try:
                    exchange_order_id = await tracked_order.get_exchange_order_id(
                    )
                    try:
                        order_update = await self.get_order_status(
                            exchange_order_id, tracked_order.trading_pair)
                    except MexcAPIError as ex:
                        err_code = ex.error_payload.get("error").get(
                            'err-code')
                        self.stop_tracking_order(tracked_order.client_order_id)
                        self.logger().info(
                            f"The limit order {tracked_order.client_order_id} "
                            f"has failed according to order status API. - {err_code}"
                        )
                        self.trigger_event(
                            self.MARKET_ORDER_FAILURE_EVENT_TAG,
                            MarketOrderFailureEvent(
                                self.current_timestamp,
                                tracked_order.client_order_id,
                                tracked_order.order_type))
                        continue

                    if order_update is None:
                        self.logger().network(
                            f"Error fetching status update for the order {tracked_order.client_order_id}: "
                            f"{exchange_order_id}.",
                            app_warning_msg=
                            f"Could not fetch updates for the order {tracked_order.client_order_id}. "
                            f"The order has either been filled or canceled.")
                        continue
                    tracked_order.last_state = order_update['state']
                    order_status = order_update['state']
                    new_confirmed_amount = Decimal(
                        order_update['deal_quantity'])
                    execute_amount_diff = new_confirmed_amount - tracked_order.executed_amount_base

                    if execute_amount_diff > s_decimal_0:
                        execute_price = Decimal(
                            Decimal(order_update['deal_amount']) /
                            Decimal(order_update['deal_quantity']))
                        tracked_order.executed_amount_base = Decimal(
                            order_update['deal_quantity'])
                        tracked_order.executed_amount_quote = Decimal(
                            order_update['deal_amount'])

                        order_filled_event = OrderFilledEvent(
                            self.current_timestamp,
                            tracked_order.client_order_id,
                            tracked_order.trading_pair,
                            tracked_order.trade_type,
                            tracked_order.order_type,
                            execute_price,
                            execute_amount_diff,
                            self.get_fee(
                                tracked_order.base_asset,
                                tracked_order.quote_asset,
                                tracked_order.order_type,
                                tracked_order.trade_type,
                                execute_amount_diff,
                                execute_price,
                            ),
                            exchange_trade_id=exchange_order_id)
                        self.logger().info(
                            f"Filled {execute_amount_diff} out of {tracked_order.amount} of the "
                            f"order {tracked_order.client_order_id}.")
                        self.trigger_event(self.MARKET_ORDER_FILLED_EVENT_TAG,
                                           order_filled_event)
                    if order_status == "FILLED":
                        fee_paid, fee_currency = await self.get_deal_detail_fee(
                            tracked_order.exchange_order_id)
                        tracked_order.fee_paid = fee_paid
                        tracked_order.fee_asset = fee_currency
                        tracked_order.last_state = order_status
                        self.stop_tracking_order(tracked_order.client_order_id)
                        if tracked_order.trade_type is TradeType.BUY:
                            self.logger().info(
                                f"The BUY {tracked_order.order_type} order {tracked_order.client_order_id} has completed "
                                f"according to order delta restful API.")
                            self.trigger_event(
                                self.MARKET_BUY_ORDER_COMPLETED_EVENT_TAG,
                                BuyOrderCompletedEvent(
                                    self.current_timestamp,
                                    tracked_order.client_order_id,
                                    tracked_order.base_asset,
                                    tracked_order.quote_asset,
                                    tracked_order.fee_asset
                                    or tracked_order.quote_asset,
                                    tracked_order.executed_amount_base,
                                    tracked_order.executed_amount_quote,
                                    tracked_order.fee_paid,
                                    tracked_order.order_type))
                        elif tracked_order.trade_type is TradeType.SELL:
                            self.logger().info(
                                f"The SELL {tracked_order.order_type} order {tracked_order.client_order_id} has completed "
                                f"according to order delta restful API.")
                            self.trigger_event(
                                self.MARKET_SELL_ORDER_COMPLETED_EVENT_TAG,
                                SellOrderCompletedEvent(
                                    self.current_timestamp,
                                    tracked_order.client_order_id,
                                    tracked_order.base_asset,
                                    tracked_order.quote_asset,
                                    tracked_order.fee_asset
                                    or tracked_order.quote_asset,
                                    tracked_order.executed_amount_base,
                                    tracked_order.executed_amount_quote,
                                    tracked_order.fee_paid,
                                    tracked_order.order_type))
                        continue
                    if order_status == "CANCELED" or order_status == "PARTIALLY_CANCELED":
                        tracked_order.last_state = order_status
                        self.stop_tracking_order(tracked_order.client_order_id)
                        self.logger().info(
                            f"Order {tracked_order.client_order_id} has been cancelled "
                            f"according to order delta restful API.")
                        self.trigger_event(
                            self.MARKET_ORDER_CANCELLED_EVENT_TAG,
                            OrderCancelledEvent(self.current_timestamp,
                                                tracked_order.client_order_id))
                except Exception as ex:
                    self.logger().error("_update_order_status error ..." +
                                        repr(ex),
                                        exc_info=True)
    def test_non_grouped_hanging_order_and_original_order_removed_when_hanging_order_completed(
            self):
        strategy_active_orders = []
        newly_created_buy_orders_ids = [
            "Order-1234570000000000", "Order-1234570020000000",
            "Order-1234570040000000", "Order-1234570060000000"
        ]
        newly_created_sell_orders_ids = [
            "Order-1234570010000000", "Order-1234570030000000",
            "Order-1234570050000000", "Order-1234570070000000"
        ]

        type(self.strategy).active_orders = PropertyMock(
            return_value=strategy_active_orders)
        self.strategy.buy_with_specific_market.side_effect = newly_created_buy_orders_ids
        self.strategy.sell_with_specific_market.side_effect = newly_created_sell_orders_ids

        buy_order_1 = LimitOrder("Order-1234569960000000",
                                 "BTC-USDT",
                                 True,
                                 "BTC",
                                 "USDT",
                                 Decimal(101),
                                 Decimal(1),
                                 creation_timestamp=1234569960000000)
        sell_order_1 = LimitOrder("Order-1234569970000000",
                                  "BTC-USDT",
                                  False,
                                  "BTC",
                                  "USDT",
                                  Decimal(110),
                                  Decimal(1),
                                  creation_timestamp=1234569970000000)

        self.tracker.add_order(buy_order_1)
        strategy_active_orders.append(buy_order_1)
        self.tracker.add_order(sell_order_1)
        strategy_active_orders.append(sell_order_1)

        self.tracker.update_strategy_orders_with_equivalent_orders()

        buy_hanging_order = next(
            order for order in self.tracker.strategy_current_hanging_orders
            if order.is_buy)
        sell_hanging_order = next(
            order for order in self.tracker.strategy_current_hanging_orders
            if not order.is_buy)

        self.assertEqual(buy_order_1.client_order_id,
                         buy_hanging_order.order_id)
        self.assertEqual(sell_order_1.client_order_id,
                         sell_hanging_order.order_id)
        self.assertEqual(2, len(self.tracker.original_orders))
        self.assertEqual(2, len(self.tracker.strategy_current_hanging_orders))

        # Now we simulate the buy hanging order being fully filled
        strategy_active_orders.remove(buy_order_1)
        self.tracker._did_complete_buy_order(
            MarketEvent.BuyOrderCompleted, self,
            BuyOrderCompletedEvent(timestamp=datetime.now().timestamp(),
                                   order_id=buy_order_1.client_order_id,
                                   base_asset="BTC",
                                   quote_asset="USDT",
                                   base_asset_amount=buy_order_1.quantity,
                                   quote_asset_amount=buy_order_1.quantity *
                                   buy_order_1.price,
                                   order_type=OrderType.LIMIT))

        self.assertEqual(1, len(self.tracker.strategy_current_hanging_orders))
        self.assertNotIn(buy_hanging_order,
                         self.tracker.strategy_current_hanging_orders)
        self.assertEqual(1, len(self.tracker.original_orders))
        self.assertNotIn(buy_order_1, self.tracker.original_orders)
        self.assertTrue(
            self.tracker.is_order_id_in_completed_hanging_orders(
                buy_hanging_order.order_id))
        self.assertFalse(
            self.tracker.is_order_id_in_completed_hanging_orders(
                sell_hanging_order.order_id))
    def test_grouped_hanging_order_and_original_orders_removed_when_hanging_order_completed(self):
        strategy_active_orders = []
        newly_created_buy_orders_ids = ["Order-1234570000000000",
                                        "Order-1234570020000000",
                                        "Order-1234570040000000",
                                        "Order-1234570060000000"]
        newly_created_sell_orders_ids = ["Order-1234570010000000",
                                         "Order-1234570030000000",
                                         "Order-1234570050000000",
                                         "Order-1234570070000000"]

        type(self.strategy).active_orders = PropertyMock(return_value=strategy_active_orders)
        self.strategy.buy_with_specific_market.side_effect = newly_created_buy_orders_ids
        self.strategy.sell_with_specific_market.side_effect = newly_created_sell_orders_ids

        buy_order_1 = LimitOrder("Order-1234569960000000",
                                 "BTC-USDT",
                                 True,
                                 "BTC",
                                 "USDT",
                                 Decimal(101),
                                 Decimal(1))
        buy_order_2 = LimitOrder("Order-1234569980000000",
                                 "BTC-USDT",
                                 True,
                                 "BTC",
                                 "USDT",
                                 Decimal(105),
                                 Decimal(1))

        sell_order_1 = LimitOrder("Order-1234569970000000",
                                  "BTC-USDT",
                                  False,
                                  "BTC",
                                  "USDT",
                                  Decimal(110),
                                  Decimal(1))
        sell_order_2 = LimitOrder("Order-1234569990000000",
                                  "BTC-USDT",
                                  False,
                                  "BTC",
                                  "USDT",
                                  Decimal(115),
                                  Decimal(1))

        self.tracker.set_aggregation_method(HangingOrdersAggregationType.VOLUME_WEIGHTED)

        self.tracker.add_order(buy_order_1)
        strategy_active_orders.append(buy_order_1)
        self.tracker.add_order(buy_order_2)
        strategy_active_orders.append(buy_order_2)
        self.tracker.add_order(sell_order_1)
        strategy_active_orders.append(sell_order_1)
        self.tracker.add_order(sell_order_2)
        strategy_active_orders.append(sell_order_2)

        self.tracker.update_strategy_orders_with_equivalent_orders()

        buy_hanging_order = next(order for order in self.tracker.strategy_current_hanging_orders if order.is_buy)
        sell_hanging_order = next(order for order in self.tracker.strategy_current_hanging_orders if not order.is_buy)
        strategy_active_orders.remove(buy_order_1)
        strategy_active_orders.remove(buy_order_2)
        strategy_active_orders.append(buy_hanging_order)
        strategy_active_orders.append(sell_hanging_order)

        self.assertEqual(4, len(self.tracker.original_orders))
        self.assertEqual(2, len(self.tracker.strategy_current_hanging_orders))

        # Now we simulate the buy hanging order being fully filled
        strategy_active_orders.remove(buy_hanging_order)
        self.tracker._did_complete_buy_order(MarketEvent.BuyOrderCompleted,
                                             self,
                                             BuyOrderCompletedEvent(
                                                 timestamp=datetime.now().timestamp(),
                                                 order_id=buy_hanging_order.order_id,
                                                 base_asset="BTC",
                                                 quote_asset="USDT",
                                                 fee_asset="USDT",
                                                 base_asset_amount=buy_hanging_order.amount,
                                                 quote_asset_amount=buy_hanging_order.amount * buy_hanging_order.price,
                                                 fee_amount=Decimal(0),
                                                 order_type=OrderType.LIMIT))

        self.assertEqual(1, len(self.tracker.strategy_current_hanging_orders))
        self.assertNotIn(buy_hanging_order, self.tracker.strategy_current_hanging_orders)
        self.assertEqual(2, len(self.tracker.original_orders))
        self.assertTrue(all(not order.is_buy for order in self.tracker.original_orders))

        # In this moment the only hanging order in the tracker is the generated by the two sells.
        # We add a new buy hanging order to test that fully filling the sell hanging order only removes the sells

        self.tracker.add_order(buy_order_1)
        strategy_active_orders.append(buy_order_1)

        self.tracker.update_strategy_orders_with_equivalent_orders()

        buy_hanging_order = next(order for order in self.tracker.strategy_current_hanging_orders if order.is_buy)
        strategy_active_orders.remove(buy_order_1)
        strategy_active_orders.append(buy_hanging_order)

        self.assertEqual(3, len(self.tracker.original_orders))
        self.assertEqual(2, len(self.tracker.strategy_current_hanging_orders))

        # Now we simulate the sell hanging order being fully filled
        strategy_active_orders.remove(sell_hanging_order)
        self.tracker._did_complete_sell_order(MarketEvent.BuyOrderCompleted,
                                              self,
                                              BuyOrderCompletedEvent(
                                                  timestamp=datetime.now().timestamp(),
                                                  order_id=sell_hanging_order.order_id,
                                                  base_asset="BTC",
                                                  quote_asset="USDT",
                                                  fee_asset="USDT",
                                                  base_asset_amount=sell_hanging_order.amount,
                                                  quote_asset_amount=sell_hanging_order.amount * sell_hanging_order.price,
                                                  fee_amount=Decimal(0),
                                                  order_type=OrderType.LIMIT))

        self.assertEqual(1, len(self.tracker.strategy_current_hanging_orders))
        self.assertNotIn(sell_hanging_order, self.tracker.strategy_current_hanging_orders)
        self.assertEqual(1, len(self.tracker.original_orders))
        self.assertTrue(all(order.is_buy for order in self.tracker.original_orders))
    def _update_inflight_order(self, tracked_order: BitmexInFlightOrder,
                               event: Dict[str, Any]):
        trading_pair_multiplier = self._trading_pair_to_multipliers[
            tracked_order.trading_pair]
        event["amount_remaining"] = Decimal(str(
            event["leavesQty"])) / trading_pair_multiplier.base_multiplier

        issuable_events: List[MarketEvent] = tracked_order.update(event)

        # Issue relevent events
        for (market_event, new_amount, new_price, new_fee) in issuable_events:
            base, quote = self.split_trading_pair(tracked_order.trading_pair)
            if market_event == MarketEvent.OrderFilled:
                self.trigger_event(
                    ORDER_FILLED_EVENT,
                    OrderFilledEvent(
                        self.current_timestamp, tracked_order.client_order_id,
                        tracked_order.trading_pair, tracked_order.trade_type,
                        tracked_order.order_type, new_price, new_amount,
                        build_trade_fee(self._domain, True, base, quote,
                                        tracked_order.order_type,
                                        tracked_order.trade_type, new_amount,
                                        new_price),
                        tracked_order.client_order_id))
            elif market_event == MarketEvent.OrderCancelled:
                self.logger().info(
                    f"Successfully cancelled order {tracked_order.client_order_id}"
                )
                self.stop_tracking_order(tracked_order.client_order_id)
                self.trigger_event(
                    ORDER_CANCELLED_EVENT,
                    OrderCancelledEvent(self.current_timestamp,
                                        tracked_order.client_order_id))
            elif market_event == MarketEvent.BuyOrderCompleted:
                self.logger().info(
                    f"The market buy order {tracked_order.client_order_id} has completed "
                    f"according to user stream.")
                self.trigger_event(
                    BUY_ORDER_COMPLETED_EVENT,
                    BuyOrderCompletedEvent(self.current_timestamp,
                                           tracked_order.client_order_id, base,
                                           quote,
                                           tracked_order.executed_amount_base,
                                           tracked_order.executed_amount_quote,
                                           tracked_order.order_type,
                                           tracked_order.exchange_order_id))
            elif market_event == MarketEvent.SellOrderCompleted:
                self.logger().info(
                    f"The market sell order {tracked_order.client_order_id} has completed "
                    f"according to user stream.")
                self.trigger_event(
                    SELL_ORDER_COMPLETED_EVENT,
                    SellOrderCompletedEvent(
                        self.current_timestamp, tracked_order.client_order_id,
                        base, quote, tracked_order.executed_amount_base,
                        tracked_order.executed_amount_quote,
                        tracked_order.order_type,
                        tracked_order.exchange_order_id))
            # Complete the order if relevent
            if tracked_order.is_done:
                self.stop_tracking_order(tracked_order.client_order_id)