"""Gate 平仓历史匹配(fetch_positions_history),供 reconcile / 中控全平同步共用。""" from __future__ import annotations def unified_symbol_for_match(symbol_str: str) -> str: x = (symbol_str or "").strip().upper() if ":" in x: x = x.split(":")[0] return x def pick_gate_position_close( hist: list[dict], symbol: str, direction: str, *, opened_at_ms: int | None = None, closed_at_ms: int | None = None, used_keys: set[str] | None = None, max_close_delta_ms: int = 25 * 60 * 1000, ) -> dict | None: """ 从 Gate 平仓历史列表中选取与 symbol/direction/开仓时间最匹配的一条。 返回 normalize 后的 dict(含 close_ms、pnl、sync_key 等),无匹配则 None。 """ if not hist: return None sym_u = unified_symbol_for_match(symbol) dir_l = (direction or "long").strip().lower() if dir_l not in ("long", "short"): return None used = used_keys or set() ref_ms = closed_at_ms or opened_at_ms best = None best_d = None for h in hist: if not isinstance(h, dict): continue sk = h.get("sync_key") if not sk or sk in used: continue if h.get("symbol_u") != sym_u: continue if (h.get("side") or "").strip().lower() != dir_l: continue cm = h.get("close_ms") if cm is None: continue if opened_at_ms is not None: if cm < opened_at_ms - 15 * 60 * 1000: continue if cm > opened_at_ms + 15 * 86400 * 1000: continue if ref_ms is not None: d = abs(int(cm) - int(ref_ms)) else: d = 0 if best_d is None or d < best_d: best_d = d best = h if best is None or best_d is None: return None if ref_ms is not None and best_d > max_close_delta_ms: return None return best