Fix UI stuck after manual close: fast API return and closing state instead of SL/TP revive.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -59,6 +59,7 @@ from modules.trading.order_pending import (
|
||||
from modules.core.db_conn import commit_retry, execute_retry
|
||||
from modules.trading.sl_tp_guard import (
|
||||
cancel_monitor_exit_orders,
|
||||
clear_close_pending,
|
||||
close_pending_active,
|
||||
ensure_monitor_order_columns,
|
||||
mark_close_pending,
|
||||
@@ -1170,6 +1171,8 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
direction: str,
|
||||
) -> Optional[dict]:
|
||||
"""只读:从已关闭监控补全止盈止损,不写库。"""
|
||||
if should_skip_monitor_revive(sym, direction):
|
||||
return {"symbol": sym, "direction": direction}
|
||||
if not mon:
|
||||
rsl, rtp, rtrail, rinitial = _restore_sl_tp_from_closed(conn, sym, direction)
|
||||
if rsl is None and rtp is None and not rtrail:
|
||||
@@ -1197,6 +1200,24 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
merged["initial_stop_loss"] = rinitial
|
||||
return merged
|
||||
|
||||
def _row_as_closing_state(row: dict) -> dict:
|
||||
"""手动/程序平仓已提交、柜台持仓未清零时的展示状态。"""
|
||||
out = dict(row)
|
||||
out["order_state"] = "closing"
|
||||
out["source_label"] = "平仓处理中"
|
||||
out["stop_loss"] = None
|
||||
out["take_profit"] = None
|
||||
out["sl_monitoring"] = False
|
||||
out["tp_monitoring"] = False
|
||||
out["sl_order_active"] = False
|
||||
out["tp_order_active"] = False
|
||||
out["pending_orders"] = []
|
||||
out["can_close"] = False
|
||||
out["close_allowed"] = False
|
||||
out["can_place_orders"] = False
|
||||
out["trailing_be"] = False
|
||||
return out
|
||||
|
||||
def _revive_closed_monitor(conn, symbol: str, direction: str) -> Optional[dict]:
|
||||
"""柜台仍有持仓但本地监控被误关时,恢复最近一条同品种记录。"""
|
||||
if should_skip_monitor_revive(symbol, direction):
|
||||
@@ -2413,12 +2434,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
direction = p.get("direction") or "long"
|
||||
if not mon:
|
||||
mon = _find_pending_monitor(conn, ths, direction)
|
||||
if not mon:
|
||||
if not mon and not close_pending_active(ths, direction):
|
||||
if fast:
|
||||
mon = _find_active_monitor(conn, ths, direction)
|
||||
else:
|
||||
mon = _find_or_revive_monitor(conn, ths, direction)
|
||||
if mon:
|
||||
if mon and not close_pending_active(ths, direction):
|
||||
if fast:
|
||||
mon = _overlay_sl_tp_readonly(conn, mon, ths, direction) or mon
|
||||
else:
|
||||
@@ -2432,14 +2453,18 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
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
|
||||
elif fast:
|
||||
elif fast and not close_pending_active(ths, direction):
|
||||
mon = _overlay_sl_tp_readonly(conn, None, ths, direction)
|
||||
elif close_pending_active(ths, direction):
|
||||
mon = mon or {"symbol": ths, "direction": direction}
|
||||
try:
|
||||
row = _compose_position_row(
|
||||
conn, mon=mon, ctp=p, mode=mode, capital=capital,
|
||||
now_iso=now_iso, fast=fast,
|
||||
)
|
||||
if row:
|
||||
if close_pending_active(ths, direction):
|
||||
row = _row_as_closing_state(row)
|
||||
rows.append(row)
|
||||
except Exception as exc:
|
||||
logger.warning("compose ctp position row failed: %s", exc)
|
||||
@@ -3433,14 +3458,36 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
_close_all_monitors_for_sym_dir(conn, sym, direction)
|
||||
conn.commit()
|
||||
try:
|
||||
from modules.ctp.ctp_trade_sync import sync_trade_logs_from_ctp
|
||||
sync_trade_logs_from_ctp(conn, mode, capital=capital, trading_mode=mode)
|
||||
on_user_initiated_close(conn, trading_day=trading_day_label())
|
||||
conn.commit()
|
||||
except Exception as exc:
|
||||
logger.debug("sync trades after close: %s", exc)
|
||||
logger.debug("user initiated close hook: %s", exc)
|
||||
cap_snapshot = capital
|
||||
conn.close()
|
||||
_push_position_snapshot_async()
|
||||
return jsonify({"ok": True, "message": "已平仓,交易记录已写入"})
|
||||
|
||||
def _after_close() -> None:
|
||||
try:
|
||||
bg = get_db()
|
||||
try:
|
||||
init_strategy_tables(bg)
|
||||
from modules.ctp.ctp_trade_sync import sync_trade_logs_from_ctp
|
||||
sync_trade_logs_from_ctp(
|
||||
bg, mode, capital=cap_snapshot, trading_mode=mode,
|
||||
)
|
||||
bg.commit()
|
||||
finally:
|
||||
bg.close()
|
||||
except Exception as exc:
|
||||
logger.debug("sync trades after close: %s", exc)
|
||||
_push_position_snapshot_async(fast=True)
|
||||
|
||||
threading.Thread(target=_after_close, daemon=True, name="close-finalize").start()
|
||||
_push_position_snapshot_async(fast=True)
|
||||
return jsonify({
|
||||
"ok": True,
|
||||
"message": "平仓委托已提交",
|
||||
"closing": True,
|
||||
})
|
||||
except ValueError as exc:
|
||||
conn.close()
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
Reference in New Issue
Block a user