from __future__ import annotations from dataclasses import dataclass from decimal import Decimal def _as_decimal(value: object, default: str = "0") -> Decimal: if value is None: return Decimal(default) return Decimal(str(value)) def unwrap_order_payload(payload: dict[str, object]) -> dict[str, object]: row = payload.get("order") if isinstance(payload, dict) else None return row if isinstance(row, dict) else payload @dataclass(frozen=True) class BingxContract: symbol: str venue_symbol: str quote_asset: str base_asset: str price_precision: int quantity_precision: int min_quantity: Decimal min_notional: Decimal tick_size: Decimal step_size: Decimal maker_fee: Decimal taker_fee: Decimal max_leverage: int @classmethod def from_http(cls, payload: dict[str, object]) -> "BingxContract": venue_symbol = str(payload.get("symbol") or payload.get("ticker") or "") normalized = venue_symbol.replace("-", "") price_precision = int(payload.get("pricePrecision") or payload.get("price_scale") or 2) quantity_precision = int( payload.get("quantityPrecision") or payload.get("quantity_scale") or 3, ) tick_size = _as_decimal( payload.get("tickSize") or payload.get("priceStep") or f"1e-{price_precision}", ) step_size = _as_decimal( payload.get("stepSize") or payload.get("quantityStep") or f"1e-{quantity_precision}", ) return cls( symbol=normalized, venue_symbol=venue_symbol, quote_asset=str(payload.get("currency") or payload.get("quoteAsset") or "USDT"), base_asset=str(payload.get("asset") or payload.get("baseAsset") or normalized[:-4]), price_precision=price_precision, quantity_precision=quantity_precision, min_quantity=_as_decimal(payload.get("minQty") or payload.get("minQuantity") or step_size), min_notional=_as_decimal(payload.get("minNotional") or payload.get("minQuoteAmount") or "2"), tick_size=tick_size, step_size=step_size, maker_fee=_as_decimal(payload.get("makerFeeRate") or payload.get("makerFee") or "0.0002"), taker_fee=_as_decimal(payload.get("takerFeeRate") or payload.get("takerFee") or "0.0005"), max_leverage=int(payload.get("maxLongLeverage") or payload.get("maxLeverage") or 1), ) @dataclass(frozen=True) class BingxOrderAck: order_id: str client_order_id: str symbol: str status: str | None @classmethod def from_http(cls, payload: dict[str, object]) -> "BingxOrderAck": row = unwrap_order_payload(payload) return cls( order_id=str(row.get("orderId") or row.get("id") or ""), client_order_id=str(row.get("clientOrderID") or row.get("clientOrderId") or ""), symbol=str(row.get("symbol") or ""), status=str(row.get("status")) if row.get("status") is not None else None, )