from __future__ import annotations from decimal import Decimal from nautilus_trader.common.providers import InstrumentProvider from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Symbol from nautilus_trader.model.instruments import CryptoPerpetual from nautilus_trader.model.objects import Currency from nautilus_trader.model.objects import Money from nautilus_trader.model.objects import Price from nautilus_trader.model.objects import Quantity from .config import BingxInstrumentProviderConfig from .enums import BINGX_VENUE from .http import BingxHttpClient from .schemas import BingxContract class BingxInstrumentProvider(InstrumentProvider): def __init__( self, client: BingxHttpClient, config: BingxInstrumentProviderConfig | None = None, ) -> None: super().__init__(config=config) self._client = client self._cfg = config or BingxInstrumentProviderConfig() async def load_all_async(self, filters: dict | None = None) -> None: raw_contracts = await self._client.public_get("/openApi/swap/v2/quote/contracts") contracts = raw_contracts if isinstance(raw_contracts, list) else raw_contracts.get("contracts", []) requested = set(self._cfg.symbol_filters or ()) for row in contracts: contract = BingxContract.from_http(row) if requested and contract.symbol not in requested and contract.venue_symbol not in requested: continue self.add(self._parse_contract(contract)) async def load_ids_async( self, instrument_ids: list[InstrumentId], filters: dict | None = None, ) -> None: self._cfg = BingxInstrumentProviderConfig( load_all=True, symbol_filters=tuple(i.symbol.value for i in instrument_ids), default_maker_fee=self._cfg.default_maker_fee, default_taker_fee=self._cfg.default_taker_fee, ) await self.load_all_async(filters) def _parse_contract(self, contract: BingxContract) -> CryptoPerpetual: base_currency = Currency.from_str(contract.base_asset) quote_currency = Currency.from_str(contract.quote_asset) symbol = Symbol(contract.symbol) return CryptoPerpetual( instrument_id=InstrumentId(symbol=symbol, venue=BINGX_VENUE), raw_symbol=Symbol(contract.venue_symbol), base_currency=base_currency, quote_currency=quote_currency, settlement_currency=quote_currency, is_inverse=False, price_precision=contract.price_precision, price_increment=Price.from_str(str(contract.tick_size)), size_precision=contract.quantity_precision, size_increment=Quantity.from_str(str(contract.step_size)), max_quantity=Quantity.from_str("1000000000"), min_quantity=Quantity.from_str(str(contract.min_quantity)), max_notional=None, min_notional=Money(contract.min_notional, quote_currency), max_price=Price.from_str("1000000000"), min_price=Price.from_str(str(contract.tick_size)), margin_init=Decimal("0.11111111") if contract.max_leverage >= 9 else Decimal("1") / Decimal(contract.max_leverage or 1), margin_maint=Decimal("0.05"), maker_fee=contract.maker_fee or self._cfg.default_maker_fee, taker_fee=contract.taker_fee or self._cfg.default_taker_fee, ts_event=0, ts_init=0, )