diff --git a/examples/live/bybit/bybit_market_maker.py b/examples/live/bybit/bybit_market_maker.py index 0f6ca9d5dbed..8e09282415e8 100644 --- a/examples/live/bybit/bybit_market_maker.py +++ b/examples/live/bybit/bybit_market_maker.py @@ -39,7 +39,15 @@ # *** THIS INTEGRATION IS STILL UNDER CONSTRUCTION. *** # *** CONSIDER IT TO BE IN AN UNSTABLE BETA PHASE AND EXERCISE CAUTION. *** +# LINEAR product_type = BybitProductType.LINEAR +symbol = f"ETHUSDT-{product_type.value.upper()}" +trade_size = Decimal("0.010") + +# INVERSE +# product_type = BybitProductType.INVERSE +# symbol = f"XRPUSD-{product_type.value.upper()}" # Use for inverse +# trade_size = Decimal("100") # Use for inverse # Configure the trading node config_node = TradingNodeConfig( @@ -101,14 +109,13 @@ node = TradingNode(config=config_node) # Configure your strategy -symbol = f"ETHUSDT-{product_type.value.upper()}" strat_config = VolatilityMarketMakerConfig( instrument_id=InstrumentId.from_str(f"{symbol}.BYBIT"), external_order_claims=[InstrumentId.from_str(f"{symbol}.BYBIT")], bar_type=BarType.from_str(f"{symbol}.BYBIT-1-MINUTE-LAST-EXTERNAL"), atr_period=20, - atr_multiple=6.0, - trade_size=Decimal("0.010"), + atr_multiple=3.0, + trade_size=trade_size, ) # Instantiate your strategy strategy = VolatilityMarketMaker(config=strat_config) diff --git a/nautilus_trader/adapters/bybit/data.py b/nautilus_trader/adapters/bybit/data.py index c7bbe669df13..0b8c21d73c92 100644 --- a/nautilus_trader/adapters/bybit/data.py +++ b/nautilus_trader/adapters/bybit/data.py @@ -152,9 +152,10 @@ def __init__( self._decoders["trade"][product_type] = decoder_ws_trade() self._decoders["ticker"][product_type] = decoder_ws_ticker(product_type) self._decoders["kline"][product_type] = decoder_ws_kline() - self._decoder_ws_msg_general = msgspec.json.Decoder(BybitWsMessageGeneral) + self._log.info(f"Initialized WebSocket handlers for {product_type.value} products") + self._tob_quotes: set[InstrumentId] = set() self._depths: dict[InstrumentId, int] = {} self._topic_bar_type: dict[str, BarType] = {} diff --git a/nautilus_trader/adapters/bybit/endpoints/market/instruments_info.py b/nautilus_trader/adapters/bybit/endpoints/market/instruments_info.py index 7472caf90a1e..22f7fca60fa5 100644 --- a/nautilus_trader/adapters/bybit/endpoints/market/instruments_info.py +++ b/nautilus_trader/adapters/bybit/endpoints/market/instruments_info.py @@ -19,6 +19,7 @@ from nautilus_trader.adapters.bybit.common.enums import BybitProductType from nautilus_trader.adapters.bybit.endpoints.endpoint import BybitHttpEndpoint from nautilus_trader.adapters.bybit.http.client import BybitHttpClient +from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentsInverseResponse from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentsLinearResponse from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentsOptionResponse from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentsSpotResponse @@ -43,10 +44,13 @@ def __init__( endpoint_type=BybitEndpointType.MARKET, url_path=url_path, ) + self._response_decoder_instrument_spot = msgspec.json.Decoder(BybitInstrumentsSpotResponse) self._response_decoder_instrument_linear = msgspec.json.Decoder( BybitInstrumentsLinearResponse, ) - self._response_decoder_instrument_spot = msgspec.json.Decoder(BybitInstrumentsSpotResponse) + self._response_decoder_instrument_inverse = msgspec.json.Decoder( + BybitInstrumentsInverseResponse, + ) self._response_decoder_instrument_option = msgspec.json.Decoder( BybitInstrumentsOptionResponse, ) @@ -57,15 +61,18 @@ async def get( ) -> ( BybitInstrumentsSpotResponse | BybitInstrumentsLinearResponse + | BybitInstrumentsInverseResponse | BybitInstrumentsOptionResponse ): method_type = HttpMethod.GET raw = await self._method(method_type, params) if params.category == BybitProductType.SPOT: return self._response_decoder_instrument_spot.decode(raw) - elif params.category in (BybitProductType.LINEAR, BybitProductType.INVERSE): + elif params.category == BybitProductType.LINEAR: return self._response_decoder_instrument_linear.decode(raw) + elif params.category == BybitProductType.INVERSE: + return self._response_decoder_instrument_inverse.decode(raw) elif params.category == BybitProductType.OPTION: return self._response_decoder_instrument_option.decode(raw) else: - raise ValueError("Invalid account type") + raise ValueError(f"Invalid product type, was {params.category}") diff --git a/nautilus_trader/adapters/bybit/http/account.py b/nautilus_trader/adapters/bybit/http/account.py index d55e14d18026..34fff20dddd1 100644 --- a/nautilus_trader/adapters/bybit/http/account.py +++ b/nautilus_trader/adapters/bybit/http/account.py @@ -86,11 +86,16 @@ async def query_position_info( product_type: BybitProductType, symbol: str | None = None, ) -> list[BybitPositionStruct]: - # symbol = 'USD' + match product_type: + case BybitProductType.INVERSE: + settle_coin = None + case _: + settle_coin = self.default_settle_coin if symbol is None else None + response = await self._endpoint_position_info.get( PositionInfoGetParams( symbol=symbol, - settleCoin=(self.default_settle_coin if symbol is None else None), + settleCoin=settle_coin, category=get_category_from_product_type(product_type), ), ) @@ -106,11 +111,17 @@ async def query_open_orders( product_type: BybitProductType, symbol: str | None = None, ) -> list[BybitOrder]: + match product_type: + case BybitProductType.INVERSE: + settle_coin = None + case _: + settle_coin = self.default_settle_coin if symbol is None else None + response = await self._endpoint_open_orders.get( BybitOpenOrdersGetParams( category=product_type, symbol=symbol, - settleCoin=(self.default_settle_coin if symbol is None else None), + settleCoin=settle_coin, ), ) return response.result.list diff --git a/nautilus_trader/adapters/bybit/providers.py b/nautilus_trader/adapters/bybit/providers.py index 666b4accaafb..470561e8ddfb 100644 --- a/nautilus_trader/adapters/bybit/providers.py +++ b/nautilus_trader/adapters/bybit/providers.py @@ -22,6 +22,7 @@ from nautilus_trader.adapters.bybit.http.market import BybitMarketHttpAPI from nautilus_trader.adapters.bybit.schemas.account.fee_rate import BybitFeeRate from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrument +from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentInverse from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentLinear from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentList from nautilus_trader.adapters.bybit.schemas.instrument import BybitInstrumentOption @@ -144,10 +145,12 @@ def _parse_instrument( self._parse_spot_instrument(instrument, fee_rate) elif isinstance(instrument, BybitInstrumentLinear): self._parse_linear_instrument(instrument, fee_rate) + elif isinstance(instrument, BybitInstrumentInverse): + self._parse_inverse_instrument(instrument, fee_rate) elif isinstance(instrument, BybitInstrumentOption): self._parse_option_instrument(instrument) else: - raise TypeError("Unsupported instrument type in BybitInstrumentProvider") + raise TypeError(f"Unsupported Bybit instrument, was {instrument}") async def load_async(self, instrument_id: InstrumentId, filters: dict | None = None) -> None: PyCondition.not_none(instrument_id, "instrument_id") @@ -174,19 +177,31 @@ def _parse_spot_instrument( if self._log_warnings: self._log.warning(f"Unable to parse option instrument {data.symbol}, {e}") - def _parse_option_instrument( + def _parse_linear_instrument( self, - instrument: BybitInstrumentOption, + data: BybitInstrumentLinear, + fee_rate: BybitFeeRate, ) -> None: try: - pass + base_currency = data.parse_to_base_currency() + quote_currency = data.parse_to_quote_currency() + ts_event = self._clock.timestamp_ns() + ts_init = self._clock.timestamp_ns() + instrument = data.parse_to_instrument( + fee_rate=fee_rate, + ts_event=ts_event, + ts_init=ts_init, + ) + self.add_currency(base_currency) + self.add_currency(quote_currency) + self.add(instrument=instrument) except ValueError as e: if self._log_warnings: - self._log.warning(f"Unable to parse option instrument {instrument.symbol}, {e}") + self._log.warning(f"Unable to parse linear instrument {data.symbol}, {e}") - def _parse_linear_instrument( + def _parse_inverse_instrument( self, - data: BybitInstrumentLinear, + data: BybitInstrumentInverse, fee_rate: BybitFeeRate, ) -> None: try: @@ -204,4 +219,14 @@ def _parse_linear_instrument( self.add(instrument=instrument) except ValueError as e: if self._log_warnings: - self._log.warning(f"Unable to parse instrument {data.symbol}, {e}") + self._log.warning(f"Unable to parse inverse instrument {data.symbol}, {e}") + + def _parse_option_instrument( + self, + instrument: BybitInstrumentOption, + ) -> None: + try: + pass + except ValueError as e: + if self._log_warnings: + self._log.warning(f"Unable to parse option instrument {instrument.symbol}, {e}") diff --git a/nautilus_trader/adapters/bybit/schemas/instrument.py b/nautilus_trader/adapters/bybit/schemas/instrument.py index 041276173897..17d9d83bdf2f 100644 --- a/nautilus_trader/adapters/bybit/schemas/instrument.py +++ b/nautilus_trader/adapters/bybit/schemas/instrument.py @@ -93,7 +93,7 @@ def parse_to_base_currency(self) -> Currency: name=self.baseCoin, currency_type=CurrencyType.CRYPTO, precision=abs(int(Decimal(self.lotSizeFilter.basePrecision).as_tuple().exponent)), - iso4217=0, # Currently undetermined for crypto assets + iso4217=0, # Currently unspecified for crypto assets ) def parse_to_quote_currency(self) -> Currency: @@ -102,7 +102,7 @@ def parse_to_quote_currency(self) -> Currency: name=self.quoteCoin, currency_type=CurrencyType.CRYPTO, precision=abs(int(Decimal(self.lotSizeFilter.quotePrecision).as_tuple().exponent)), - iso4217=0, # Currently undetermined for crypto assets + iso4217=0, # Currently unspecified for crypto assets ) @@ -112,53 +112,84 @@ def get_strike_price_from_symbol(symbol: str) -> int: return int(symbol.split("-")[2]) -class BybitInstrumentOption(msgspec.Struct): +class BybitInstrumentLinear(msgspec.Struct): symbol: str + contractType: str status: str baseCoin: str quoteCoin: str - settleCoin: str - optionsType: str launchTime: str deliveryTime: str deliveryFeeRate: str + priceScale: str + leverageFilter: LeverageFilter priceFilter: LinearPriceFilter lotSizeFilter: LotSizeFilter + unifiedMarginTrade: bool + fundingInterval: int + settleCoin: str def parse_to_instrument( self, - ) -> OptionsContract: - bybit_symbol = BybitSymbol(self.symbol + "-OPTION") - assert bybit_symbol # Type checking + fee_rate: BybitFeeRate, + ts_event: int, + ts_init: int, + ) -> CryptoPerpetual: + base_currency = self.parse_to_base_currency() + quote_currency = self.parse_to_quote_currency() + bybit_symbol = BybitSymbol(self.symbol + "-LINEAR") + assert bybit_symbol is not None # Type checking instrument_id = bybit_symbol.parse_as_nautilus() - price_increment = Price.from_str(self.priceFilter.tickSize) - if self.optionsType == "Call": - option_kind = OptionKind.CALL - elif self.optionsType == "Put": - option_kind = OptionKind.PUT + if self.settleCoin == self.baseCoin: + settlement_currency = base_currency + elif self.settleCoin == self.quoteCoin: + settlement_currency = quote_currency else: - raise ValueError(f"Unknown Bybit option type {self.optionsType}") - timestamp = time.time_ns() - strike_price = get_strike_price_from_symbol(self.symbol) - activation_ns = pd.Timedelta(milliseconds=int(self.launchTime)).total_seconds() * 1e9 - expiration_ns = pd.Timedelta(milliseconds=int(self.deliveryTime)).total_seconds() * 1e9 + raise ValueError(f"Unrecognized margin asset {self.settleCoin}") - return OptionsContract( + price_increment = Price.from_str(self.priceFilter.tickSize) + size_increment = Quantity.from_str(self.lotSizeFilter.qtyStep) + max_quantity = Quantity.from_str(self.lotSizeFilter.maxOrderQty) + min_quantity = Quantity.from_str(self.lotSizeFilter.minOrderQty) + max_price = Price.from_str(self.priceFilter.maxPrice) + min_price = Price.from_str(self.priceFilter.minPrice) + maker_fee = fee_rate.makerFeeRate + taker_fee = fee_rate.takerFeeRate + + instrument = CryptoPerpetual( instrument_id=instrument_id, - raw_symbol=Symbol(bybit_symbol.raw_symbol), - asset_class=AssetClass.CRYPTOCURRENCY, - currency=self.parse_to_quote_currency(), + raw_symbol=Symbol(str(bybit_symbol)), + base_currency=base_currency, + quote_currency=quote_currency, + settlement_currency=settlement_currency, + is_inverse=False, price_precision=price_increment.precision, + size_precision=size_increment.precision, price_increment=price_increment, - multiplier=Quantity.from_str("1.0"), - lot_size=Quantity.from_str(self.lotSizeFilter.qtyStep), - underlying=self.baseCoin, - kind=option_kind, - activation_ns=activation_ns, - expiration_ns=expiration_ns, - strike_price=Price.from_int(strike_price), - ts_init=timestamp, - ts_event=timestamp, + size_increment=size_increment, + max_quantity=max_quantity, + min_quantity=min_quantity, + max_notional=None, + min_notional=None, + max_price=max_price, + min_price=min_price, + margin_init=Decimal("0.1"), + margin_maint=Decimal("0.1"), + maker_fee=Decimal(maker_fee), + taker_fee=Decimal(taker_fee), + ts_event=ts_event, + ts_init=ts_init, + info=msgspec.json.Decoder().decode(msgspec.json.Encoder().encode(self)), + ) + return instrument + + def parse_to_base_currency(self) -> Currency: + return Currency( + code=self.baseCoin, + name=self.baseCoin, + currency_type=CurrencyType.CRYPTO, + precision=int(self.priceScale), + iso4217=0, # Currently unspecified for crypto assets ) def parse_to_quote_currency(self) -> Currency: @@ -166,12 +197,12 @@ def parse_to_quote_currency(self) -> Currency: code=self.quoteCoin, name=self.quoteCoin, currency_type=CurrencyType.CRYPTO, - precision=1, - iso4217=0, # Currently undetermined for crypto assets + precision=int(self.priceScale), + iso4217=0, # Currently unspecified for crypto assets ) -class BybitInstrumentLinear(msgspec.Struct): +class BybitInstrumentInverse(msgspec.Struct): symbol: str contractType: str status: str @@ -196,7 +227,7 @@ def parse_to_instrument( ) -> CryptoPerpetual: base_currency = self.parse_to_base_currency() quote_currency = self.parse_to_quote_currency() - bybit_symbol = BybitSymbol(self.symbol + "-LINEAR") + bybit_symbol = BybitSymbol(self.symbol + "-INVERSE") assert bybit_symbol is not None # Type checking instrument_id = bybit_symbol.parse_as_nautilus() if self.settleCoin == self.baseCoin: @@ -221,7 +252,7 @@ def parse_to_instrument( base_currency=base_currency, quote_currency=quote_currency, settlement_currency=settlement_currency, - is_inverse=False, # No inverse instruments trade on Bybit + is_inverse=True, price_precision=price_increment.precision, size_precision=size_increment.precision, price_increment=price_increment, @@ -248,7 +279,7 @@ def parse_to_base_currency(self) -> Currency: name=self.baseCoin, currency_type=CurrencyType.CRYPTO, precision=int(self.priceScale), - iso4217=0, # Currently undetermined for crypto assets + iso4217=0, # Currently unspecified for crypto assets ) def parse_to_quote_currency(self) -> Currency: @@ -257,17 +288,88 @@ def parse_to_quote_currency(self) -> Currency: name=self.quoteCoin, currency_type=CurrencyType.CRYPTO, precision=int(self.priceScale), - iso4217=0, # Currently undetermined for crypto assets + iso4217=0, # Currently unspecified for crypto assets ) -BybitInstrument = BybitInstrumentLinear | BybitInstrumentSpot | BybitInstrumentOption +class BybitInstrumentOption(msgspec.Struct): + symbol: str + status: str + baseCoin: str + quoteCoin: str + settleCoin: str + optionsType: str + launchTime: str + deliveryTime: str + deliveryFeeRate: str + priceFilter: LinearPriceFilter + lotSizeFilter: LotSizeFilter + + def parse_to_instrument( + self, + ) -> OptionsContract: + bybit_symbol = BybitSymbol(self.symbol + "-OPTION") + assert bybit_symbol # Type checking + instrument_id = bybit_symbol.parse_as_nautilus() + price_increment = Price.from_str(self.priceFilter.tickSize) + if self.optionsType == "Call": + option_kind = OptionKind.CALL + elif self.optionsType == "Put": + option_kind = OptionKind.PUT + else: + raise ValueError(f"Unknown Bybit option type {self.optionsType}") + timestamp = time.time_ns() + strike_price = get_strike_price_from_symbol(self.symbol) + activation_ns = pd.Timedelta(milliseconds=int(self.launchTime)).total_seconds() * 1e9 + expiration_ns = pd.Timedelta(milliseconds=int(self.deliveryTime)).total_seconds() * 1e9 + + return OptionsContract( + instrument_id=instrument_id, + raw_symbol=Symbol(bybit_symbol.raw_symbol), + asset_class=AssetClass.CRYPTOCURRENCY, + currency=self.parse_to_quote_currency(), + price_precision=price_increment.precision, + price_increment=price_increment, + multiplier=Quantity.from_str("1.0"), + lot_size=Quantity.from_str(self.lotSizeFilter.qtyStep), + underlying=self.baseCoin, + kind=option_kind, + activation_ns=activation_ns, + expiration_ns=expiration_ns, + strike_price=Price.from_int(strike_price), + ts_init=timestamp, + ts_event=timestamp, + ) + + def parse_to_quote_currency(self) -> Currency: + return Currency( + code=self.quoteCoin, + name=self.quoteCoin, + currency_type=CurrencyType.CRYPTO, + precision=1, + iso4217=0, # Currently unspecified for crypto assets + ) + + +BybitInstrument = ( + BybitInstrumentSpot | BybitInstrumentLinear | BybitInstrumentInverse | BybitInstrumentOption +) BybitInstrumentList = ( - list[BybitInstrumentLinear] | list[BybitInstrumentSpot] | list[BybitInstrumentOption] + list[BybitInstrumentSpot] + | list[BybitInstrumentLinear] + | list[BybitInstrumentInverse] + | list[BybitInstrumentOption] ) +class BybitInstrumentsSpotResponse(msgspec.Struct): + retCode: int + retMsg: str + result: BybitListResult[BybitInstrumentSpot] + time: int + + class BybitInstrumentsLinearResponse(msgspec.Struct): retCode: int retMsg: str @@ -275,10 +377,10 @@ class BybitInstrumentsLinearResponse(msgspec.Struct): time: int -class BybitInstrumentsSpotResponse(msgspec.Struct): +class BybitInstrumentsInverseResponse(msgspec.Struct): retCode: int retMsg: str - result: BybitListResult[BybitInstrumentSpot] + result: BybitListResult[BybitInstrumentInverse] time: int diff --git a/nautilus_trader/adapters/bybit/schemas/symbol.py b/nautilus_trader/adapters/bybit/schemas/symbol.py index c395267998d4..15914d8fc6b7 100644 --- a/nautilus_trader/adapters/bybit/schemas/symbol.py +++ b/nautilus_trader/adapters/bybit/schemas/symbol.py @@ -13,25 +13,49 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- +from typing import Final + from nautilus_trader.adapters.bybit.common.constants import BYBIT_VENUE from nautilus_trader.adapters.bybit.common.enums import BybitProductType from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Symbol +VALID_SUFFIXES: Final[list[str]] = ["-SPOT", "-LINEAR", "-INVERSE", "-OPTION"] + + +def has_valid_bybit_suffix(symbol: str) -> bool: + """ + Return whether the given `symbol` string contains a valid Bybit suffix. + + Parameters + ---------- + symbol : str + The symbol string value to check. + + Returns + ------- + bool + True if contains a valid suffix, else False. + + """ + for suffix in VALID_SUFFIXES: + if suffix in symbol: + return True + return False + + class BybitSymbol(str): + """ + Represents a Bybit specific symbol containing a product type suffix. + """ + def __new__(cls, symbol: str | None): if symbol is not None: - # Check if it contains one dot BTCUSDT-LINEAR for example is the correct - # bybit symbol format - if ( - symbol.find("-SPOT") == -1 - and symbol.find("-LINEAR") == -1 - and symbol.find("-INVERSE") == -1 - and symbol.find("-OPTION") == -1 - ): + if not has_valid_bybit_suffix(symbol): raise ValueError( - f"Invalid symbol {symbol}. Does not contain -LINEAR, -SPOT or -OPTION suffix", + f"Invalid symbol '{symbol}': " + f"does not contain a valid suffix from {VALID_SUFFIXES}", ) return super().__new__( cls, @@ -40,14 +64,32 @@ def __new__(cls, symbol: str | None): @property def raw_symbol(self) -> str: + """ + Return the raw Bybit symbol (without the product type suffix). + + Returns + ------- + str + + """ return str(self).split("-")[0] @property def product_type(self) -> BybitProductType: - if "-LINEAR" in self: - return BybitProductType.LINEAR - elif "-SPOT" in self: + """ + Return the Bybit product type for the symbol. + + Returns + ------- + BybitProductType + + """ + if "-SPOT" in self: return BybitProductType.SPOT + elif "-LINEAR" in self: + return BybitProductType.LINEAR + elif "-INVERSE" in self: + return BybitProductType.INVERSE elif "-OPTION" in self: return BybitProductType.OPTION else: @@ -55,16 +97,59 @@ def product_type(self) -> BybitProductType: @property def is_spot(self) -> bool: + """ + Return whether a SPOT product type. + + Returns + ------- + bool + + """ return self.product_type == BybitProductType.SPOT @property def is_linear(self) -> bool: + """ + Return whether a LINEAR product type. + + Returns + ------- + bool + + """ return self.product_type == BybitProductType.LINEAR + @property + def is_inverse(self) -> bool: + """ + Return whether an INVERSE product type. + + Returns + ------- + bool + + """ + return self.product_type == BybitProductType.INVERSE + @property def is_option(self) -> bool: + """ + Return whether an OPTION product type. + + Returns + ------- + bool + + """ return self.product_type == BybitProductType.OPTION def parse_as_nautilus(self) -> InstrumentId: - instrument = InstrumentId(Symbol(str(self)), BYBIT_VENUE) - return instrument + """ + Parse the Bybit symbol into a Nautilus instrument ID. + + Returns + ------- + InstrumentId + + """ + return InstrumentId(Symbol(str(self)), BYBIT_VENUE) diff --git a/nautilus_trader/examples/strategies/volatility_market_maker.py b/nautilus_trader/examples/strategies/volatility_market_maker.py index 0a2bd1633182..63c19984aa0c 100644 --- a/nautilus_trader/examples/strategies/volatility_market_maker.py +++ b/nautilus_trader/examples/strategies/volatility_market_maker.py @@ -133,7 +133,7 @@ def on_start(self) -> None: self.subscribe_bars(self.bar_type) self.subscribe_quote_ticks(self.instrument_id) - # self.subscribe_trade_ticks(self.instrument_id) + self.subscribe_trade_ticks(self.instrument_id) # self.subscribe_order_book_deltas(self.instrument_id) # For debugging # self.subscribe_order_book_snapshots( # self.instrument_id, @@ -368,7 +368,7 @@ def on_stop(self) -> None: # Unsubscribe from data self.unsubscribe_bars(self.bar_type) self.unsubscribe_quote_ticks(self.instrument_id) - # self.unsubscribe_trade_ticks(self.instrument_id) + self.unsubscribe_trade_ticks(self.instrument_id) # self.unsubscribe_order_book_deltas(self.instrument_id) # For debugging # self.unsubscribe_order_book_snapshots(self.instrument_id) # For debugging diff --git a/tests/integration_tests/adapters/bybit/sandbox/sandbox_instrument_provider.py b/tests/integration_tests/adapters/bybit/sandbox/sandbox_instrument_provider.py index 8c0a900cb2a9..9ef3c1433f84 100644 --- a/tests/integration_tests/adapters/bybit/sandbox/sandbox_instrument_provider.py +++ b/tests/integration_tests/adapters/bybit/sandbox/sandbox_instrument_provider.py @@ -40,6 +40,7 @@ async def test_bybit_instrument_provider(): product_types=[ BybitProductType.SPOT, BybitProductType.LINEAR, + BybitProductType.INVERSE, BybitProductType.OPTION, ], )