Files
crypto_monitor/gate_position_history_lib.py
dekun 59a45ed027 fix: Gate hub close sync and trade record open stop-loss snapshot
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>
2026-06-09 18:01:59 +08:00

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