Restore stop-loss and take-profit monitors after restart and CTP reconnect.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+58
-5
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user