fix: 重启后立即读库展示持仓,CTP异步重连不再阻塞
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+76
-24
@@ -78,6 +78,7 @@ from vnpy_bridge import (
|
||||
ctp_status,
|
||||
execute_order,
|
||||
get_bridge,
|
||||
set_position_refresh_callback,
|
||||
)
|
||||
|
||||
|
||||
@@ -128,9 +129,18 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def _ctp_positions(mode: str, *, refresh_if_empty: bool = True) -> list:
|
||||
def _ctp_positions(
|
||||
mode: str,
|
||||
*,
|
||||
refresh_if_empty: bool = True,
|
||||
refresh_margin: bool = False,
|
||||
) -> list:
|
||||
try:
|
||||
return ctp_list_positions(mode, refresh_if_empty=refresh_if_empty)
|
||||
return ctp_list_positions(
|
||||
mode,
|
||||
refresh_if_empty=refresh_if_empty,
|
||||
refresh_margin=refresh_margin,
|
||||
)
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
@@ -431,6 +441,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
mode: str,
|
||||
capital: float,
|
||||
now_iso: str,
|
||||
fast: bool = False,
|
||||
) -> Optional[dict]:
|
||||
if not mon and not ctp:
|
||||
return None
|
||||
@@ -463,14 +474,16 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
holding = _holding_duration(open_time, now_iso) if open_time else ""
|
||||
|
||||
mark = None
|
||||
if ctp_status(mode).get("connected"):
|
||||
if not fast and ctp_status(mode).get("connected"):
|
||||
mark = ctp_get_tick_price(mode, sym)
|
||||
if (mark is None or mark <= 0) and codes:
|
||||
if not fast and (mark is None or mark <= 0) and codes:
|
||||
mark = fetch_price(
|
||||
sym,
|
||||
codes.get("market_code", ""),
|
||||
codes.get("sina_code", ""),
|
||||
)
|
||||
if mark is None or mark <= 0:
|
||||
mark = entry if entry else None
|
||||
close_est = float(mark) if mark and mark > 0 else entry
|
||||
if float_pnl is None and mark and entry:
|
||||
pos_tmp = calc_position_metrics(
|
||||
@@ -564,7 +577,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"trailing_r_locked": int(mon.get("trailing_r_locked") or 0) if mon else 0,
|
||||
}
|
||||
|
||||
def _build_trading_live_rows(conn) -> list[dict]:
|
||||
def _build_trading_live_rows(conn, *, fast: bool = False) -> list[dict]:
|
||||
from zoneinfo import ZoneInfo
|
||||
tz = ZoneInfo("Asia/Shanghai")
|
||||
now_iso = datetime.now(tz).strftime("%Y-%m-%dT%H:%M")
|
||||
@@ -583,7 +596,10 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
if key not in monitor_by_key:
|
||||
monitor_by_key[key] = mon
|
||||
|
||||
ctp_list: list[dict] = _ctp_positions(mode) if ctp_status(mode).get("connected") else []
|
||||
ctp_list: list[dict] = (
|
||||
_ctp_positions(mode, refresh_if_empty=not fast, refresh_margin=not fast)
|
||||
if ctp_status(mode).get("connected") else []
|
||||
)
|
||||
ctp_by_key: dict[str, dict] = {}
|
||||
for p in ctp_list:
|
||||
if int(p.get("lots") or 0) <= 0:
|
||||
@@ -617,6 +633,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
try:
|
||||
row = _compose_position_row(
|
||||
conn, mon=mon, ctp=ctp, mode=mode, capital=capital, now_iso=now_iso,
|
||||
fast=fast,
|
||||
)
|
||||
if row:
|
||||
rows.append(row)
|
||||
@@ -647,6 +664,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
try:
|
||||
row = _compose_position_row(
|
||||
conn, mon=mon, ctp=ctp, mode=mode, capital=capital, now_iso=now_iso,
|
||||
fast=fast,
|
||||
)
|
||||
if row:
|
||||
rows.append(row)
|
||||
@@ -663,11 +681,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
deduped.append(row)
|
||||
return deduped
|
||||
|
||||
def _build_trading_live_payload(conn) -> dict:
|
||||
def _build_trading_live_payload(conn, *, fast: bool = False) -> dict:
|
||||
mode = get_trading_mode(get_setting)
|
||||
ctp_st = ctp_status(mode)
|
||||
_ensure_monitors_from_ctp(conn, mode)
|
||||
rows = _build_trading_live_rows(conn)
|
||||
if not fast:
|
||||
_ensure_monitors_from_ctp(conn, mode)
|
||||
rows = _build_trading_live_rows(conn, fast=fast)
|
||||
pending_orders = _build_pending_orders(conn, mode)
|
||||
capital = _capital(conn)
|
||||
risk = get_risk_status(conn, active_count=_effective_active_position_count(conn, mode))
|
||||
@@ -682,26 +701,54 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"trading_session": is_trading_session(),
|
||||
}
|
||||
|
||||
def _refresh_trading_live_snapshot() -> dict:
|
||||
def _refresh_trading_live_snapshot(*, fast: bool = False) -> dict:
|
||||
mode = get_trading_mode(get_setting)
|
||||
if not fast and ctp_status(mode).get("connected"):
|
||||
try:
|
||||
get_bridge().refresh_positions()
|
||||
except Exception as exc:
|
||||
logger.debug("refresh positions before snapshot: %s", exc)
|
||||
conn = get_db()
|
||||
try:
|
||||
init_strategy_tables(conn)
|
||||
payload = _build_trading_live_payload(conn)
|
||||
payload = _build_trading_live_payload(conn, fast=fast)
|
||||
conn.commit()
|
||||
return payload
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def _push_position_snapshot_async() -> None:
|
||||
def _push_position_snapshot_async(*, fast: bool = False) -> None:
|
||||
def _run() -> None:
|
||||
try:
|
||||
payload = _refresh_trading_live_snapshot()
|
||||
payload = _refresh_trading_live_snapshot(fast=fast)
|
||||
position_hub.broadcast("positions", payload)
|
||||
except Exception as exc:
|
||||
logger.debug("push position snapshot: %s", exc)
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
|
||||
def _bootstrap_trading_runtime() -> None:
|
||||
"""进程启动:立刻读库展示持仓,并异步连 CTP。"""
|
||||
set_position_refresh_callback(
|
||||
lambda: _push_position_snapshot_async(fast=False)
|
||||
)
|
||||
|
||||
def _warm() -> None:
|
||||
try:
|
||||
payload = _refresh_trading_live_snapshot(fast=True)
|
||||
position_hub.set_snapshot(payload)
|
||||
position_hub.broadcast("positions", payload)
|
||||
except Exception as exc:
|
||||
logger.warning("bootstrap position snapshot: %s", exc)
|
||||
|
||||
threading.Thread(target=_warm, daemon=True, name="position-bootstrap").start()
|
||||
try:
|
||||
from vnpy_bridge import ctp_start_connect
|
||||
mode = get_trading_mode(get_setting)
|
||||
ctp_start_connect(mode, force=False)
|
||||
except Exception as exc:
|
||||
logger.debug("bootstrap ctp connect: %s", exc)
|
||||
|
||||
@app.route("/trade")
|
||||
@login_required
|
||||
def trade_page():
|
||||
@@ -788,7 +835,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
conn = get_db()
|
||||
try:
|
||||
init_strategy_tables(conn)
|
||||
payload = _build_trading_live_payload(conn)
|
||||
payload = _build_trading_live_payload(conn, fast=True)
|
||||
conn.commit()
|
||||
position_hub.set_snapshot(payload)
|
||||
return jsonify(payload)
|
||||
@@ -807,7 +854,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
if snap:
|
||||
yield sse_format("positions", snap)
|
||||
else:
|
||||
payload = _refresh_trading_live_snapshot()
|
||||
payload = _refresh_trading_live_snapshot(fast=True)
|
||||
position_hub.set_snapshot(payload)
|
||||
yield sse_format("positions", payload)
|
||||
while True:
|
||||
@@ -1322,20 +1369,24 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"status": st,
|
||||
"account": acc,
|
||||
})
|
||||
try:
|
||||
st = ctp_connect(mode, force=force)
|
||||
acc = _ctp_account(mode)
|
||||
return jsonify({"ok": True, "status": st, "account": acc})
|
||||
except Exception as exc:
|
||||
st = ctp_status(mode)
|
||||
return jsonify({"ok": False, "error": str(exc), "status": st}), 400
|
||||
return jsonify({
|
||||
"ok": False,
|
||||
"error": st.get("last_error") or "CTP 连接未启动",
|
||||
"status": st,
|
||||
"account": acc,
|
||||
}), 400
|
||||
|
||||
@app.route("/api/ctp/status")
|
||||
@login_required
|
||||
def api_ctp_status():
|
||||
mode = get_trading_mode(get_setting)
|
||||
st = ctp_status(mode)
|
||||
acc = _ctp_account(mode) if st.get("connected") else {}
|
||||
acc = {}
|
||||
if st.get("connected"):
|
||||
try:
|
||||
acc = _ctp_account(mode)
|
||||
except Exception:
|
||||
acc = {}
|
||||
return jsonify({"ok": True, "status": st, "account": acc})
|
||||
|
||||
@app.route("/api/account_snapshot")
|
||||
@@ -1777,9 +1828,10 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
interval=1,
|
||||
)
|
||||
start_position_worker(
|
||||
refresh_fn=_refresh_trading_live_snapshot,
|
||||
refresh_fn=lambda: _refresh_trading_live_snapshot(fast=False),
|
||||
interval=1,
|
||||
)
|
||||
_bootstrap_trading_runtime()
|
||||
start_ctp_fee_worker(
|
||||
get_mode_fn=lambda: get_trading_mode(get_setting),
|
||||
get_setting_fn=get_setting,
|
||||
|
||||
Reference in New Issue
Block a user