def serialize(event: PositionEvent): data = { k: v for k, v in event.to_dict(event).items() if k not in ("order_fill", ) } caster = { "net_qty": float, "quantity": float, "peak_qty": float, "avg_px_open": float, "last_qty": float, "last_px": float, "avg_px_close": try_float, "realized_points": try_float, "realized_return": try_float, } values = {k: caster[k](v) if k in caster else v for k, v in data.items()} # type: ignore if "realized_pnl" in values: realized = Money.from_str(values["realized_pnl"]) values["realized_pnl"] = realized.as_double() if "unrealized_pnl" in values: unrealized = Money.from_str(values["unrealized_pnl"]) values["unrealized_pnl"] = unrealized.as_double() return values
def test_from_str_when_malformed_raises_value_error(self): # Arrange value = "@" # Act, Assert with pytest.raises(ValueError): Money.from_str(value)
def _create_engine( self, config: BacktestEngineConfig, venue_configs: List[BacktestVenueConfig], data_configs: List[BacktestDataConfig], ): # Build the backtest engine engine = BacktestEngine(config=config) # Add instruments for config in data_configs: if is_nautilus_class(config.data_type): instruments = config.catalog().instruments( instrument_ids=config.instrument_id, as_nautilus=True) for instrument in instruments or []: engine.add_instrument(instrument) # Add venues for config in venue_configs: engine.add_venue( venue=Venue(config.name), venue_type=VenueType[config.venue_type], oms_type=OMSType[config.oms_type], account_type=AccountType[config.account_type], base_currency=Currency.from_str(config.base_currency), starting_balances=[ Money.from_str(m) for m in config.starting_balances ], book_type=BookTypeParser.from_str_py(config.book_type), ) return engine
def test_from_str_given_valid_strings_returns_expected_result( self, value, expected, ): # Arrange, Act result = Money.from_str(value) # Assert assert result == expected
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, )
def _handle_order_trade_update(self, msg: BinanceFuturesOrderUpdateMsg): data: BinanceFuturesOrderData = msg.o instrument_id: InstrumentId = self._get_cached_instrument_id(data.s) client_order_id = ClientOrderId(data.c) if data.c != "" else None venue_order_id = VenueOrderId(str(data.i)) ts_event = millis_to_nanos(msg.T) # Fetch strategy ID strategy_id: StrategyId = self._cache.strategy_id_for_order( client_order_id) if strategy_id is None: if strategy_id is None: self._generate_external_order_status( instrument_id, client_order_id, venue_order_id, msg.o, ts_event, ) return if data.x == BinanceFuturesExecutionType.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 data.x == BinanceFuturesExecutionType.TRADE: instrument: Instrument = self._instrument_provider.find( instrument_id=instrument_id) # Determine commission if data.N is not None: commission = Money.from_str(f"{data.n} {data.N}") else: # Commission in margin collateral currency commission = Money(0, instrument.quote_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=PositionId( f"{instrument_id}-{data.ps.value}"), trade_id=TradeId(str(data.t)), order_side=OrderSide.BUY if data.S == BinanceOrderSide.BUY else OrderSide.SELL, 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 data.x == BinanceFuturesExecutionType.CANCELED: 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, ) elif data.x == BinanceFuturesExecutionType.EXPIRED: self.generate_order_expired( strategy_id=strategy_id, instrument_id=instrument_id, client_order_id=client_order_id, venue_order_id=venue_order_id, ts_event=ts_event, )