From 6f9686565e05360b262b40e39c0f3b0469508291 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 8 Apr 2024 19:40:18 +1000 Subject: [PATCH] Implement custom client IDs --- nautilus_trader/adapters/betfair/factories.py | 4 +- .../adapters/binance/common/data.py | 5 +- .../adapters/binance/common/execution.py | 7 ++- nautilus_trader/adapters/binance/factories.py | 10 ++-- .../adapters/binance/futures/data.py | 8 +++- .../adapters/binance/futures/execution.py | 8 +++- .../adapters/binance/futures/providers.py | 1 + nautilus_trader/adapters/binance/spot/data.py | 8 +++- .../adapters/binance/spot/execution.py | 8 +++- nautilus_trader/adapters/bybit/data.py | 5 +- nautilus_trader/adapters/bybit/execution.py | 7 ++- nautilus_trader/adapters/bybit/factories.py | 6 ++- nautilus_trader/adapters/databento/data.py | 8 +++- .../adapters/databento/factories.py | 1 + .../adapters/interactive_brokers/data.py | 48 +++++++++---------- .../adapters/interactive_brokers/execution.py | 5 +- .../adapters/interactive_brokers/factories.py | 6 ++- nautilus_trader/adapters/sandbox/factory.py | 4 +- nautilus_trader/live/factories.py | 4 +- 19 files changed, 99 insertions(+), 54 deletions(-) diff --git a/nautilus_trader/adapters/betfair/factories.py b/nautilus_trader/adapters/betfair/factories.py index b71daa8ac465..7919ac430961 100644 --- a/nautilus_trader/adapters/betfair/factories.py +++ b/nautilus_trader/adapters/betfair/factories.py @@ -140,7 +140,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, Any] The configuration dictionary. msgbus : MessageBus @@ -201,7 +201,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, Any] The configuration for the client. msgbus : MessageBus diff --git a/nautilus_trader/adapters/binance/common/data.py b/nautilus_trader/adapters/binance/common/data.py index 780fb5d33659..748e86f6550c 100644 --- a/nautilus_trader/adapters/binance/common/data.py +++ b/nautilus_trader/adapters/binance/common/data.py @@ -101,6 +101,8 @@ class BinanceCommonDataClient(LiveMarketDataClient): The account type for the client. base_url_ws : str The base url for the WebSocket client. + name : str, optional + The custom client ID. config : BinanceDataClientConfig The configuration for the client. @@ -122,11 +124,12 @@ def __init__( instrument_provider: InstrumentProvider, account_type: BinanceAccountType, base_url_ws: str, + name: str | None, config: BinanceDataClientConfig, ) -> None: super().__init__( loop=loop, - client_id=ClientId(BINANCE_VENUE.value), + client_id=ClientId(name or BINANCE_VENUE.value), venue=BINANCE_VENUE, msgbus=msgbus, cache=cache, diff --git a/nautilus_trader/adapters/binance/common/execution.py b/nautilus_trader/adapters/binance/common/execution.py index acba6cbd9349..3be952589fb9 100644 --- a/nautilus_trader/adapters/binance/common/execution.py +++ b/nautilus_trader/adapters/binance/common/execution.py @@ -108,6 +108,8 @@ class BinanceCommonExecutionClient(LiveExecutionClient): The account type for the client. base_url_ws : str The base URL for the WebSocket client. + name : str, optional + The custom client ID. config : BinanceExecClientConfig The configuration for the client. @@ -131,11 +133,12 @@ def __init__( instrument_provider: InstrumentProvider, account_type: BinanceAccountType, base_url_ws: str, + name: str | None, config: BinanceExecClientConfig, ) -> None: super().__init__( loop=loop, - client_id=ClientId(BINANCE_VENUE.value), + client_id=ClientId(name or BINANCE_VENUE.value), venue=BINANCE_VENUE, oms_type=OmsType.HEDGING if account_type.is_futures else OmsType.NETTING, instrument_provider=instrument_provider, @@ -162,7 +165,7 @@ def __init__( self._log.info(f"{config.max_retries=}", LogColor.BLUE) self._log.info(f"{config.retry_delay=}", LogColor.BLUE) - self._set_account_id(AccountId(f"{BINANCE_VENUE.value}-spot-master")) + self._set_account_id(AccountId(f"{name or BINANCE_VENUE.value}-spot-master")) # Enum parser self._enum_parser = enum_parser diff --git a/nautilus_trader/adapters/binance/factories.py b/nautilus_trader/adapters/binance/factories.py index 864ccb6f0fe6..1afcc23fbe68 100644 --- a/nautilus_trader/adapters/binance/factories.py +++ b/nautilus_trader/adapters/binance/factories.py @@ -102,7 +102,7 @@ def get_cached_binance_http_client( ("allOrders", Quota.rate_per_minute(int(1200 / 20))), ] - client_key: str = "|".join((key, secret)) + client_key: str = "|".join((account_type.value, key, secret)) if client_key not in BINANCE_HTTP_CLIENTS: client = BinanceHttpClient( clock=clock, @@ -214,7 +214,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : BinanceDataClientConfig The client configuration. msgbus : MessageBus @@ -271,6 +271,7 @@ def create( # type: ignore instrument_provider=provider, account_type=config.account_type, base_url_ws=config.base_url_ws or default_base_url_ws, + name=name, config=config, ) else: @@ -291,6 +292,7 @@ def create( # type: ignore instrument_provider=provider, account_type=config.account_type, base_url_ws=config.base_url_ws or default_base_url_ws, + name=name, config=config, ) @@ -317,7 +319,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : BinanceExecClientConfig The configuration for the client. msgbus : MessageBus @@ -374,6 +376,7 @@ def create( # type: ignore instrument_provider=provider, base_url_ws=config.base_url_ws or default_base_url_ws, account_type=config.account_type, + name=name, config=config, ) else: @@ -394,5 +397,6 @@ def create( # type: ignore instrument_provider=provider, base_url_ws=config.base_url_ws or default_base_url_ws, account_type=config.account_type, + name=name, config=config, ) diff --git a/nautilus_trader/adapters/binance/futures/data.py b/nautilus_trader/adapters/binance/futures/data.py index 815a51e9de02..ef1bca1c67eb 100644 --- a/nautilus_trader/adapters/binance/futures/data.py +++ b/nautilus_trader/adapters/binance/futures/data.py @@ -59,10 +59,12 @@ class BinanceFuturesDataClient(BinanceCommonDataClient): The instrument provider. base_url_ws : str The base URL for the WebSocket client. - account_type : BinanceAccountType - The account type for the client. config : BinanceDataClientConfig The configuration for the client. + account_type : BinanceAccountType, default 'USDT_FUTURE' + The account type for the client. + name : str, optional + The custom client ID. """ @@ -77,6 +79,7 @@ def __init__( base_url_ws: str, config: BinanceDataClientConfig, account_type: BinanceAccountType = BinanceAccountType.USDT_FUTURE, + name: str | None = None, ): PyCondition.true( account_type.is_futures, @@ -101,6 +104,7 @@ def __init__( instrument_provider=instrument_provider, account_type=account_type, base_url_ws=base_url_ws, + name=name, config=config, ) diff --git a/nautilus_trader/adapters/binance/futures/execution.py b/nautilus_trader/adapters/binance/futures/execution.py index 75f831bc4264..35aecf08490b 100644 --- a/nautilus_trader/adapters/binance/futures/execution.py +++ b/nautilus_trader/adapters/binance/futures/execution.py @@ -72,10 +72,12 @@ class BinanceFuturesExecutionClient(BinanceCommonExecutionClient): The instrument provider. base_url_ws : str The base URL for the WebSocket client. - account_type : BinanceAccountType - The account type for the client. config : BinanceExecClientConfig The configuration for the client. + account_type : BinanceAccountType, default 'USDT_FUTURE' + The account type for the client. + name : str, optional + The custom client ID. """ @@ -90,6 +92,7 @@ def __init__( base_url_ws: str, config: BinanceExecClientConfig, account_type: BinanceAccountType = BinanceAccountType.USDT_FUTURE, + name: str | None = None, ): PyCondition.true( account_type.is_futures, @@ -118,6 +121,7 @@ def __init__( instrument_provider=instrument_provider, account_type=account_type, base_url_ws=base_url_ws, + name=name, config=config, ) diff --git a/nautilus_trader/adapters/binance/futures/providers.py b/nautilus_trader/adapters/binance/futures/providers.py index 39d2c4e155c7..3162ea6f9884 100644 --- a/nautilus_trader/adapters/binance/futures/providers.py +++ b/nautilus_trader/adapters/binance/futures/providers.py @@ -334,6 +334,7 @@ def _parse_instrument( underlying=base_currency, quote_currency=quote_currency, settlement_currency=settlement_currency, + is_inverse=False, # No inverse instruments trade on Binance activation_ns=activation.value, expiration_ns=expiration.value, price_precision=price_precision, diff --git a/nautilus_trader/adapters/binance/spot/data.py b/nautilus_trader/adapters/binance/spot/data.py index 5b88aee29f64..5d929673bd92 100644 --- a/nautilus_trader/adapters/binance/spot/data.py +++ b/nautilus_trader/adapters/binance/spot/data.py @@ -56,10 +56,12 @@ class BinanceSpotDataClient(BinanceCommonDataClient): The instrument provider. base_url_ws : str The base URL for the WebSocket client. - account_type : BinanceAccountType - The account type for the client. config : BinanceDataClientConfig The configuration for the client. + account_type : BinanceAccountType, default 'SPOT' + The account type for the client. + name : str, optional + The custom client ID. """ @@ -74,6 +76,7 @@ def __init__( base_url_ws: str, config: BinanceDataClientConfig, account_type: BinanceAccountType = BinanceAccountType.SPOT, + name: str | None = None, ): PyCondition.true( account_type.is_spot_or_margin, @@ -97,6 +100,7 @@ def __init__( instrument_provider=instrument_provider, account_type=account_type, base_url_ws=base_url_ws, + name=name, config=config, ) diff --git a/nautilus_trader/adapters/binance/spot/execution.py b/nautilus_trader/adapters/binance/spot/execution.py index a7c03f139f21..467bfa640d16 100644 --- a/nautilus_trader/adapters/binance/spot/execution.py +++ b/nautilus_trader/adapters/binance/spot/execution.py @@ -65,10 +65,12 @@ class BinanceSpotExecutionClient(BinanceCommonExecutionClient): The instrument provider. base_url_ws : str The base URL for the WebSocket client. - account_type : BinanceAccountType - The account type for the client. config : BinanceExecClientConfig The configuration for the client. + account_type : BinanceAccountType, default 'SPOT' + The account type for the client. + name : str, optional + The custom client ID. """ @@ -83,6 +85,7 @@ def __init__( base_url_ws: str, config: BinanceExecClientConfig, account_type: BinanceAccountType = BinanceAccountType.SPOT, + name: str | None = None, ): PyCondition.true( account_type.is_spot_or_margin, @@ -111,6 +114,7 @@ def __init__( instrument_provider=instrument_provider, account_type=account_type, base_url_ws=base_url_ws, + name=name, config=config, ) diff --git a/nautilus_trader/adapters/bybit/data.py b/nautilus_trader/adapters/bybit/data.py index cb9c7b8cf613..c4e07b0f8d8d 100644 --- a/nautilus_trader/adapters/bybit/data.py +++ b/nautilus_trader/adapters/bybit/data.py @@ -94,6 +94,8 @@ class BybitDataClient(LiveMarketDataClient): The product base urls for the WebSocket clients. config : BybitDataClientConfig The configuration for the client. + name : str, optional + The custom client ID. """ @@ -108,11 +110,12 @@ def __init__( product_types: list[BybitProductType], ws_base_urls: dict[BybitProductType, str], config: BybitDataClientConfig, + name: str | None, ) -> None: self._enum_parser = BybitEnumParser() super().__init__( loop=loop, - client_id=ClientId(BYBIT_VENUE.value), + client_id=ClientId(name or BYBIT_VENUE.value), venue=BYBIT_VENUE, msgbus=msgbus, cache=cache, diff --git a/nautilus_trader/adapters/bybit/execution.py b/nautilus_trader/adapters/bybit/execution.py index b89669b75dda..8066c843905f 100644 --- a/nautilus_trader/adapters/bybit/execution.py +++ b/nautilus_trader/adapters/bybit/execution.py @@ -101,6 +101,8 @@ class BybitExecutionClient(LiveExecutionClient): The base URL for the WebSocket client. config : BybitExecClientConfig The configuration for the client. + name : str, optional + The custom client ID. """ @@ -115,6 +117,7 @@ def __init__( product_types: list[BybitProductType], base_url_ws: str, config: BybitExecClientConfig, + name: str | None, ) -> None: if BybitProductType.SPOT in product_types: if len(set(product_types)) > 1: @@ -125,7 +128,7 @@ def __init__( super().__init__( loop=loop, - client_id=ClientId(BYBIT_VENUE.value), + client_id=ClientId(name or BYBIT_VENUE.value), venue=BYBIT_VENUE, oms_type=OmsType.NETTING, instrument_provider=instrument_provider, @@ -153,7 +156,7 @@ def __init__( self._enum_parser = BybitEnumParser() - account_id = AccountId(f"{BYBIT_VENUE.value}-UNIFIED") + account_id = AccountId(f"{name or BYBIT_VENUE.value}-UNIFIED") self._set_account_id(account_id) # WebSocket API diff --git a/nautilus_trader/adapters/bybit/factories.py b/nautilus_trader/adapters/bybit/factories.py index 4bf71beb6804..2cd85650f385 100644 --- a/nautilus_trader/adapters/bybit/factories.py +++ b/nautilus_trader/adapters/bybit/factories.py @@ -157,7 +157,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : BybitDataClientConfig The client configuration. msgbus : MessageBus @@ -202,6 +202,7 @@ def create( # type: ignore product_types=product_types, ws_base_urls=ws_base_urls, config=config, + name=name, ) @@ -227,7 +228,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : BybitExecClientConfig The client configuration. msgbus : MessageBus @@ -266,4 +267,5 @@ def create( # type: ignore product_types=config.product_types or [BybitProductType.SPOT], base_url_ws=config.base_url_ws or base_url_ws, config=config, + name=name, ) diff --git a/nautilus_trader/adapters/databento/data.py b/nautilus_trader/adapters/databento/data.py index ad35a07df524..6b4e96cd2e0e 100644 --- a/nautilus_trader/adapters/databento/data.py +++ b/nautilus_trader/adapters/databento/data.py @@ -24,7 +24,7 @@ from nautilus_trader.adapters.databento.common import databento_schema_from_nautilus_bar_type from nautilus_trader.adapters.databento.config import DatabentoDataClientConfig from nautilus_trader.adapters.databento.constants import ALL_SYMBOLS -from nautilus_trader.adapters.databento.constants import DATABENTO_CLIENT_ID +from nautilus_trader.adapters.databento.constants import DATABENTO from nautilus_trader.adapters.databento.constants import PUBLISHERS_PATH from nautilus_trader.adapters.databento.enums import DatabentoSchema from nautilus_trader.adapters.databento.loaders import DatabentoDataLoader @@ -48,6 +48,7 @@ from nautilus_trader.model.data import capsule_to_data from nautilus_trader.model.enums import BookType from nautilus_trader.model.enums import bar_aggregation_to_str +from nautilus_trader.model.identifiers import ClientId from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Venue from nautilus_trader.model.instruments import instruments_from_pyo3 @@ -78,6 +79,8 @@ class DatabentoDataClient(LiveMarketDataClient): The loader for the client. config : DatabentoDataClientConfig, optional The configuration for the client. + name : str, optional + The custom client ID. """ @@ -91,6 +94,7 @@ def __init__( instrument_provider: DatabentoInstrumentProvider, loader: DatabentoDataLoader | None = None, config: DatabentoDataClientConfig | None = None, + name: str | None = None, ) -> None: if config is None: config = DatabentoDataClientConfig() @@ -98,7 +102,7 @@ def __init__( super().__init__( loop=loop, - client_id=DATABENTO_CLIENT_ID, + client_id=ClientId(name or DATABENTO), venue=None, # Not applicable msgbus=msgbus, cache=cache, diff --git a/nautilus_trader/adapters/databento/factories.py b/nautilus_trader/adapters/databento/factories.py index cce5333e34d9..32f4907db101 100644 --- a/nautilus_trader/adapters/databento/factories.py +++ b/nautilus_trader/adapters/databento/factories.py @@ -177,4 +177,5 @@ def create( # type: ignore instrument_provider=provider, loader=loader, config=config, + name=name, ) diff --git a/nautilus_trader/adapters/interactive_brokers/data.py b/nautilus_trader/adapters/interactive_brokers/data.py index 0175e639d3c7..2fb5c032d83e 100644 --- a/nautilus_trader/adapters/interactive_brokers/data.py +++ b/nautilus_trader/adapters/interactive_brokers/data.py @@ -50,6 +50,28 @@ class InteractiveBrokersDataClient(LiveMarketDataClient): """ Provides a data client for the InteractiveBrokers exchange by using the `Gateway` to stream market data. + + Parameters + ---------- + loop : asyncio.AbstractEventLoop + The event loop for the client. + client : InteractiveBrokersClient + The nautilus InteractiveBrokersClient using ibapi. + msgbus : MessageBus + The message bus for the client. + cache : Cache + The cache for the client. + clock : LiveClock + The clock for the client. + instrument_provider : InteractiveBrokersInstrumentProvider + The instrument provider. + ibg_client_id : int + Client ID used to connect TWS/Gateway. + config : InteractiveBrokersDataClientConfig + Configuration for the client. + name : str, optional + The custom client ID. + """ def __init__( @@ -62,33 +84,11 @@ def __init__( instrument_provider: InteractiveBrokersInstrumentProvider, ibg_client_id: int, config: InteractiveBrokersDataClientConfig, + name: str | None = None, ) -> None: - """ - Initialize a new instance of the ``InteractiveBrokersDataClient`` class. - - Parameters - ---------- - loop : asyncio.AbstractEventLoop - The event loop for the client. - client : InteractiveBrokersClient - The nautilus InteractiveBrokersClient using ibapi. - msgbus : MessageBus - The message bus for the client. - cache : Cache - The cache for the client. - clock : LiveClock - The clock for the client. - instrument_provider : InteractiveBrokersInstrumentProvider - The instrument provider. - ibg_client_id : int - Client ID used to connect TWS/Gateway. - config : InteractiveBrokersDataClientConfig - Configuration for the client. - - """ super().__init__( loop=loop, - client_id=ClientId(f"{IB_VENUE.value}-{ibg_client_id:03d}"), + client_id=ClientId(name or f"{IB_VENUE.value}-{ibg_client_id:03d}"), venue=None, msgbus=msgbus, cache=cache, diff --git a/nautilus_trader/adapters/interactive_brokers/execution.py b/nautilus_trader/adapters/interactive_brokers/execution.py index eb9b8b48762b..17cbf1e28a94 100644 --- a/nautilus_trader/adapters/interactive_brokers/execution.py +++ b/nautilus_trader/adapters/interactive_brokers/execution.py @@ -120,6 +120,8 @@ class InteractiveBrokersExecutionClient(LiveExecutionClient): Client ID used to connect TWS/Gateway. config : InteractiveBrokersExecClientConfig, optional The configuration for the instance. + name : str, optional + The custom client ID. """ @@ -134,11 +136,12 @@ def __init__( instrument_provider: InteractiveBrokersInstrumentProvider, ibg_client_id: int, config: InteractiveBrokersExecClientConfig, + name: str | None = None, ) -> None: super().__init__( loop=loop, # client_id=ClientId(f"{IB_VENUE.value}-{ibg_client_id:03d}"), # TODO: Fix account_id.get_id() - client_id=ClientId(f"{IB_VENUE.value}"), + client_id=ClientId(name or f"{IB_VENUE.value}"), venue=IB_VENUE, oms_type=OmsType.NETTING, instrument_provider=instrument_provider, diff --git a/nautilus_trader/adapters/interactive_brokers/factories.py b/nautilus_trader/adapters/interactive_brokers/factories.py index 120460855c58..ae5568969c68 100644 --- a/nautilus_trader/adapters/interactive_brokers/factories.py +++ b/nautilus_trader/adapters/interactive_brokers/factories.py @@ -155,7 +155,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict The configuration dictionary. msgbus : MessageBus @@ -197,6 +197,7 @@ def create( # type: ignore instrument_provider=provider, ibg_client_id=config.ibg_client_id, config=config, + name=name, ) return data_client @@ -223,7 +224,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, object] The configuration for the client. msgbus : MessageBus @@ -274,5 +275,6 @@ def create( # type: ignore instrument_provider=provider, ibg_client_id=config.ibg_client_id, config=config, + name=name, ) return exec_client diff --git a/nautilus_trader/adapters/sandbox/factory.py b/nautilus_trader/adapters/sandbox/factory.py index 83bcdd6a2a2c..95f0479a373f 100644 --- a/nautilus_trader/adapters/sandbox/factory.py +++ b/nautilus_trader/adapters/sandbox/factory.py @@ -47,7 +47,7 @@ def create( # type: ignore loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, object] The configuration for the client. portfolio : PortfolioFacade @@ -70,7 +70,7 @@ def create( # type: ignore portfolio=portfolio, msgbus=msgbus, cache=cache, - venue=config.venue, + venue=name or config.venue, balance=config.balance, currency=config.currency, ) diff --git a/nautilus_trader/live/factories.py b/nautilus_trader/live/factories.py index c39d0ffe5ab6..d33713fedf1c 100644 --- a/nautilus_trader/live/factories.py +++ b/nautilus_trader/live/factories.py @@ -46,7 +46,7 @@ def create( loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, object] The configuration for the client. msgbus : MessageBus @@ -88,7 +88,7 @@ def create( loop : asyncio.AbstractEventLoop The event loop for the client. name : str - The client name. + The custom client ID. config : dict[str, object] The configuration for the client. msgbus : MessageBus