# -*- 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)