async def test_reconcile_state_no_cached_with_rejected_order(self): # Arrange report = OrderStatusReport( account_id=self.account_id, instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), venue_order_id=VenueOrderId("1"), order_side=OrderSide.BUY, order_type=OrderType.LIMIT, time_in_force=TimeInForce.GTC, order_status=OrderStatus.REJECTED, price=Price.from_str("1.00000"), quantity=Quantity.from_int(10_000), filled_qty=Quantity.from_int(0), post_only=True, cancel_reason="SOME_REASON", report_id=UUID4(), ts_accepted=0, ts_last=0, ts_init=0, ) self.client.add_order_status_report(report) # Act result = await self.exec_engine.reconcile_state() # Assert assert result assert len(self.cache.orders()) == 1 assert self.cache.orders()[0].status == OrderStatus.REJECTED
async def test_reconcile_state_no_cached_with_partially_filled_order_and_no_trade( self): # Arrange venue_order_id = VenueOrderId("1") order_report = OrderStatusReport( account_id=self.account_id, instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), venue_order_id=venue_order_id, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, time_in_force=TimeInForce.GTC, order_status=OrderStatus.PARTIALLY_FILLED, price=Price.from_str("1.00000"), quantity=Quantity.from_int(10_000), filled_qty=Quantity.from_int(5_000), avg_px=Decimal("1.00000"), post_only=True, report_id=UUID4(), ts_accepted=0, ts_triggered=0, ts_last=0, ts_init=0, ) self.client.add_order_status_report(order_report) # Act result = await self.exec_engine.reconcile_state() # Assert assert result assert len(self.cache.orders()) == 1 assert self.cache.orders()[0].status == OrderStatus.PARTIALLY_FILLED
async def test_reconcile_state_no_cached_with_triggered_order(self): # Arrange report = OrderStatusReport( account_id=self.account_id, instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), venue_order_id=VenueOrderId("1"), order_side=OrderSide.BUY, order_type=OrderType.STOP_LIMIT, time_in_force=TimeInForce.GTC, order_status=OrderStatus.TRIGGERED, price=Price.from_str("0.99500"), trigger_price=Price.from_str("1.00000"), trigger_type=TriggerType.BID_ASK, offset_type=TrailingOffsetType.PRICE, quantity=Quantity.from_int(10_000), filled_qty=Quantity.from_int(0), post_only=True, report_id=UUID4(), ts_accepted=1_000_000_000, ts_triggered=2_000_000_000, ts_last=2_000_000_000, ts_init=3_000_000_000, ) self.client.add_order_status_report(report) # Act result = await self.exec_engine.reconcile_state() # Assert assert result assert len(self.cache.orders()) == 1 assert len(self.cache.orders_open()) == 1 assert self.cache.orders()[0].status == OrderStatus.TRIGGERED
def parse_order_status_http( account_id: AccountId, instrument: Instrument, data: Dict[str, Any], report_id: UUID4, ts_init: int, ) -> OrderStatusReport: client_id_str = data.get("clientId") price = data.get("price") avg_px = data["avgFillPrice"] created_at = int( pd.to_datetime(data["createdAt"], utc=True).to_datetime64()) return OrderStatusReport( account_id=account_id, instrument_id=InstrumentId(Symbol(data["market"]), FTX_VENUE), client_order_id=ClientOrderId(client_id_str) if client_id_str is not None else None, venue_order_id=VenueOrderId(str(data["id"])), order_side=OrderSide.BUY if data["side"] == "buy" else OrderSide.SELL, order_type=parse_order_type(data=data, price_str="price"), time_in_force=TimeInForce.IOC if data["ioc"] else TimeInForce.GTC, order_status=parse_order_status(data), price=instrument.make_price(price) if price is not None else None, quantity=instrument.make_qty(data["size"]), filled_qty=instrument.make_qty(data["filledSize"]), avg_px=Decimal(str(avg_px)) if avg_px is not None else None, post_only=data["postOnly"], reduce_only=data["reduceOnly"], report_id=report_id, ts_accepted=created_at, ts_last=created_at, ts_init=ts_init, )
def _generate_external_order_status(self, instrument: Instrument, data: Dict[str, Any]) -> None: client_id_str = data.get("clientId") price = data.get("price") created_at = int( pd.to_datetime(data["createdAt"], utc=True).to_datetime64()) report = OrderStatusReport( account_id=self.account_id, instrument_id=InstrumentId(Symbol(data["market"]), FTX_VENUE), client_order_id=ClientOrderId(client_id_str) if client_id_str is not None else None, venue_order_id=VenueOrderId(str(data["id"])), order_side=OrderSide.BUY if data["side"] == "buy" else OrderSide.SELL, order_type=parse_order_type(data=data, price_str="price"), time_in_force=TimeInForce.IOC if data["ioc"] else TimeInForce.GTC, order_status=OrderStatus.ACCEPTED, price=instrument.make_price(price) if price is not None else None, quantity=instrument.make_qty(data["size"]), filled_qty=instrument.make_qty(0), avg_px=None, post_only=data["postOnly"], reduce_only=data["reduceOnly"], report_id=self._uuid_factory.generate(), ts_accepted=created_at, ts_last=created_at, ts_init=self._clock.timestamp_ns(), ) self._send_order_status_report(report)
def test_add_order_status_reports(self): # Arrange report_id1 = UUID4() mass_status = ExecutionMassStatus( client_id=ClientId("IB"), account_id=AccountId("IB", "U123456789"), venue=Venue("IDEALPRO"), report_id=report_id1, ts_init=0, ) venue_order_id = VenueOrderId("2") report_id2 = UUID4() report = OrderStatusReport( account_id=AccountId("IB", "U123456789"), instrument_id=AUDUSD_IDEALPRO, client_order_id=ClientOrderId("O-123456"), order_list_id=OrderListId("1"), venue_order_id=venue_order_id, order_side=OrderSide.SELL, order_type=OrderType.STOP_LIMIT, contingency_type=ContingencyType.OCO, time_in_force=TimeInForce.DAY, expire_time=None, order_status=OrderStatus.REJECTED, price=Price.from_str("0.90090"), trigger_price=Price.from_str("0.90100"), trigger_type=TriggerType.DEFAULT, limit_offset=None, trailing_offset=Decimal("0.00010"), offset_type=TrailingOffsetType.PRICE, quantity=Quantity.from_int(1_000_000), filled_qty=Quantity.from_int(0), display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason="SOME_REASON", report_id=report_id2, ts_accepted=1_000_000, ts_triggered=0, ts_last=2_000_000, ts_init=3_000_000, ) # Act mass_status.add_order_reports([report]) # Assert assert mass_status.order_reports()[venue_order_id] == report assert ( repr(mass_status) == f"ExecutionMassStatus(client_id=IB, account_id=IB-U123456789, venue=IDEALPRO, order_reports={{VenueOrderId('2'): OrderStatusReport(account_id=IB-U123456789, instrument_id=AUD/USD.IDEALPRO, client_order_id=O-123456, order_list_id=1, venue_order_id=2, order_side=SELL, order_type=STOP_LIMIT, contingency_type=OCO, time_in_force=DAY, expire_time=None, order_status=REJECTED, price=0.90090, trigger_price=0.90100, trigger_type=DEFAULT, limit_offset=None, trailing_offset=0.00010, offset_type=PRICE, quantity=1_000_000, filled_qty=0, leaves_qty=1_000_000, display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason=SOME_REASON, report_id={report_id2}, ts_accepted=1000000, ts_triggered=0, ts_last=2000000, ts_init=3000000)}}, trade_reports={{}}, position_reports={{}}, report_id={report_id1}, ts_init=0)" # noqa ) assert ( repr(report) == f"OrderStatusReport(account_id=IB-U123456789, instrument_id=AUD/USD.IDEALPRO, client_order_id=O-123456, order_list_id=1, venue_order_id=2, order_side=SELL, order_type=STOP_LIMIT, contingency_type=OCO, time_in_force=DAY, expire_time=None, order_status=REJECTED, price=0.90090, trigger_price=0.90100, trigger_type=DEFAULT, limit_offset=None, trailing_offset=0.00010, offset_type=PRICE, quantity=1_000_000, filled_qty=0, leaves_qty=1_000_000, display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason=SOME_REASON, report_id={report_id2}, ts_accepted=1000000, ts_triggered=0, ts_last=2000000, ts_init=3000000)" # noqa )
async def generate_order_status_report(self, order) -> Optional[OrderStatusReport]: return [ OrderStatusReport( client_order_id=ClientOrderId(), venue_order_id=VenueOrderId(), order_status=OrderStatus(), filled_qty=Quantity.zero(), ts_init=int(pd.Timestamp(order["timestamp"]).to_datetime64()), ) for order in self.client().betting.list_current_orders() ["currentOrders"] ]
async def test_reconcile_state_no_cached_with_filled_order_and_trade(self): # Arrange venue_order_id = VenueOrderId("1") order_report = OrderStatusReport( account_id=self.account_id, instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), venue_order_id=venue_order_id, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, time_in_force=TimeInForce.GTC, order_status=OrderStatus.FILLED, price=Price.from_str("1.00000"), quantity=Quantity.from_int(10_000), filled_qty=Quantity.from_int(10_000), avg_px=Decimal("1.00000"), post_only=True, reduce_only=False, report_id=UUID4(), ts_accepted=0, ts_triggered=0, ts_last=0, ts_init=0, ) trade_report = TradeReport( account_id=self.account_id, instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), venue_order_id=venue_order_id, trade_id=TradeId("1"), order_side=OrderSide.BUY, last_qty=Quantity.from_int(10_000), last_px=Price.from_str("1.00000"), commission=Money(0, USD), liquidity_side=LiquiditySide.MAKER, report_id=UUID4(), ts_event=0, ts_init=0, ) self.client.add_order_status_report(order_report) self.client.add_trade_reports(venue_order_id, [trade_report]) # Act result = await self.exec_engine.reconcile_state() # Assert assert result assert len(self.cache.orders()) == 1 assert self.cache.orders()[0].status == OrderStatus.FILLED
def parse_trigger_order_status_http( account_id: AccountId, instrument: Instrument, triggers: Dict[int, VenueOrderId], data: Dict[str, Any], report_id: UUID4, ts_init: int, ) -> OrderStatusReport: order_id = data["id"] parent_order_id = triggers.get(order_id) # Map trigger to parent client_id_str = data.get("clientId") trigger_price = data.get("triggerPrice") order_price = data.get("orderPrice") avg_px = data["avgFillPrice"] triggered_at = data["triggeredAt"] trail_value = data["trailValue"] created_at = int( pd.to_datetime(data["createdAt"], utc=True).to_datetime64()) return OrderStatusReport( account_id=account_id, instrument_id=instrument.id, client_order_id=ClientOrderId(client_id_str) if client_id_str is not None else None, venue_order_id=parent_order_id or VenueOrderId(str(order_id)), order_side=OrderSide.BUY if data["side"] == "buy" else OrderSide.SELL, order_type=parse_order_type(data=data), time_in_force=TimeInForce.GTC, order_status=parse_order_status(data), price=instrument.make_price(order_price) if order_price is not None else None, trigger_price=instrument.make_price(trigger_price) if trigger_price is not None else None, trigger_type=TriggerType.LAST, trailing_offset=Decimal(str(trail_value)) if trail_value is not None else None, offset_type=TrailingOffsetType.PRICE, quantity=instrument.make_qty(data["size"]), filled_qty=instrument.make_qty(data["filledSize"]), avg_px=Decimal(str(avg_px)) if avg_px is not None else None, post_only=False, reduce_only=data["reduceOnly"], report_id=report_id, ts_accepted=created_at, ts_triggered=int( pd.to_datetime(triggered_at, utc=True).to_datetime64()) if triggered_at is not None else 0, ts_last=created_at, ts_init=ts_init, )
def test_instantiate_order_status_report(self): # Arrange, Act report_id = UUID4() report = OrderStatusReport( account_id=AccountId("SIM", "001"), instrument_id=AUDUSD_IDEALPRO, client_order_id=ClientOrderId("O-123456"), order_list_id=OrderListId("1"), venue_order_id=VenueOrderId("2"), order_side=OrderSide.SELL, order_type=OrderType.STOP_LIMIT, contingency_type=ContingencyType.OCO, time_in_force=TimeInForce.DAY, expire_time=None, order_status=OrderStatus.REJECTED, price=Price.from_str("0.90090"), trigger_price=Price.from_str("0.90100"), trigger_type=TriggerType.DEFAULT, limit_offset=None, trailing_offset=Decimal("0.00010"), offset_type=TrailingOffsetType.PRICE, quantity=Quantity.from_int(1_000_000), filled_qty=Quantity.from_int(0), display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason="SOME_REASON", report_id=report_id, ts_accepted=1_000_000, ts_triggered=1_500_000, ts_last=2_000_000, ts_init=3_000_000, ) # Assert assert ( str(report) == f"OrderStatusReport(account_id=SIM-001, instrument_id=AUD/USD.IDEALPRO, client_order_id=O-123456, order_list_id=1, venue_order_id=2, order_side=SELL, order_type=STOP_LIMIT, contingency_type=OCO, time_in_force=DAY, expire_time=None, order_status=REJECTED, price=0.90090, trigger_price=0.90100, trigger_type=DEFAULT, limit_offset=None, trailing_offset=0.00010, offset_type=PRICE, quantity=1_000_000, filled_qty=0, leaves_qty=1_000_000, display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason=SOME_REASON, report_id={report_id}, ts_accepted=1000000, ts_triggered=1500000, ts_last=2000000, ts_init=3000000)" # noqa ) assert ( repr(report) == f"OrderStatusReport(account_id=SIM-001, instrument_id=AUD/USD.IDEALPRO, client_order_id=O-123456, order_list_id=1, venue_order_id=2, order_side=SELL, order_type=STOP_LIMIT, contingency_type=OCO, time_in_force=DAY, expire_time=None, order_status=REJECTED, price=0.90090, trigger_price=0.90100, trigger_type=DEFAULT, limit_offset=None, trailing_offset=0.00010, offset_type=PRICE, quantity=1_000_000, filled_qty=0, leaves_qty=1_000_000, display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason=SOME_REASON, report_id={report_id}, ts_accepted=1000000, ts_triggered=1500000, ts_last=2000000, ts_init=3000000)" # noqa )
def test_handle_order_status_report(self): # Arrange order_report = OrderStatusReport( account_id=AccountId("SIM", "001"), instrument_id=AUDUSD_SIM.id, client_order_id=ClientOrderId("O-123456"), order_list_id=OrderListId("1"), venue_order_id=VenueOrderId("2"), order_side=OrderSide.SELL, order_type=OrderType.STOP_LIMIT, contingency_type=ContingencyType.OCO, time_in_force=TimeInForce.DAY, expire_time=None, order_status=OrderStatus.REJECTED, price=Price.from_str("0.90090"), trigger_price=Price.from_str("0.90100"), trigger_type=TriggerType.DEFAULT, limit_offset=None, trailing_offset=Decimal("0.00010"), offset_type=TrailingOffsetType.PRICE, quantity=Quantity.from_int(1_000_000), filled_qty=Quantity.from_int(0), display_qty=None, avg_px=None, post_only=True, reduce_only=False, cancel_reason="SOME_REASON", report_id=UUID4(), ts_accepted=1_000_000, ts_triggered=1_500_000, ts_last=2_000_000, ts_init=3_000_000, ) # Act self.exec_engine.reconcile_report(order_report) # Assert assert self.exec_engine.report_count == 1
def parse_order_report_http( account_id: AccountId, instrument_id: InstrumentId, data: BinanceFuturesOrder, report_id: UUID4, ts_init: int, ) -> OrderStatusReport: price = Decimal(data.price) trigger_price = Decimal(data.stopPrice) avg_px = Decimal(data.avgPrice) time_in_force = BinanceFuturesTimeInForce(data.timeInForce.upper()) return OrderStatusReport( account_id=account_id, instrument_id=instrument_id, client_order_id=ClientOrderId(data.clientOrderId) if data.clientOrderId != "" else None, venue_order_id=VenueOrderId(str(data.orderId)), order_side=OrderSide[data.side.upper()], order_type=parse_order_type(data.type), time_in_force=parse_time_in_force(time_in_force), order_status=parse_order_status(data.status), price=Price.from_str(data.price) if price is not None else None, quantity=Quantity.from_str(data.origQty), filled_qty=Quantity.from_str(data.executedQty), avg_px=avg_px if avg_px > 0 else None, post_only=time_in_force == BinanceFuturesTimeInForce.GTX, reduce_only=data.reduceOnly, report_id=report_id, ts_accepted=millis_to_nanos(data.time), ts_last=millis_to_nanos(data.updateTime), ts_init=ts_init, trigger_price=Price.from_str(str(trigger_price)) if trigger_price > 0 else None, trigger_type=parse_trigger_type(data.workingType), trailing_offset=Decimal(data.priceRate) * 100 if data.priceRate is not None else None, offset_type=TrailingOffsetType.BASIS_POINTS if data.priceRate is not None else TrailingOffsetType.NONE, )
def _generate_external_order_status( self, instrument_id: InstrumentId, client_order_id: ClientOrderId, venue_order_id: VenueOrderId, data: BinanceFuturesOrderData, ts_event: int, ) -> None: report = OrderStatusReport( account_id=self.account_id, instrument_id=instrument_id, client_order_id=client_order_id, venue_order_id=venue_order_id, order_side=OrderSide.BUY if data.S == BinanceOrderSide.BUY else OrderSide.SELL, order_type=parse_order_type(data.o), time_in_force=parse_time_in_force(data.f), order_status=OrderStatus.ACCEPTED, price=Price.from_str(data.p) if data.p is not None else None, trigger_price=Price.from_str(data.sp) if data.sp is not None else None, trigger_type=parse_trigger_type(data.wt), trailing_offset=Decimal(data.cr) * 100 if data.cr is not None else None, offset_type=TrailingOffsetType.BASIS_POINTS, quantity=Quantity.from_str(data.q), filled_qty=Quantity.from_str(data.z), avg_px=None, post_only=data.f == BinanceFuturesTimeInForce.GTX, reduce_only=data.R, report_id=self._uuid_factory.generate(), ts_accepted=ts_event, ts_last=ts_event, ts_init=self._clock.timestamp_ns(), ) self._send_order_status_report(report)
def parse_order_report_http( account_id: AccountId, instrument_id: InstrumentId, data: Dict[str, Any], report_id: UUID4, ts_init: int, ) -> OrderStatusReport: client_id_str = data.get("clientOrderId") order_type = data["type"].upper() price = data.get("price") trigger_price = Decimal(data["stopPrice"]) avg_px = Decimal(data["price"]) return OrderStatusReport( account_id=account_id, instrument_id=instrument_id, client_order_id=ClientOrderId(client_id_str) if client_id_str is not None else None, venue_order_id=VenueOrderId(str(data["orderId"])), order_side=OrderSide[data["side"].upper()], order_type=parse_order_type(order_type), time_in_force=parse_time_in_force(data["timeInForce"].upper()), order_status=TimeInForce(data["status"].upper()), price=Price.from_str(price) if price is not None else None, quantity=Quantity.from_str(data["origQty"]), filled_qty=Quantity.from_str(data["executedQty"]), avg_px=avg_px if avg_px > 0 else None, post_only=order_type == "LIMIT_MAKER", reduce_only=False, report_id=report_id, ts_accepted=millis_to_nanos(data["time"]), ts_last=millis_to_nanos(data["updateTime"]), ts_init=ts_init, trigger_price=Price.from_str(str(trigger_price)) if trigger_price > 0 else None, trigger_type=TriggerType.LAST if trigger_price > 0 else TriggerType.NONE, )