""" OKX 挂单聚合:普通委托 + 算法单(conditional / oco / trigger)。 交易所 App「止盈止损」页多为 orders-algo-pending,仅 fetch_open_orders 默认拿不到。 """ from __future__ import annotations from typing import Any def _order_dedupe_key(order: dict) -> str: info = order.get("info") or {} if not isinstance(info, dict): info = {} return str(order.get("id") or info.get("algoId") or info.get("ordId") or "") def _okx_algo_cancel_id(order_id: str) -> str: oid = str(order_id or "") if ":" in oid: return oid.split(":", 1)[0] return oid def _okx_order_needs_stop_cancel_param(order: dict) -> bool: """OKX 条件/算法单撤单须 params.stop=True,否则 cancel_order 走普通单接口会静默失败。""" if not isinstance(order, dict): return False info = order.get("info") or {} if not isinstance(info, dict): info = {} if order.get("stopLossPrice") is not None or order.get("takeProfitPrice") is not None: return True if info.get("algoId") or info.get("slTriggerPx") or info.get("tpTriggerPx"): return True typ = str(order.get("type") or info.get("ordType") or "").lower() for token in ("conditional", "oco", "trigger", "move_order_stop", "iceberg"): if token in typ: return True return False def fetch_okx_all_open_orders(ex, exchange_symbol: str) -> list[dict]: """合并 OKX 普通挂单与算法挂单(去重)。""" if not exchange_symbol: return [] ex.load_markets() sym = exchange_symbol try: sym = ex.market(exchange_symbol)["symbol"] except Exception: pass seen: set[str] = set() out: list[dict] = [] def add_batch(batch: list | None) -> None: for o in batch or []: if not isinstance(o, dict): continue k = _order_dedupe_key(o) if not k or k in seen: continue seen.add(k) out.append(o) try: add_batch(ex.fetch_open_orders(sym)) except Exception: pass for params in ( {"ordType": "conditional"}, {"ordType": "oco"}, {"trigger": True}, ): try: add_batch(ex.fetch_open_orders(sym, params=dict(params))) except Exception: pass return out def cancel_okx_all_open_orders(ex, exchange_symbol: str) -> int: """ 撤销某合约全部挂单(普通 + 条件/算法)。 OKX 止盈止损在 orders-algo-pending,必须用 stop=True 才能撤掉。 """ if not exchange_symbol: return 0 ex.load_markets() sym = exchange_symbol try: sym = ex.market(exchange_symbol)["symbol"] except Exception: pass n = 0 for o in fetch_okx_all_open_orders(ex, sym): oid = _order_dedupe_key(o) if not oid: continue cancel_id = _okx_algo_cancel_id(oid) params = {"stop": True} if _okx_order_needs_stop_cancel_param(o) else None try: ex.cancel_order(cancel_id, sym, params) n += 1 continue except Exception: pass try: ex.cancel_order(oid, sym, params) n += 1 except Exception: pass try: ex.cancel_all_orders(sym) except Exception: pass return n