Files
sanguo_vnpy_v2/scripts/qmt_relogin/qmt_relogin_test.py
T

144 lines
4.5 KiB
Python

# -*- coding: utf-8 -*-
"""Experiment: graceful close -> exe relaunch -> auto-login/auto-run/bridge.
Read-only w.r.t. trading: no order methods sent anywhere. Exit codes:
0 all green | 10 polite close refused | 11 launch/ready failed
12 instance not auto-run | 13 bridge rpc dead."""
import datetime
import io
import os
import re
import subprocess
import sys
import time
ACCOUNT = "66639661"
def log(m):
print("[%s] %s" % (datetime.datetime.now().strftime("%H:%M:%S"), m), flush=True)
def shot(name):
try:
import pyautogui
pyautogui.FAILSAFE = False
pyautogui.screenshot(r"C:\sanguo_bigqmt\%s" % name)
log("shot saved: %s" % name)
except Exception as e:
log("shot err %r" % e)
d = open(r"C:\sanguo_bigqmt\qmt_install_dir.txt", encoding="utf-8").read().strip()
root = os.path.dirname(d)
formula_log = os.path.join(root, "userdata", "log", "XtClient_Formula_20260909.log")
from bigqmt_signal_trader import qmt_launcher as L # noqa: E402
from bigqmt_signal_trader.redis_rpc import call_redis_rpc # noqa: E402
import redis # noqa: E402
try:
log("session locked: %s" % L.session_is_locked())
except Exception as e:
log("session check err %r" % e)
# 1) flush stale queue (composition pre-verified: 100% read-only, 0 orders)
with open(r"C:\redis\redis.conf") as f:
pw = re.search(r"^requirepass (\S+)", f.read(), re.M).group(1)
rc = redis.Redis(host="127.0.0.1", port=6379, db=5, password=pw,
decode_responses=True)
Q = "bigqmt:rpc:queue:%s" % ACCOUNT
n = rc.llen(Q)
rc.delete(Q)
log("queue flushed: %d stale read-only items deleted" % n)
# 2) graceful close, polite ONLY
subprocess.run(["taskkill", "/IM", "XtItClient.exe"], capture_output=True)
deadline = time.time() + 20
alive = True
while time.time() < deadline:
out = subprocess.run(["tasklist", "/FI", "IMAGENAME eq XtItClient.exe"],
capture_output=True, text=True).stdout or ""
if "XtItClient.exe" not in out:
alive = False
break
time.sleep(1)
if alive:
log("ABORT: polite close refused (dialog up?); NOT forcing")
shot("relogin_abort.png")
sys.exit(10)
log("polite close ok")
time.sleep(5)
t_launch = datetime.datetime.now()
log("relaunching via open_qmt(mode=exe) at %s" % t_launch.strftime("%H:%M:%S"))
# 3) exe relaunch: remembered-session auto-login path (no typing)
try:
waited = L.open_qmt(d, mode="exe", ready_timeout_seconds=240)
log("open_qmt(exe) ready in %.1fs" % waited)
except Exception as e:
log("FATAL open_qmt(exe): %r" % e)
shot("relogin_launchfail.png")
sys.exit(11)
def formula_events(since):
"""Return (banner_seen, runonce_seen) for log lines timestamped >= since."""
banner = runonce = False
try:
with io.open(formula_log, "r", encoding="gbk", errors="replace") as f:
for ln in f:
try:
ts = datetime.datetime.strptime(ln[:19], "%Y-%m-%d %H:%M:%S")
except ValueError:
continue
if ts < since:
continue
if "XtItClient/tags" in ln:
banner = True
if "runonce" in ln or "PythonFormula::" in ln:
runonce = True
except OSError:
pass
return banner, runonce
# 4) instance auto-run detection
t0 = time.time()
banner = runonce = False
while time.time() - t0 < 150:
time.sleep(5)
banner, runonce = formula_events(t_launch)
if runonce:
break
log("formula after relaunch: banner=%s runonce=%s (waited %.0fs)"
% (banner, runonce, time.time() - t0))
if not runonce:
log("instance did NOT auto-run after graceful relaunch")
shot("relogin_norun.png")
sys.exit(12)
# 5) bridge probe (ORDER-class query, safe immediately after login)
t0 = time.time()
got = None
while time.time() - t0 < 150:
for method, params in (("ping", {}), ("query_stock_orders",
{"account_id": ACCOUNT})):
try:
r = call_redis_rpc(rc, ACCOUNT, method, params, timeout_seconds=10)
if isinstance(r, dict):
got = (method, r)
break
except Exception as e:
last = "%s -> %s" % (method, e)
if got:
break
time.sleep(5)
if not got:
log("FATAL bridge rpc no answer; last=%r" % last)
shot("relogin_norpc.png")
sys.exit(13)
log("bridge answering via %r ok=%s" % (got[0], got[1].get("ok")))
shot("relogin_green.png")
log("=== RELOGIN_TEST_ALL_GREEN ===")
sys.exit(0)