"""L2 — LIMIT order payload wiring in BingxDirectExecutionAdapter.submit_intent. The venue adapter forwards _order_type/_limit_price in the intent metadata; the backend must place a LIMIT order (type=LIMIT + price + GTC) when asked, and keep MARKET as the default. Offline unit test of payload construction — the signed_post client is stubbed to capture the order payload; no exchange contact. """ from __future__ import annotations import asyncio from datetime import datetime, timezone from types import SimpleNamespace from prod.clean_arch.adapters.bingx_direct import BingxDirectExecutionAdapter from prod.clean_arch.dita import DecisionAction, Intent, TradeSide def _adapter(captured: dict): a = BingxDirectExecutionAdapter.__new__(BingxDirectExecutionAdapter) a._config = SimpleNamespace(recv_window_ms=5000, default_leverage=1, exchange_leverage_cap=3) a._client_order_run_id = "test" a._entry_client_order_seq = 0 a._exit_client_order_seq = 0 a._state = SimpleNamespace(open_positions={}, account={}) async def _signed_post(path, params): if path.endswith("/trade/order"): captured["order"] = dict(params) return {"orderId": "1", "status": "NEW"} a._client = SimpleNamespace(signed_post=_signed_post) a._instrument_venue_symbol = lambda asset: "BTC-USDT" a._format_quantity = lambda asset, q: f"{float(q)}" a._format_price = lambda asset, p: f"{float(p)}" async def _refresh(asset, include_history=True): return a._state a._refresh_exchange_state = _refresh return a def _intent(metadata: dict) -> Intent: return Intent( timestamp=datetime.now(timezone.utc), trade_id="T1", decision_id="D1", asset="BTCUSDT", action=DecisionAction.ENTER, side=TradeSide.SHORT, reason="TEST", target_size=0.01, leverage=2.0, reference_price=100.0, confidence=0.5, exit_leg_ratios=(1.0,), metadata=metadata, ) def test_limit_intent_places_limit_order(): captured: dict = {} asyncio.run(_adapter(captured).submit_intent(_intent({"_order_type": "LIMIT", "_limit_price": 95.0}))) o = captured["order"] assert o["type"] == "LIMIT", o assert "price" in o and float(o["price"]) == 95.0, o assert o.get("timeInForce") == "GTC", o def test_market_intent_places_market_order(): captured: dict = {} asyncio.run(_adapter(captured).submit_intent(_intent({}))) o = captured["order"] assert o["type"] == "MARKET", o assert "price" not in o, o def test_limit_without_valid_price_falls_back_to_market(): captured: dict = {} asyncio.run(_adapter(captured).submit_intent(_intent({"_order_type": "LIMIT", "_limit_price": 0.0}))) assert captured["order"]["type"] == "MARKET", captured["order"]