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))
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 ))
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_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)
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)
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()
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)
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)
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)
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"] ) )
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")))
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)
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)
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)
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)
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, ), )
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, ), )
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)
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)
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 ))
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)
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}")
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)