def test_order_side_parser_given_invalid_value_raises_value_error(self): # Arrange, Act, Assert with pytest.raises(ValueError): OrderSideParser.to_str_py(0) with pytest.raises(ValueError): OrderSideParser.from_str_py("")
def test_order_side_to_str(self, enum, expected): # Arrange # Act result = OrderSideParser.to_str_py(enum) # Assert assert expected == result
async def _submit_trailing_stop_market_order( self, order: TrailingStopMarketOrder) -> None: if order.trigger_type in (TriggerType.DEFAULT, TriggerType.LAST): working_type = "CONTRACT_PRICE" elif order.trigger_type == TriggerType.MARK: working_type = "MARK_PRICE" else: self._log.error( f"Cannot submit order: invalid `order.trigger_type`, was " f"{TriggerTypeParser.to_str_py(order.trigger_price)}. {order}", ) return if order.offset_type not in (TrailingOffsetType.DEFAULT, TrailingOffsetType.BASIS_POINTS): self._log.error( f"Cannot submit order: invalid `order.offset_type`, was " f"{TrailingOffsetTypeParser.to_str_py(order.offset_type)} (use `BASIS_POINTS`). " f"{order}", ) return await self._http_account.new_order( symbol=format_symbol(order.instrument_id.symbol.value), side=OrderSideParser.to_str_py(order.side), type=binance_order_type(order), time_in_force=TimeInForceParser.to_str_py(order.time_in_force), quantity=str(order.quantity), activation_price=str(order.trigger_price), callback_rate=str(order.trailing_offset / 100), working_type=working_type, reduce_only=order. is_reduce_only, # Cannot be sent with Hedge-Mode or closePosition new_client_order_id=order.client_order_id.value, recv_window=5000, )
def test_order_side_from_str(self, string, expected): # Arrange # Act result = OrderSideParser.from_str_py(string) # Assert assert expected == result
async def _submit_stop_limit_order( self, order: StopLimitOrder, position: Optional[Position], ) -> None: order_type = "stop" if position is not None: if order.is_buy and order.trigger_price < position.avg_px_open: order_type = "take_profit" elif order.is_sell and order.trigger_price > position.avg_px_open: order_type = "take_profit" response = await self._http_client.place_trigger_order( market=order.instrument_id.symbol.value, side=OrderSideParser.to_str_py(order.side).lower(), size=str(order.quantity), order_type=order_type, client_id=order.client_order_id.value, price=str(order.price), trigger_price=str(order.trigger_price), reduce_only=order.is_reduce_only, ) self.generate_order_accepted( strategy_id=order.strategy_id, instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=VenueOrderId(str(response["id"])), ts_event=self._clock.timestamp_ns(), )
async def _submit_market_order(self, order: MarketOrder) -> None: await self._http_account.new_order( symbol=format_symbol(order.instrument_id.symbol.value), side=OrderSideParser.to_str_py(order.side), type="MARKET", quantity=str(order.quantity), new_client_order_id=order.client_order_id.value, recv_window=5000, )
async def _submit_market_order(self, order: MarketOrder) -> None: await self._http_client.place_order( market=order.instrument_id.symbol.value, side=OrderSideParser.to_str_py(order.side).lower(), size=str(order.quantity), order_type="market", client_id=order.client_order_id.value, ioc=order.time_in_force == TimeInForce.IOC, reduce_only=order.is_reduce_only, )
def test_make_order_market_on_close(self, side, liability): order = BetfairTestStubs.market_order( time_in_force=TimeInForce.OC, side=OrderSideParser.from_str_py(side)) result = make_order(order) expected = { "marketOnCloseOrder": { "liability": liability }, "orderType": "MARKET_ON_CLOSE", } assert result == expected
async def _submit_stop_limit_order(self, order: StopLimitOrder) -> None: await self._http_account.new_order( symbol=format_symbol(order.instrument_id.symbol.value), side=OrderSideParser.to_str_py(order.side), type=binance_order_type(order), time_in_force=TimeInForceParser.to_str_py(order.time_in_force), quantity=str(order.quantity), price=str(order.price), stop_price=str(order.trigger_price), iceberg_qty=str(order.display_qty) if order.display_qty is not None else None, new_client_order_id=order.client_order_id.value, recv_window=5000, )
def _handle_fills(self, instrument: Instrument, data: Dict[str, Any]) -> None: if data["type"] != "order": self._log.error(f"Fill not for order, {data}") return # Parse identifiers venue_order_id = VenueOrderId(str(data["orderId"])) client_order_id = self._order_ids.get(venue_order_id) if client_order_id is None: client_order_id = ClientOrderId(str(uuid.uuid4())) # TODO(cs): WIP # triggers = await self._http_client.get_trigger_order_triggers(venue_order_id.value) # # for trigger in triggers: # client_order_id = self._open_triggers.get(trigger) # if client_order_id is not None: # break # if client_order_id is None: # client_order_id = ClientOrderId(str(uuid.uuid4())) # Fetch strategy ID strategy_id: StrategyId = self._cache.strategy_id_for_order( client_order_id) if strategy_id is None: self._generate_external_trade_report(instrument, data) return self.generate_order_filled( strategy_id=strategy_id, instrument_id=instrument.id, client_order_id=client_order_id, venue_order_id=venue_order_id, venue_position_id=None, # NETTING accounts trade_id=TradeId(str(data["id"])), # Trade ID order_side=OrderSideParser.from_str_py(data["side"].upper()), order_type=self._order_types[venue_order_id], last_qty=Quantity(data["size"], instrument.size_precision), last_px=Price(data["price"], instrument.price_precision), quote_currency=instrument.quote_currency, commission=Money(data["fee"], Currency.from_str(data["feeCurrency"])), liquidity_side=LiquiditySide.MAKER if data["liquidity"] == "maker" else LiquiditySide.TAKER, ts_event=pd.to_datetime(data["time"], utc=True).to_datetime64(), ) if not self._calculated_account: self._loop.create_task(self._update_account_state())
async def _submit_limit_order(self, order: LimitOrder) -> None: time_in_force = TimeInForceParser.to_str_py(order.time_in_force) if order.is_post_only: time_in_force = "GTX" await self._http_account.new_order( symbol=format_symbol(order.instrument_id.symbol.value), side=OrderSideParser.to_str_py(order.side), type=binance_order_type(order), time_in_force=time_in_force, quantity=str(order.quantity), price=str(order.price), reduce_only=order. is_reduce_only, # Cannot be sent with Hedge-Mode or closePosition new_client_order_id=order.client_order_id.value, recv_window=5000, )
async def _submit_trailing_stop_market( self, order: TrailingStopMarketOrder) -> None: response = await self._http_client.place_trigger_order( market=order.instrument_id.symbol.value, side=OrderSideParser.to_str_py(order.side).lower(), size=str(order.quantity), order_type="trailing_stop", client_id=order.client_order_id.value, trigger_price=str(order.trigger_price), trail_value=str(order.trailing_offset) if order.is_buy else str(-order.trailing_offset), reduce_only=order.is_reduce_only, ) self.generate_order_accepted( strategy_id=order.strategy_id, instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=VenueOrderId(str(response["id"])), ts_event=self._clock.timestamp_ns(), )
def _handle_execution_report(self, data: Dict[str, Any]): execution_type: str = data["x"] instrument_id: InstrumentId = self._get_cached_instrument_id(data["s"]) # Parse client order ID client_order_id_str: str = data["c"] if not client_order_id_str or not client_order_id_str.startswith("O"): client_order_id_str = data["C"] client_order_id = ClientOrderId(client_order_id_str) # Fetch strategy ID strategy_id: StrategyId = self._cache.strategy_id_for_order(client_order_id) if strategy_id is None: # TODO(cs): Implement external order handling self._log.error( f"Cannot handle trade report: strategy ID for {client_order_id} not found.", ) return venue_order_id = VenueOrderId(str(data["i"])) ts_event: int = millis_to_nanos(data["E"]) if execution_type == "NEW": self.generate_order_accepted( strategy_id=strategy_id, instrument_id=instrument_id, client_order_id=client_order_id, venue_order_id=venue_order_id, ts_event=ts_event, ) elif execution_type in "TRADE": instrument: Instrument = self._instrument_provider.find(instrument_id=instrument_id) # Determine commission commission_asset: str = data["N"] commission_amount: str = data["n"] if commission_asset is not None: commission = Money.from_str(f"{commission_amount} {commission_asset}") else: # Binance typically charges commission as base asset or BNB commission = Money(0, instrument.base_currency) self.generate_order_filled( strategy_id=strategy_id, instrument_id=instrument_id, client_order_id=client_order_id, venue_order_id=venue_order_id, venue_position_id=None, # NETTING accounts trade_id=TradeId(str(data["t"])), # Trade ID order_side=OrderSideParser.from_str_py(data["S"]), order_type=parse_order_type(data["o"]), last_qty=Quantity.from_str(data["l"]), last_px=Price.from_str(data["L"]), quote_currency=instrument.quote_currency, commission=commission, liquidity_side=LiquiditySide.MAKER if data["m"] else LiquiditySide.TAKER, ts_event=ts_event, ) elif execution_type == "CANCELED" or execution_type == "EXPIRED": self.generate_order_canceled( strategy_id=strategy_id, instrument_id=instrument_id, client_order_id=client_order_id, venue_order_id=venue_order_id, ts_event=ts_event, )