59a45ed027
Sync order monitors from Gate position history after hub flat close. Store and display initial_stop_loss in trade records instead of post-amend exchange stops. Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
1.9 KiB
Python
67 lines
1.9 KiB
Python
"""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
|