81 lines
3.5 KiB
Python
81 lines
3.5 KiB
Python
|
|
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,
|
||
|
|
)
|