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))
Beispiel #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
            ))
Beispiel #5
0
    def test_metrics_not_collected_when_convertion_rate_to_volume_token_not_found(
            self):
        mock_rate_oracle = MagicMock()
        mock_rate_oracle.stored_or_live_rate = AsyncMock(return_value=None)

        local_collector = TradeVolumeMetricCollector(
            connector=self.connector_mock,
            activation_interval=10,
            rate_provider=mock_rate_oracle,
            instance_id=self.instance_id)
        local_collector._dispatcher = self.dispatcher_mock

        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.async_run_with_timeout(local_collector.collect_metrics([event]))

        self.dispatcher_mock.request.assert_not_called()
Beispiel #6
0
    def _process_trade_message(self, order_msg: Dict[str, Any]):
        """
        Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
        event if the total executed amount equals to the specified order amount.
        """
        # Only process trade when trade fees have been accounted for; when trade status is "settled".
        if order_msg["status"] != "settled":
            return

        ex_order_id = order_msg["order_id"]

        client_order_id = None
        for track_order in self.in_flight_orders.values():
            if track_order.exchange_order_id == ex_order_id:
                client_order_id = track_order.client_order_id
                break

        if client_order_id is None:
            return

        tracked_order = self.in_flight_orders[client_order_id]
        updated = tracked_order.update_with_trade_update(order_msg)
        if not updated:
            return

        self.trigger_event(
            MarketEvent.OrderFilled,
            OrderFilledEvent(
                self.current_timestamp,
                tracked_order.client_order_id,
                tracked_order.trading_pair,
                tracked_order.trade_type,
                tracked_order.order_type,
                Decimal(str(order_msg["price"])),
                Decimal(str(order_msg["quantity"])),
                AddedToCostTradeFee(flat_fees=[
                    TokenAmount(order_msg["fee_currency_id"],
                                Decimal(str(order_msg["fee_amount"])))
                ]),
                exchange_trade_id=order_msg["id"]))
        if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                tracked_order.executed_amount_base >= tracked_order.amount:
            tracked_order.last_state = "filled"
            self.logger().info(
                f"The {tracked_order.trade_type.name} order "
                f"{tracked_order.client_order_id} has completed "
                f"according to order status API.")
            event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                else MarketEvent.SellOrderCompleted
            event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                else SellOrderCompletedEvent
            self.trigger_event(
                event_tag,
                event_class(
                    self.current_timestamp, tracked_order.client_order_id,
                    tracked_order.base_asset, tracked_order.quote_asset,
                    tracked_order.executed_amount_base,
                    tracked_order.executed_amount_quote,
                    tracked_order.order_type, tracked_order.exchange_order_id))
            self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #7
0
    def test_process_tick_does_not_collect_metrics_if_activation_interval_not_reached(
            self):
        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.metrics_collector._register_fill_event(event)

        last_process_tick_timestamp_copy = self.metrics_collector._last_process_tick_timestamp
        last_executed_collection_process = self.metrics_collector._last_executed_collection_process

        self.metrics_collector.process_tick(timestamp=5)

        self.assertEqual(last_process_tick_timestamp_copy,
                         self.metrics_collector._last_process_tick_timestamp)
        self.assertEqual(
            last_executed_collection_process,
            self.metrics_collector._last_executed_collection_process)
        self.assertIn(event, self.metrics_collector._collected_events)
Beispiel #8
0
    def test_process_order_fill_event_buy(self):
        amount = Decimal("1")
        price = Decimal("9000")
        event = OrderFilledEvent(
            timestamp=1,
            order_id="order1",
            trading_pair=self.trading_pair,
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=price,
            amount=amount,
            trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
        )
        # first event creates DB record
        self.delegate.process_order_fill_event(event)
        with self.trade_fill_sql.get_new_session() as session:
            count = session.query(InventoryCost).count()
            self.assertEqual(count, 1)

            # second event causes update to existing record
            self.delegate.process_order_fill_event(event)
            record = InventoryCost.get_record(session, self.base_asset,
                                              self.quote_asset)
            self.assertEqual(record.base_volume, amount * 2)
            self.assertEqual(record.quote_volume, price * 2)
    def _process_trade_event_message(self, order_msg: Dict[str, Any]):
        """
        Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
        event if the total executed amount equals to the specified order amount.
        :param order_msg: The order event message payload
        """

        client_order_id = str(order_msg["ClientOrderId"])
        if client_order_id in self.in_flight_orders:
            tracked_order = self.in_flight_orders[client_order_id]
            updated = tracked_order.update_with_trade_update(order_msg)

            if updated:
                trade_amount = Decimal(str(order_msg["Quantity"]))
                trade_price = Decimal(str(order_msg["Price"]))
                trade_fee = self.get_fee(base_currency=tracked_order.base_asset,
                                         quote_currency=tracked_order.quote_asset,
                                         order_type=tracked_order.order_type,
                                         order_side=tracked_order.trade_type,
                                         amount=trade_amount,
                                         price=trade_price)
                amount_for_fee = (trade_amount if tracked_order.trade_type is TradeType.BUY
                                  else trade_amount * trade_price)
                tracked_order.fee_paid += amount_for_fee * trade_fee.percent

                self.trigger_event(
                    MarketEvent.OrderFilled,
                    OrderFilledEvent(
                        self.current_timestamp,
                        tracked_order.client_order_id,
                        tracked_order.trading_pair,
                        tracked_order.trade_type,
                        tracked_order.order_type,
                        trade_price,
                        trade_amount,
                        trade_fee,
                        exchange_trade_id=str(order_msg["TradeId"])
                    )
                )
                if (math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or
                        tracked_order.executed_amount_base >= tracked_order.amount):
                    tracked_order.mark_as_filled()
                    self.logger().info(f"The {tracked_order.trade_type.name} order "
                                       f"{tracked_order.client_order_id} has completed "
                                       f"according to order status API")
                    event_tag = (MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY
                                 else MarketEvent.SellOrderCompleted)
                    event_class = (BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY
                                   else SellOrderCompletedEvent)
                    self.trigger_event(event_tag,
                                       event_class(self.current_timestamp,
                                                   tracked_order.client_order_id,
                                                   tracked_order.base_asset,
                                                   tracked_order.quote_asset,
                                                   tracked_order.executed_amount_base,
                                                   tracked_order.executed_amount_quote,
                                                   tracked_order.order_type,
                                                   tracked_order.exchange_order_id))
                    self.stop_tracking_order(tracked_order.client_order_id)
    async def _process_trade_message(self, trade_msg: Dict[str, Any]):
        """
        Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
        event if the total executed amount equals to the specified order amount.
        """
        # I'm not sure why it's needed.
        # for order in self._in_flight_orders.values():
        #     await order.get_exchange_order_id()
        track_order = [o for o in self._in_flight_orders.values() if trade_msg["order_id"] == o.exchange_order_id]
        if not track_order:
            return
        tracked_order = track_order[0]
        updated = tracked_order.update_with_trade_update(trade_msg)
        if not updated:
            return

        quantity = trade_msg["quantity"] if trade_msg.get("quantity") is not None else "0"
        price = trade_msg["price"] if trade_msg.get("price") is not None else "0"
        fee_amount = trade_msg["fee_amount"] if trade_msg.get("fee_amount") is not None else "0"
        fee_amount = "0" if not fee_amount else fee_amount

        self.trigger_event(
            MarketEvent.OrderFilled,
            OrderFilledEvent(
                self.current_timestamp,
                tracked_order.client_order_id,
                tracked_order.trading_pair,
                tracked_order.trade_type,
                tracked_order.order_type,
                Decimal(price),
                Decimal(quantity),
                TradeFee(0.0, [(trade_msg["fee_currency_id"], Decimal(fee_amount))]),
                exchange_trade_id=trade_msg["order_id"]
            )
        )
        if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                tracked_order.executed_amount_base >= tracked_order.amount:
            tracked_order.last_state = "FILLED"
            self.logger().info(f"The {tracked_order.trade_type.name} order "
                               f"{tracked_order.client_order_id} has completed "
                               f"according to order status API.")
            event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                else MarketEvent.SellOrderCompleted
            event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                else SellOrderCompletedEvent
            self.trigger_event(event_tag,
                               event_class(self.current_timestamp,
                                           tracked_order.client_order_id,
                                           tracked_order.base_asset,
                                           tracked_order.quote_asset,
                                           tracked_order.fee_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)
Beispiel #11
0
 async def _process_trade_message_rest(self, trade_msg: Dict[str, Any]):
     """
     Updates in-flight order and trigger order filled event for trade message received from REST API. Triggers order completed
     event if the total executed amount equals to the specified order amount.
     """
     for order in self._in_flight_orders.values():
         await order.get_exchange_order_id()
     track_order = [
         o for o in self._in_flight_orders.values()
         if str(trade_msg["order_id"]) == o.exchange_order_id
     ]
     if not track_order:
         return
     tracked_order = track_order[0]
     (delta_trade_amount, delta_trade_price,
      trade_id) = tracked_order.update_with_trade_update_rest(trade_msg)
     if not delta_trade_amount:
         return
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp,
             tracked_order.client_order_id,
             tracked_order.trading_pair,
             tracked_order.trade_type,
             tracked_order.order_type,
             delta_trade_price,
             delta_trade_amount,
             # TradeFee(0.0, [(trade_msg["fee_coin_name"], Decimal(str(trade_msg["fees"])))]),
             estimate_fee.estimate_fee(
                 self.name, tracked_order.order_type
                 in [OrderType.LIMIT, OrderType.LIMIT_MAKER]),
             exchange_trade_id=trade_id))
     if math.isclose(
             tracked_order.executed_amount_base, tracked_order.amount
     ) or tracked_order.executed_amount_base >= tracked_order.amount:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to trade status rest API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset, tracked_order.fee_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)
Beispiel #12
0
 async def _process_trade_message(self, trade_msg: Dict[str, Any]):
     """
     Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
     event if the total executed amount equals to the specified order amount.
     """
     for order in self._in_flight_orders.values():
         await order.get_exchange_order_id()
     track_order = [
         o for o in self._in_flight_orders.values()
         if trade_msg["order_id"] == o.exchange_order_id
     ]
     if not track_order:
         return
     tracked_order = track_order[0]
     updated = tracked_order.update_with_trade_update(trade_msg)
     if not updated:
         return
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(self.current_timestamp,
                          tracked_order.client_order_id,
                          tracked_order.trading_pair,
                          tracked_order.trade_type,
                          tracked_order.order_type,
                          Decimal(str(trade_msg["traded_price"])),
                          Decimal(str(trade_msg["traded_quantity"])),
                          AddedToCostTradeFee(flat_fees=[
                              TokenAmount(trade_msg["fee_currency"],
                                          Decimal(str(trade_msg["fee"])))
                          ]),
                          exchange_trade_id=str(
                              trade_msg.get("trade_id",
                                            int(self._time() * 1e6)))))
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset,
                         tracked_order.executed_amount_base,
                         tracked_order.executed_amount_quote,
                         tracked_order.order_type))
         self.stop_tracking_order(tracked_order.client_order_id)
 def simulate_order_filled(market_info: MarketTradingPairTuple,
                           order: Union[LimitOrder, MarketOrder]):
     market_info.market.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             time.time(), order.client_order_id if isinstance(
                 order, LimitOrder) else order.order_id, order.trading_pair,
             TradeType.BUY if order.is_buy else TradeType.SELL,
             OrderType.LIMIT if isinstance(order, LimitOrder) else
             OrderType.MARKET, order.price, order.quantity if isinstance(
                 order, LimitOrder) else order.amount, Decimal("1")))
Beispiel #14
0
 async def _update_order_fills_from_trades(self):
     last_tick = self._last_poll_timestamp / self.UPDATE_ORDER_STATUS_MIN_INTERVAL
     current_tick = self.current_timestamp / self.UPDATE_ORDER_STATUS_MIN_INTERVAL
     if current_tick > last_tick and len(self._in_flight_orders) > 0:
         trading_pairs_to_order_map = defaultdict(lambda: {})
         for order in self._in_flight_orders.values():
             trading_pairs_to_order_map[order.trading_pair][order.exchange_order_id] = order
         trading_pairs = list(trading_pairs_to_order_map.keys())
         tasks = [
             self.request(
                 path="/fapi/v1/userTrades",
                 params={
                     "symbol": convert_to_exchange_trading_pair(trading_pair)
                 },
                 is_signed=True,
                 add_timestamp=True
             ) for trading_pair in trading_pairs]
         self.logger().debug(f"Polling for order fills of {len(tasks)} trading_pairs.")
         results = await safe_gather(*tasks, return_exceptions=True)
         for trades, trading_pair in zip(results, trading_pairs):
             order_map = trading_pairs_to_order_map.get(trading_pair)
             if isinstance(trades, Exception):
                 self.logger().network(
                     f"Error fetching trades update for the order {trading_pair}: {trades}.",
                     app_warning_msg=f"Failed to fetch trade update for {trading_pair}."
                 )
                 continue
             for trade in trades:
                 order_id = str(trade.get("orderId"))
                 if order_id in order_map:
                     tracked_order = order_map.get(order_id)
                     order_type = tracked_order.order_type
                     applied_trade = tracked_order.update_with_trade_updates(trade)
                     if applied_trade:
                         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,
                                 order_type,
                                 Decimal(trade.get("price")),
                                 Decimal(trade.get("qty")),
                                 self.get_fee(
                                     tracked_order.base_asset,
                                     tracked_order.quote_asset,
                                     order_type,
                                     tracked_order.trade_type,
                                     Decimal(trade["price"]),
                                     Decimal(trade["qty"])),
                                 exchange_trade_id=trade["id"]
                             )
                         )
 async def update_swap_order(self, update_result: Dict[str, any],
                             tracked_order: UniswapInFlightOrder):
     if update_result.get("confirmed", False):
         if update_result["receipt"].get("status", 0) == 1:
             order_id = await tracked_order.get_exchange_order_id()
             gas_used = update_result["receipt"]["gasUsed"]
             gas_price = tracked_order.gas_price
             fee = Decimal(str(gas_used)) * Decimal(
                 str(gas_price)) / Decimal(str(1e9))
             self.trigger_event(
                 MarketEvent.OrderFilled,
                 OrderFilledEvent(self.current_timestamp,
                                  tracked_order.client_order_id,
                                  tracked_order.trading_pair,
                                  tracked_order.trade_type,
                                  tracked_order.order_type,
                                  Decimal(str(tracked_order.price)),
                                  Decimal(str(tracked_order.amount)),
                                  AddedToCostTradeFee(flat_fees=[
                                      TokenAmount(tracked_order.fee_asset,
                                                  Decimal(str(fee)))
                                  ]),
                                  exchange_trade_id=order_id))
             tracked_order.last_state = "FILLED"
             self.logger().info(
                 f"The {tracked_order.trade_type.name} order "
                 f"{tracked_order.client_order_id} has completed "
                 f"according to order status API.")
             event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                 else MarketEvent.SellOrderCompleted
             event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                 else SellOrderCompletedEvent
             self.trigger_event(
                 event_tag,
                 event_class(self.current_timestamp,
                             tracked_order.client_order_id,
                             tracked_order.base_asset,
                             tracked_order.quote_asset,
                             tracked_order.fee_asset,
                             tracked_order.executed_amount_base,
                             tracked_order.executed_amount_quote,
                             float(fee), tracked_order.order_type))
             self.stop_tracking_order(tracked_order.client_order_id)
         else:
             self.logger().info(
                 f"The {tracked_order.type} order {tracked_order.client_order_id} has failed according to order status API. "
             )
             self.trigger_event(
                 MarketEvent.OrderFailure,
                 MarketOrderFailureEvent(self.current_timestamp,
                                         tracked_order.client_order_id,
                                         tracked_order.order_type))
             self.stop_tracking_order(tracked_order.client_order_id)
    def _process_rest_trade_details(self, order_detail_msg: Any):
        for trade_msg in order_detail_msg['detail']:
            """
            Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
            event if the total executed amount equals to the specified order amount.
            """
            # for order in self._in_flight_orders.values():
            #     await order.get_exchange_order_id()
            tracked_order = self.find_exchange_order(trade_msg['order_id'])
            if tracked_order is None:
                return

            updated = tracked_order.update_with_rest_order_detail(trade_msg)
            if not updated:
                return

            self.trigger_event(
                MarketEvent.OrderFilled,
                OrderFilledEvent(
                    self.current_timestamp,
                    tracked_order.client_order_id,
                    tracked_order.trading_pair,
                    tracked_order.trade_type,
                    tracked_order.order_type,
                    Decimal(str(trade_msg["executed_price"])),
                    Decimal(str(trade_msg["executed_amount"])),
                    estimate_fee(
                        self.name, tracked_order.order_type
                        in [OrderType.LIMIT, OrderType.LIMIT_MAKER]),
                    # TradeFee(0.0, [(trade_msg["fee_currency"], Decimal(str(trade_msg["fee"])))]),
                    exchange_trade_id=trade_msg["tid"]))
            if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                    tracked_order.executed_amount_base >= tracked_order.amount:
                tracked_order.last_state = "FILLED"
                self.logger().info(
                    f"The {tracked_order.trade_type.name} order "
                    f"{tracked_order.client_order_id} has completed "
                    f"according to order status API.")
                event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                    else MarketEvent.SellOrderCompleted
                event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                    else SellOrderCompletedEvent
                self.trigger_event(
                    event_tag,
                    event_class(self.current_timestamp,
                                tracked_order.client_order_id,
                                tracked_order.base_asset,
                                tracked_order.quote_asset,
                                tracked_order.executed_amount_base,
                                tracked_order.executed_amount_quote,
                                tracked_order.order_type))
                self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #17
0
    def test_create_order_and_process_fill(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)

        fill_event = OrderFilledEvent(timestamp=1642020000,
                                      order_id=create_event.order_id,
                                      trading_pair=create_event.trading_pair,
                                      trade_type=TradeType.BUY,
                                      order_type=create_event.type,
                                      price=Decimal(1010),
                                      amount=create_event.amount,
                                      trade_fee=AddedToCostTradeFee(),
                                      exchange_trade_id="TradeId1")

        recorder._did_fill_order(MarketEvent.OrderFilled.value, self,
                                 fill_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.OrderFilled.name, order_status[1].status)
        self.assertEqual(1, len(trade_fills))
        self.assertEqual(self.config_file_path,
                         trade_fills[0].config_file_path)
        self.assertEqual(fill_event.order_id, trade_fills[0].order_id)
Beispiel #18
0
    def test_collect_metrics_for_single_event(self):
        self.rate_oracle._prices = {"HBOT-USDT": Decimal("100")}

        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.async_run_with_timeout(
            self.metrics_collector.collect_metrics([event]))

        expected_dispatch_request = {
            "url": f"{self.metrics_collector_url}/client_metrics",
            "method": "POST",
            "request_obj": {
                "headers": {
                    'Content-Type': "application/json"
                },
                "data":
                json.dumps({
                    "source": "hummingbot",
                    "name": TradeVolumeMetricCollector.METRIC_NAME,
                    "instance_id": self.instance_id,
                    "exchange": self.connector_name,
                    "version": self.client_version,
                    "system":
                    f"{platform.system()} {platform.release()}({platform.platform()})",
                    "value": str(event.amount * event.price * 100)
                }),
                "params": {
                    "ddtags":
                    f"instance_id:{self.instance_id},"
                    f"client_version:{self.client_version},"
                    f"type:metrics",
                    "ddsource":
                    "hummingbot-client"
                }
            }
        }

        self.dispatcher_mock.request.assert_called()
        dispatched_metric = self.dispatcher_mock.request.call_args[0][0]

        self.assertEqual(expected_dispatch_request, dispatched_metric)
Beispiel #19
0
 def test_process_order_fill_event_sell_no_initial_cost_set(self):
     amount = Decimal("1")
     price = Decimal("9000")
     event = OrderFilledEvent(
         timestamp=1,
         order_id="order1",
         trading_pair=self.trading_pair,
         trade_type=TradeType.SELL,
         order_type=OrderType.LIMIT,
         price=price,
         amount=amount,
         trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
     )
     with self.assertRaises(RuntimeError):
         self.delegate.process_order_fill_event(event)
    def _process_order_message_traded(self, order_msg):
        tracked_order: DigifinexInFlightOrder = self.find_exchange_order(
            order_msg['id'])
        if tracked_order is None:
            return

        (delta_trade_amount,
         delta_trade_price) = tracked_order.update_with_order_update(order_msg)
        if not delta_trade_amount:
            return

        self.trigger_event(
            MarketEvent.OrderFilled,
            OrderFilledEvent(
                self.current_timestamp,
                tracked_order.client_order_id,
                tracked_order.trading_pair,
                tracked_order.trade_type,
                tracked_order.order_type,
                delta_trade_price,
                delta_trade_amount,
                estimate_fee(
                    self.name, tracked_order.order_type
                    in [OrderType.LIMIT, OrderType.LIMIT_MAKER]),
                # TradeFee(0.0, [(trade_msg["fee_currency"], Decimal(str(trade_msg["fee"])))]),
                exchange_trade_id=str(int(self._time() * 1e6))))
        if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                tracked_order.executed_amount_base >= tracked_order.amount:
            tracked_order.last_state = "2"
            self.logger().info(
                f"The {tracked_order.trade_type.name} order "
                f"{tracked_order.client_order_id} has completed "
                f"according to order status API.")
            event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                else MarketEvent.SellOrderCompleted
            event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                else SellOrderCompletedEvent
            self.trigger_event(
                event_tag,
                event_class(self.current_timestamp,
                            tracked_order.client_order_id,
                            tracked_order.base_asset,
                            tracked_order.quote_asset,
                            tracked_order.executed_amount_base,
                            tracked_order.executed_amount_quote,
                            tracked_order.order_type))
            self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #21
0
    def test_fill_events_created_from_order_book_rows_have_unique_trade_ids(
            self):
        rows = [
            OrderBookRow(Decimal(1000), Decimal(1), 1),
            OrderBookRow(Decimal(1001), Decimal(2), 2)
        ]
        fill_events = OrderFilledEvent.order_filled_events_from_order_book_rows(
            timestamp=1640001112.223,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            trade_fee=AddedToCostTradeFee(),
            order_book_rows=rows)

        self.assertEqual("OID1_0", fill_events[0].exchange_trade_id)
        self.assertEqual("OID1_1", fill_events[1].exchange_trade_id)
 def _trigger_filled_event(self, order: InFlightOrder):
     self._connector.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             timestamp=self.current_timestamp,
             order_id=order.client_order_id,
             trading_pair=order.trading_pair,
             trade_type=order.trade_type,
             order_type=order.order_type,
             price=order.last_filled_price,
             amount=order.last_filled_amount,
             trade_fee=order.latest_trade_fee,
             exchange_trade_id=str(order.last_trade_id),
             leverage=int(order.leverage),
             position=order.position.name,
         ),
     )
Beispiel #23
0
 def _trigger_filled_event(self, order: InFlightOrder, fill_amount: Decimal,
                           fill_price: Decimal, fill_fee: TradeFeeBase,
                           trade_id: str):
     self._connector.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             timestamp=self.current_timestamp,
             order_id=order.client_order_id,
             trading_pair=order.trading_pair,
             trade_type=order.trade_type,
             order_type=order.order_type,
             price=fill_price,
             amount=fill_amount,
             trade_fee=fill_fee,
             exchange_trade_id=trade_id,
             leverage=int(order.leverage),
             position=order.position.name,
         ),
     )
Beispiel #24
0
 async def _trigger_order_fill(self,
                               tracked_order: AltmarketsInFlightOrder,
                               update_msg: Dict[str, Any]):
     executed_price = Decimal(str(update_msg.get("price")
                                  if update_msg.get("price") is not None
                                  else update_msg.get("avg_price", "0")))
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp,
             tracked_order.client_order_id,
             tracked_order.trading_pair,
             tracked_order.trade_type,
             tracked_order.order_type,
             executed_price,
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(percent=update_msg["trade_fee"]),
             update_msg.get("exchange_trade_id", update_msg.get("id", update_msg.get("order_id")))
         )
     )
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             (not tracked_order.is_cancelled and tracked_order.is_done):
         tracked_order.last_state = "done"
         self.logger().info(f"The {tracked_order.trade_type.name} order "
                            f"{tracked_order.client_order_id} has completed "
                            f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(event_tag,
                            event_class(self.current_timestamp,
                                        tracked_order.client_order_id,
                                        tracked_order.base_asset,
                                        tracked_order.quote_asset,
                                        tracked_order.fee_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)
 def _trigger_order_fill(self, tracked_order: GateIoInFlightOrder,
                         update_msg: Dict[str, Any]):
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp, tracked_order.client_order_id,
             tracked_order.trading_pair, tracked_order.trade_type,
             tracked_order.order_type,
             Decimal(
                 str(
                     update_msg.get("fill_price",
                                    update_msg.get("price", "0")))),
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(flat_fees=[
                 TokenAmount(tracked_order.fee_asset,
                             tracked_order.fee_paid)
             ]), str(update_msg.get("update_time_ms",
                                    update_msg.get("id")))))
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             tracked_order.is_done:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset, tracked_order.fee_asset,
                         tracked_order.executed_amount_base,
                         tracked_order.executed_amount_quote,
                         tracked_order.fee_paid, tracked_order.order_type,
                         tracked_order.exchange_order_id))
         self.stop_tracking_order(tracked_order.client_order_id)
 async def _trigger_order_fill(self, tracked_order: CoinzoomInFlightOrder,
                               update_msg: Dict[str, Any]):
     self.trigger_event(
         MarketEvent.OrderFilled,
         OrderFilledEvent(
             self.current_timestamp, tracked_order.client_order_id,
             tracked_order.trading_pair, tracked_order.trade_type,
             tracked_order.order_type,
             Decimal(
                 str(
                     update_msg.get("averagePrice",
                                    update_msg.get("price", "0")))),
             tracked_order.executed_amount_base,
             AddedToCostTradeFee(percent=update_msg["trade_fee"]),
             update_msg.get("exchange_trade_id",
                            update_msg.get("id",
                                           update_msg.get("orderId")))))
     if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
             tracked_order.executed_amount_base >= tracked_order.amount or \
             tracked_order.is_done:
         tracked_order.last_state = "FILLED"
         self.logger().info(
             f"The {tracked_order.trade_type.name} order "
             f"{tracked_order.client_order_id} has completed "
             f"according to order status API.")
         event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
             else MarketEvent.SellOrderCompleted
         event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
             else SellOrderCompletedEvent
         await asyncio.sleep(0.1)
         self.trigger_event(
             event_tag,
             event_class(self.current_timestamp,
                         tracked_order.client_order_id,
                         tracked_order.base_asset,
                         tracked_order.quote_asset,
                         tracked_order.executed_amount_base,
                         tracked_order.executed_amount_quote,
                         tracked_order.order_type))
         self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #27
0
    def test_process_order_fill_event_sell(self):
        amount = Decimal("1")
        price = Decimal("9000")

        # Test when no records
        self.assertIsNone(self.delegate.get_price())

        with self.trade_fill_sql.get_new_session() as session:
            with session.begin():
                record = InventoryCost(
                    base_asset=self.base_asset,
                    quote_asset=self.quote_asset,
                    base_volume=amount,
                    quote_volume=amount * price,
                )
                session.add(record)

        amount_sell = Decimal("0.5")
        price_sell = Decimal("10000")
        event = OrderFilledEvent(
            timestamp=1,
            order_id="order1",
            trading_pair=self.trading_pair,
            trade_type=TradeType.SELL,
            order_type=OrderType.LIMIT,
            price=price_sell,
            amount=amount_sell,
            trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
        )

        self.delegate.process_order_fill_event(event)
        with self.trade_fill_sql.get_new_session() as session:
            record = InventoryCost.get_record(session, self.base_asset,
                                              self.quote_asset)
            # Remaining base volume reduced by sold amount
            self.assertEqual(record.base_volume, amount - amount_sell)
            # Remaining quote volume has been reduced using original price
            self.assertEqual(record.quote_volume, amount_sell * price)
Beispiel #28
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
            ))
Beispiel #29
0
    async def _update_order_fills_from_trades(self):
        """
        This is intended to be a backup measure to get filled events with trade ID for orders,
        in case Binance's user stream events are not working.
        NOTE: It is not required to copy this functionality in other connectors.
        This is separated from _update_order_status which only updates the order status without producing filled
        events, since Binance's get order endpoint does not return trade IDs.
        The minimum poll interval for order status is 10 seconds.
        """
        small_interval_last_tick = self._last_poll_timestamp / self.UPDATE_ORDER_STATUS_MIN_INTERVAL
        small_interval_current_tick = self.current_timestamp / self.UPDATE_ORDER_STATUS_MIN_INTERVAL
        long_interval_last_tick = self._last_poll_timestamp / self.LONG_POLL_INTERVAL
        long_interval_current_tick = self.current_timestamp / self.LONG_POLL_INTERVAL

        if (long_interval_current_tick > long_interval_last_tick or
            (self.in_flight_orders
             and small_interval_current_tick > small_interval_last_tick)):
            query_time = int(self._last_trades_poll_binance_timestamp * 1e3)
            self._last_trades_poll_binance_timestamp = self._binance_time_synchronizer.time(
            )
            order_by_exchange_id_map = {}
            for order in self._order_tracker.all_orders.values():
                order_by_exchange_id_map[order.exchange_order_id] = order

            tasks = []
            trading_pairs = self._order_book_tracker._trading_pairs
            for trading_pair in trading_pairs:
                params = {
                    "symbol":
                    await BinanceAPIOrderBookDataSource.
                    exchange_symbol_associated_to_pair(
                        trading_pair=trading_pair,
                        domain=self._domain,
                        api_factory=self._api_factory,
                        throttler=self._throttler)
                }
                if self._last_poll_timestamp > 0:
                    params["startTime"] = query_time
                tasks.append(
                    self._api_request(method=RESTMethod.GET,
                                      path_url=CONSTANTS.MY_TRADES_PATH_URL,
                                      params=params,
                                      is_auth_required=True))

            self.logger().debug(
                f"Polling for order fills of {len(tasks)} trading pairs.")
            results = await safe_gather(*tasks, return_exceptions=True)

            for trades, trading_pair in zip(results, trading_pairs):

                if isinstance(trades, Exception):
                    self.logger().network(
                        f"Error fetching trades update for the order {trading_pair}: {trades}.",
                        app_warning_msg=
                        f"Failed to fetch trade update for {trading_pair}.")
                    continue
                for trade in trades:
                    exchange_order_id = str(trade["orderId"])
                    if exchange_order_id in order_by_exchange_id_map:
                        # This is a fill for a tracked order
                        tracked_order = order_by_exchange_id_map[
                            exchange_order_id]
                        trade_update = TradeUpdate(
                            trade_id=str(trade["id"]),
                            client_order_id=tracked_order.client_order_id,
                            exchange_order_id=exchange_order_id,
                            trading_pair=trading_pair,
                            fee_asset=trade["commissionAsset"],
                            fee_paid=Decimal(trade["commission"]),
                            fill_base_amount=Decimal(trade["qty"]),
                            fill_quote_amount=Decimal(trade["quoteQty"]),
                            fill_price=Decimal(trade["price"]),
                            fill_timestamp=int(trade["time"]),
                        )
                        self._order_tracker.process_trade_update(trade_update)
                    elif self.is_confirmed_new_order_filled_event(
                            str(trade["id"]), exchange_order_id, trading_pair):
                        # This is a fill of an order registered in the DB but not tracked any more
                        self._current_trade_fills.add(
                            TradeFillOrderDetails(market=self.display_name,
                                                  exchange_trade_id=str(
                                                      trade["id"]),
                                                  symbol=trading_pair))
                        self.trigger_event(
                            MarketEvent.OrderFilled,
                            OrderFilledEvent(
                                timestamp=float(trade["time"]) * 1e-3,
                                order_id=self._exchange_order_ids.get(
                                    str(trade["orderId"]), None),
                                trading_pair=trading_pair,
                                trade_type=TradeType.BUY
                                if trade["isBuyer"] else TradeType.SELL,
                                order_type=OrderType.LIMIT_MAKER
                                if trade["isMaker"] else OrderType.LIMIT,
                                price=Decimal(trade["price"]),
                                amount=Decimal(trade["qty"]),
                                trade_fee=DeductedFromReturnsTradeFee(
                                    flat_fees=[
                                        TokenAmount(
                                            trade["commissionAsset"],
                                            Decimal(trade["commission"]))
                                    ]),
                                exchange_trade_id=str(trade["id"])))
                        self.logger().info(
                            f"Recreating missing trade in TradeFill: {trade}")
Beispiel #30
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)