From b56cd0243cfdf8ef0309ba192bfdb33a04980d04 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 3 Jul 2026 22:24:34 +0800 Subject: [PATCH] Stop auto-deleting roll pending legs when monitor briefly closes but CTP position remains. Co-authored-by: Cursor --- modules/trading/install.py | 48 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/modules/trading/install.py b/modules/trading/install.py index 8336f84..b973c3d 100644 --- a/modules/trading/install.py +++ b/modules/trading/install.py @@ -3657,7 +3657,25 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se (now_s, gid), ) + def _ctp_has_open_position(mode: str, sym: str, direction: str) -> bool: + """柜台是否仍有该品种方向持仓(滚仓组是否应保留)。""" + direction = (direction or "long").strip().lower() + sym = (sym or "").strip() + if not sym or not ctp_status(mode).get("connected"): + return False + for p in list(_ctp_positions(mode) or []) + list(trading_state.get_positions() or []): + if int(p.get("lots") or 0) <= 0: + continue + if (p.get("direction") or "long").strip().lower() != direction: + continue + if _match_ctp_symbol(sym, p.get("symbol") or ""): + return True + return False + def _close_stale_roll_groups(conn) -> int: + """仅当持仓监控已结束且柜台无对应持仓时,才归档滚仓组。""" + mode = get_trading_mode(get_setting) + _revive_monitors_for_open_ctp(conn, mode) rows = conn.execute( """SELECT g.*, m.status AS monitor_status FROM roll_groups g @@ -3665,9 +3683,33 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se WHERE g.status='active' AND (m.id IS NULL OR m.status != 'active')""" ).fetchall() + closed = 0 for r in rows: - _archive_roll_group(conn, dict(r), result_label="持仓已结束") - return len(rows) + grp = dict(r) + gid = int(grp.get("id") or 0) + sym = (grp.get("symbol") or "").strip() + direction = (grp.get("direction") or "long").strip().lower() + mid = int(grp.get("order_monitor_id") or 0) + if mid: + fresh = conn.execute( + "SELECT status FROM trade_order_monitors WHERE id=?", + (mid,), + ).fetchone() + if fresh and (fresh["status"] or "").strip().lower() == "active": + continue + if _ctp_has_open_position(mode, sym, direction): + if mid: + _revive_closed_monitor(conn, sym, direction) + logger.debug( + "skip stale roll group #%s: CTP position still open %s %s", + gid, sym, direction, + ) + continue + _archive_roll_group(conn, grp, result_label="持仓已结束") + closed += 1 + if closed: + commit_retry(conn) + return closed def _enrich_roll_leg_row(row: dict, mode: str) -> dict: out = dict(row) @@ -4703,6 +4745,8 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se init_strategy_tables(conn) mode = get_trading_mode(get_setting) try: + if ctp_status(mode).get("connected"): + _revive_monitors_for_open_ctp(conn, mode) check_roll_monitors( conn, get_mark_price_fn=lambda sym: _roll_mark_price(sym, {}, mode, allow_ctp=True),