2b2ae3c4d8
- qmt_sentinel.py: sanguo-qmt-sentinel 每30min(进程+桥RPC ping判红,账户类仅记录); 红->schtasks /run sanguo-qmt-relogin->轮询桥恢复<=10min->复检留痕;无退避(拍板#4) - qmt_probe_0915.py: sanguo-qmt-probe0915 周一至五09:15; 510050.SH买100@0.01 FIX_PRICE必不成交单(09-09 09:40实证参数形状)+即撤; status57废单=会话陈旧 ->触发relogin赶开盘; 窗口硬卡09:15:00-09:19:30(09:20-09:25不可撤) - qmt_gate_common.py: redis/ping/tasklist/触发/轮询/gate留痕共享库 - 留痕: C:\sanguo_bigqmt\gate\qmt_gate_YYYYMMDD.log + qmt_gate_latest.json - ui_act.py 修复: activate仅contains时KeyError; credtype LocalFree类型错; 新增text op (typewrite连打丢数字/shift字符,验证码须key/hotkey逐字符打) - 实测: 首夜14跳全绿cash零漂移; 下单RPC管线ok=True; SKIP/退出码/桥死分支全验 - schtask XML x2(SYSTEM+HighestAvailable,照sanguo-xt-daily模式)
123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Shared helpers for the QMT gate scripts (qmt_sentinel.py / qmt_probe_0915.py).
|
|
|
|
Design: scripts judge AVAILABILITY (process + bridge RPC + counter behaviour),
|
|
agents/humans judge CORRECTNESS (cash baseline drift is recorded, never red).
|
|
Audit trail: C:\\sanguo_bigqmt\\gate\\qmt_gate_YYYYMMDD.log (append) +
|
|
qmt_gate_latest.json (overwrite status light).
|
|
"""
|
|
import datetime
|
|
import json
|
|
import re
|
|
import subprocess
|
|
|
|
ACCOUNT = "66639661"
|
|
GATE_DIR = r"C:\sanguo_bigqmt\gate"
|
|
RELOGIN_TASK = "sanguo-qmt-relogin"
|
|
QMT_PROCS = ("XtItClient.exe", "XtMiniQmt.exe", "miniquote.exe")
|
|
|
|
|
|
def now_str():
|
|
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
def gate_log(kind, verdict, msg):
|
|
line = "%s [%s] %s %s" % (now_str(), kind, verdict, msg)
|
|
try:
|
|
import os
|
|
os.makedirs(GATE_DIR, exist_ok=True)
|
|
path = r"%s\qmt_gate_%s.log" % (
|
|
GATE_DIR, datetime.datetime.now().strftime("%Y%m%d"))
|
|
with open(path, "a", encoding="utf-8") as f:
|
|
f.write(line + "\n")
|
|
except Exception as exc:
|
|
line += " (log-write-failed %r)" % exc
|
|
print(line, flush=True)
|
|
return line
|
|
|
|
|
|
def gate_json(kind, verdict, **detail):
|
|
import os
|
|
payload = {"ts": now_str(), "kind": kind, "verdict": verdict}
|
|
payload.update(detail)
|
|
try:
|
|
os.makedirs(GATE_DIR, exist_ok=True)
|
|
with open(r"%s\qmt_gate_latest.json" % GATE_DIR, "w",
|
|
encoding="utf-8") as f:
|
|
json.dump(payload, f, ensure_ascii=False, default=str, indent=1)
|
|
except Exception as exc:
|
|
print("GATE_JSON_WRITE_FAIL %r" % exc, flush=True)
|
|
return payload
|
|
|
|
|
|
def redis_client():
|
|
import redis
|
|
with open(r"C:\redis\redis.conf") as f:
|
|
pw = re.search(r"^requirepass (\S+)", f.read(), re.M).group(1)
|
|
return redis.Redis(host="127.0.0.1", port=6379, db=5, password=pw,
|
|
socket_connect_timeout=5, decode_responses=True)
|
|
|
|
|
|
def bridge_ping(rc, timeout=10):
|
|
"""(up, detail) — up means the RPC server answered ping with ok+pong."""
|
|
from bigqmt_signal_trader.redis_rpc import call_redis_rpc
|
|
try:
|
|
r = call_redis_rpc(rc, ACCOUNT, "ping", {}, timeout_seconds=timeout)
|
|
except Exception as exc:
|
|
return False, "RPC_DOWN %s %s" % (type(exc).__name__, str(exc)[:120])
|
|
if not isinstance(r, dict):
|
|
return False, "RPC_BAD %r" % (r,)
|
|
if not r.get("ok"):
|
|
return False, "RPC_ERR %s" % str(r.get("error") or r)[:150]
|
|
d = r.get("data") or {}
|
|
if not d.get("pong"):
|
|
return False, "RPC_NO_PONG %s" % str(d)[:150]
|
|
return True, "server_time=%s rev=%s allow_order=%s" % (
|
|
d.get("server_time"), d.get("rpc_revision"), d.get("allow_order_methods"))
|
|
|
|
|
|
def procs_alive():
|
|
"""QMT family process presence via tasklist (no psutil dependency)."""
|
|
out = subprocess.run(["tasklist", "/FO", "CSV", "/NH"],
|
|
capture_output=True, text=True, timeout=30).stdout or ""
|
|
return {name: ('"%s"' % name) in out for name in QMT_PROCS}
|
|
|
|
|
|
def relogin_running():
|
|
"""True if the v2 relogin python is mid-flight (avoid double trigger)."""
|
|
try:
|
|
import psutil
|
|
except ImportError:
|
|
return False
|
|
for p in psutil.process_iter(["pid", "name", "cmdline"]):
|
|
try:
|
|
if "python" not in (p.info["name"] or "").lower():
|
|
continue
|
|
cmd = " ".join(p.info["cmdline"] or [])
|
|
except Exception:
|
|
continue
|
|
if "qmt_relogin.py" in cmd:
|
|
return True
|
|
return False
|
|
|
|
|
|
def trigger_relogin():
|
|
r = subprocess.run(["schtasks", "/run", "/tn", RELOGIN_TASK],
|
|
capture_output=True, text=True, timeout=30)
|
|
ok = r.returncode == 0
|
|
return ok, ((r.stdout or "") + (r.stderr or "")).strip()[:120]
|
|
|
|
|
|
def wait_bridge(rc, timeout_sec, kind):
|
|
"""Poll bridge ping until it answers; every probe logged once at the end."""
|
|
import time
|
|
t0 = time.time()
|
|
last = ""
|
|
while time.time() - t0 < timeout_sec:
|
|
up, detail = bridge_ping(rc)
|
|
if up:
|
|
return True, int(time.time() - t0), detail
|
|
last = detail
|
|
time.sleep(5)
|
|
return False, int(time.time() - t0), last
|