diff --git a/install_trading.py b/install_trading.py index 95ac033..45fd75a 100644 --- a/install_trading.py +++ b/install_trading.py @@ -603,6 +603,40 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se ) return None, None, 0, None + def _restore_monitor_sl_tp_if_missing( + conn, + mon: Optional[dict], + sym: str, + direction: str, + ) -> Optional[dict]: + """活跃监控缺少止盈止损时,从最近关闭的同品种记录恢复并写回数据库。""" + if not mon: + return None + sl = mon.get("stop_loss") + tp = mon.get("take_profit") + trailing = int(mon.get("trailing_be") or 0) + if sl is not None or tp is not None or trailing: + return mon + rsl, rtp, rtrail, rinitial = _restore_sl_tp_from_closed(conn, sym, direction) + if rsl is None and rtp is None and not rtrail: + return mon + conn.execute( + """UPDATE trade_order_monitors SET + stop_loss=?, take_profit=?, trailing_be=?, initial_stop_loss=? + WHERE id=? AND status='active'""", + (rsl, rtp, rtrail, rinitial, int(mon["id"])), + ) + row = conn.execute( + "SELECT * FROM trade_order_monitors WHERE id=?", (int(mon["id"]),), + ).fetchone() + if row: + logger.info( + "恢复止盈止损 monitor=%s sym=%s sl=%s tp=%s", + mon.get("id"), sym, rsl, rtp, + ) + return dict(row) + return mon + def _ctp_position_keys(mode: str) -> set[tuple[str, str]]: keys: set[tuple[str, str]] = set() for p in _ctp_positions(mode): @@ -1567,19 +1601,22 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se mon = mv break ths = _ctp_pos_to_ths_code(p) or (p.get("symbol") or "") + direction = p.get("direction") or "long" if not mon: - mon = _find_pending_monitor( - conn, ths, p.get("direction") or "long", - ) + mon = _find_pending_monitor(conn, ths, direction) + if not mon: + mon = _find_or_revive_monitor(conn, ths, direction) if mon: + mon = _restore_monitor_sl_tp_if_missing(conn, mon, ths, direction) or mon _sync_monitor_from_ctp( conn, int(mon["id"]), mon.get("symbol") or ths, - mon.get("direction") or p.get("direction") or "long", + mon.get("direction") or direction, mode, ctp=p, capital=capital, ) mon = _find_active_monitor( - conn, mon.get("symbol") or ths, mon.get("direction") or "long", + conn, mon.get("symbol") or ths, mon.get("direction") or direction, ) or mon + mon = _restore_monitor_sl_tp_if_missing(conn, mon, ths, direction) or mon try: row = _compose_position_row( conn, mon=mon, ctp=p, mode=mode, capital=capital, @@ -1610,6 +1647,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se if ctp_st.get("connected") and (not fast or _has_pending_monitors(conn)): _reconcile_pending(conn, mode, capital=capital) if ctp_st.get("connected") and not fast: + _ensure_monitors_from_ctp(conn, mode) _sync_trade_monitors_with_ctp(conn, mode) rows = _build_trading_live_rows(conn, fast=fast) active_orders = _build_active_orders( @@ -1797,6 +1835,21 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se return _schedule_recommend_refresh() + def _after_connect() -> None: + try: + conn = get_db() + try: + init_strategy_tables(conn) + _ensure_monitors_from_ctp(conn, mode) + conn.commit() + finally: + conn.close() + _push_position_snapshot_async(fast=False) + except Exception as exc: + logger.debug("ctp connected monitor restore: %s", exc) + + threading.Thread(target=_after_connect, daemon=True, name="ctp-monitor-restore").start() + @app.route("/trade") @login_required def trade_page(): diff --git a/sl_tp_guard.py b/sl_tp_guard.py index 2139a6b..bde697e 100644 --- a/sl_tp_guard.py +++ b/sl_tp_guard.py @@ -614,8 +614,16 @@ def reconcile_monitors_without_position(conn, mode: str, *, grace_sec: int = 120 position_keys.add((sym, direction)) margin_used = ctp_account_margin_used(mode) or 0.0 - if not position_keys and margin_used > 300: - return 0 + if not position_keys: + if margin_used > 100: + return 0 + try: + bridge = get_bridge() + since_connect = time.time() - float(getattr(bridge, "_last_connect_ok_ts", 0) or 0) + if since_connect < 180: + return 0 + except Exception: + return 0 now_ts = time.time()