From ae28bc62730b7518fa4ecad2b0055c5620749735 Mon Sep 17 00:00:00 2001 From: dekun Date: Thu, 2 Jul 2026 23:04:34 +0800 Subject: [PATCH] Count roll groups as one position slot and protect monitors during roll. Include active roll groups in deduped position slots; normalize CTP position keys in reconcile; skip closing monitors tied to active roll groups. Co-authored-by: Cursor --- modules/risk/account_risk_lib.py | 9 +++++++++ modules/trading/sl_tp_guard.py | 18 ++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/modules/risk/account_risk_lib.py b/modules/risk/account_risk_lib.py index 364fd0c..2d1c7aa 100644 --- a/modules/risk/account_risk_lib.py +++ b/modules/risk/account_risk_lib.py @@ -289,6 +289,15 @@ def active_position_slots_from_monitors(conn) -> set[tuple[str, str]]: sym = (r["symbol"] or "").strip() if sym: keys.add(_position_slot_key(sym, r["direction"] or "long")) + for r in conn.execute( + """SELECT m.symbol, m.direction + FROM roll_groups g + JOIN trade_order_monitors m ON m.id = g.order_monitor_id + WHERE g.status='active'""" + ): + sym = (r["symbol"] or "").strip() + if sym: + keys.add(_position_slot_key(sym, r["direction"] or "long")) except Exception: pass return keys diff --git a/modules/trading/sl_tp_guard.py b/modules/trading/sl_tp_guard.py index f573cd8..d475a4a 100644 --- a/modules/trading/sl_tp_guard.py +++ b/modules/trading/sl_tp_guard.py @@ -647,9 +647,11 @@ def reconcile_monitors_without_position(conn, mode: str, *, grace_sec: int = 120 for p in positions: if int(p.get("lots") or 0) <= 0: continue - sym = (p.get("symbol") or "").lower() + sym = (p.get("symbol") or "").strip() direction = p.get("direction") or "long" - position_keys.add((sym, direction)) + if sym: + base = sym.lower().split(".")[0] + position_keys.add((base, (direction or "long").strip().lower())) try: from modules.ctp.ctp_trading_state import trading_state @@ -657,9 +659,11 @@ def reconcile_monitors_without_position(conn, mode: str, *, grace_sec: int = 120 lots = int(p.get("lots") or 0) if lots <= 0: continue - sym = (p.get("symbol") or "").lower() + sym = (p.get("symbol") or "").strip() direction = p.get("direction") or "long" - position_keys.add((sym, direction)) + if sym: + base = sym.lower().split(".")[0] + position_keys.add((base, (direction or "long").strip().lower())) except Exception: pass @@ -709,6 +713,12 @@ def reconcile_monitors_without_position(conn, mode: str, *, grace_sec: int = 120 break if matched: continue + mid = mon.get("id") + if conn.execute( + "SELECT 1 FROM roll_groups WHERE order_monitor_id=? AND status='active' LIMIT 1", + (mid,), + ).fetchone(): + continue try: cancel_monitor_exit_orders(conn, mon, mode=mode) except Exception as exc: