fix(monitor): 策略监控v1.2二审两修——fills双轨配对逐对账+no_restart盘窗口径重设计 [vps]

10-01 二次审计 P1-3/P2-3 处置(抗辩记录在案:P3-2 同日不复活=alerts_store
既有设计决策、P3-3 expected=7 为告警设定值非事实硬编码,均维持现状):
①P1-3 fills_vs_paper 全表总量口径在 live 桌回归日必误红——重设计为
_dual_track_pairs(instance_id 优先精确配对,回退 strategies 首名,
reconcile_report v2 语义镜像)逐对笔数+金额对账;配对内 0:1=真双轨分歧
照红;无 running live 账户=n/a 绿(09-30 语义保留)。
②P2-3 no_restart 旧「creation>=当日00:00」双失效(盘中重启次日必绿+
08:05 当日必然未确认→red 恒降黄)——重设计为近 3 个确认交易日盘窗
(09:15-15:30)内出生=违例:违例日皆已确认→red 可达;夜间/盘前出生=
合法 deploy 窗;连休无新增确认日自然绿。
附带:_count_running 排除 mode='live'(同族口径,wmic needle 数的是
shadow 进程);registry titles 随 LOG_GLOBS/新语义更新(P3-4);
runbook 注记三处同步+连休预期形态注(P3-1)。
fixture 增双轨配对(8=live↔7=shadow);测试 24 绿(新增盘窗红/deploy 窗
绿/未确认日缓评估/降级载体迁 is_identity_resid/配对全状态链)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-10-01 07:50:55 +08:00
parent 4755e8dec6
commit 86e2e4a137
4 changed files with 181 additions and 64 deletions
+108 -33
View File
@@ -84,15 +84,79 @@ def _ro(path):
def _count_running(db_path):
"""running 账户数, 排除 live 桌(mode='live', live_orchestrator 权威口径)——
本检查族的 wmic needle 数的是 shadow 进程, live 桌回归日口径不再混桌
(10-01 二次审计 P1-3 同族; mode 未标 NULL/其它=影子/eod 舰队都计入)."""
conn = _ro(db_path)
try:
return int(conn.execute(
"SELECT COUNT(*) FROM paper_accounts WHERE status='running'"
"SELECT COUNT(*) FROM paper_accounts "
"WHERE status='running' AND (mode IS NULL OR mode!='live')"
).fetchone()[0])
finally:
conn.close()
def _desk_accounts(db_path, mode):
"""desk 账户行 [(id, instance_id, strategies_json)]; live 桌=mode='live'."""
conn = _ro(db_path)
try:
return conn.execute(
"SELECT id, instance_id, strategies FROM paper_accounts "
"WHERE mode=? AND status='running'", (mode,)).fetchall()
finally:
conn.close()
def _dual_track_pairs(db_path):
"""live↔shadow 配对 [(live_id, shadow_id)](reconcile_report v2 语义镜像:
instance_id 优先精确配对, 缺 instance 回退 strategies 首名; 10-01 审计
P1-3——全表总量口径在 live 桌回归日必误红, 逐对账才是双轨本义)."""
import json as _json
def _first_name(s):
try:
return (_json.loads(s or "[]") or [{}])[0].get("name", "")
except (ValueError, IndexError, KeyError):
return ""
lives = [(r[0], r[1], _first_name(r[2]))
for r in _desk_accounts(db_path, "live")]
shadows = [(r[0], r[1], _first_name(r[2]))
for r in _desk_accounts(db_path, "shadow")]
pairs, used = [], set()
for lid, linst, lname in lives:
cand = next(((s, ) for s, sinst, _ in shadows
if sinst and sinst == linst and s not in used), None) \
or next(((s, ) for s, _, sname in shadows
if not (linst or None) and lname and sname == lname
and s not in used), None)
if cand:
used.add(cand[0])
pairs.append((lid, cand[0]))
return pairs
def _day_fills(db_path, table, account_id, today):
"""当日某账户成交 (笔数, 金额)——live_trades 按 traded_at 前缀,
paper_trades 只数 rejected=0 按 bar_date."""
conn = _ro(db_path)
try:
if table == "live_trades":
row = conn.execute(
"SELECT COUNT(*), COALESCE(SUM(price*volume),0) FROM live_trades "
"WHERE account_id=? AND traded_at LIKE ?",
(account_id, today + "%")).fetchone()
else:
row = conn.execute(
"SELECT COUNT(*), COALESCE(SUM(price*volume),0) FROM paper_trades "
"WHERE account_id=? AND rejected=0 AND bar_date=?",
(account_id, today)).fetchone()
return int(row[0]), float(row[1] or 0)
finally:
conn.close()
def _pipeline_one(ctx, sql, args=()):
conn = _ro(ctx["pipeline_db"])
try:
@@ -125,13 +189,29 @@ def engines_actual(ctx, e):
def engines_no_restart(ctx, e):
"""全 shadow 进程 CreationDate < 当日 00:00(盘前出生=昨夜/今晨被重启)."""
"""近 N 个**确认**交易日盘窗(09:15~15:30)内出生的 shadow 进程=会话日重启
(§11.0; 10-01 二次审计 P2-3 重设计)。旧「creation>=当日00:00」双失效:
盘中重启次日必绿(只盖 00:00-08:05)且 08:05 当日必然未确认→red 恒降黄。
新语义: 违例日均为已确认交易日→red 可达, 不经未确认日降级; 夜间/盘前
出生=合法 deploy 窗不计; 连休无新增确认日=自然绿。"""
shadows = shadow_processes(run_wmic())
bad = [{"cmd": cmd, "created": c.isoformat() if c else None}
for cmd, c in shadows if c is None or c >= ctx["midnight"]]
days = trading_days(ctx["main_db"], ctx["now"].date())
windows = []
for d in sorted(days)[-e["lookback_confirmed"]:]:
ymd = d.replace("-", "")
windows.append((ymd + "0915", ymd + "1530"))
bad = []
for cmd, c in shadows:
if c is None:
bad.append({"cmd": cmd, "created": None})
continue
ts = c.strftime("%Y%m%d%H%M")
if any(a <= ts <= b for a, b in windows):
bad.append({"cmd": cmd, "created": c.isoformat()})
status = "green" if not bad else e["severity"]
return _done(e, status, f"<{ctx['midnight'].isoformat()}", len(bad),
{"violations": bad[:20], "procs": len(shadows)})
return _done(e, status, 0, len(bad),
{"violations": bad[:20], "procs": len(shadows),
"windows": [f"{a[:8]}" for a, _ in windows]})
# ---------- 盘前班: 日志面 ----------
@@ -217,33 +297,28 @@ def orders_leftover(ctx, e):
def fills_vs_paper(ctx, e):
"""当日 live_trades vs paper_trades(rejected=0 才是成交, 一表两态)笔数+金额
双对账; live 侧当日零行=绿 n/a(09-30 复盘修订)——shadow 桌本地撮合订单不出门
(runner.py 源码+paper_shadow_orders 全史 0 行+QMT 两日 cash/mktval 恒定三证),
LIVE=0 时代 live_trades 结构性为空, shadow 独走日(28/201/32)不构成对账对象;
live 桌回归出成交即自动武装。柜台↔台账恒等式仍归 15:10 仪式(infra 域)。"""
conn = _ro(ctx["main_db"])
try:
live = conn.execute(
"SELECT COUNT(*), COALESCE(SUM(price*volume),0) FROM live_trades "
"WHERE traded_at LIKE ?", (ctx["today"] + "%",)).fetchone()
paper = conn.execute(
"SELECT COUNT(*), COALESCE(SUM(price*volume),0) FROM paper_trades "
"WHERE rejected=0 AND bar_date=?", (ctx["today"],)).fetchone()
finally:
conn.close()
ln, lam = int(live[0]), float(live[1] or 0)
pn, pam = int(paper[0]), float(paper[1] or 0)
if ln == 0:
return _done(e, "green", "n/a", "no-live-fills",
{"live_count": 0, "paper_count": pn,
"paper_amount": round(pam, 2)})
ok = ln == pn and abs(lam - pam) <= e["amount_tol"]
status = "green" if ok else e["severity"]
return _done(e, status, f"count相等 且 金额±{e['amount_tol']}",
f"{ln}:{pn} {lam:.2f}:{pam:.2f}",
{"live_count": ln, "live_amount": round(lam, 2),
"paper_count": pn, "paper_amount": round(pam, 2)})
"""当日双轨配对逐对账: live 账户 vs 其 shadow 镜像(笔数+金额, 容差内)。
10-01 二次审计 P1-3 重设计——全表总量口径无账户配对, live 桌回归日两桌
各跑各的策略必总量不平=误红; 逐对账才是「同策略同参数分别接真/假 broker」
的双轨本义。无 running live 账户=n/a 绿(09-30 修订保留: shadow 桌本地撮合
订单不出门, LIVE=0 时代 shadow 独走日不构成对账对象); 配对内 0:1 也算不平
(影子镜像未跟=真双轨分歧)。柜台↔台账恒等式仍归 15:10 仪式(infra 域)。"""
pairs = _dual_track_pairs(ctx["main_db"])
if not pairs:
return _done(e, "green", "n/a", "no-live-desk",
{"pairs": 0, "note": "无 running live 账户, 双轨对账不适用"})
bad = []
for lid, sid in pairs:
ln, lam = _day_fills(ctx["main_db"], "live_trades", lid, ctx["today"])
pn, pam = _day_fills(ctx["main_db"], "paper_trades", sid, ctx["today"])
if not (ln == pn and abs(lam - pam) <= e["amount_tol"]):
bad.append({"live": lid, "shadow": sid,
"live_count": ln, "live_amount": round(lam, 2),
"paper_count": pn, "paper_amount": round(pam, 2)})
status = "green" if not bad else e["severity"]
return _done(e, status, f"逐对count相等 且 金额±{e['amount_tol']}",
f"{len(pairs) - len(bad)}/{len(pairs)}对平",
{"pairs": len(pairs), "mismatch": bad[:10]})
def weekly_ran(ctx, e):
@@ -20,17 +20,17 @@ REGISTRY = [
"enabled": True,
"title": "wmic 实存 shadow 进程数≠DB running 数"},
{"key": "strategy.engines_no_restart", "fn": "engines_no_restart",
"shifts": ("morn", "noon"), "severity": "red", "trading_day": True,
"enabled": True,
"title": "引擎盘前重生(日内重启嫌疑)"},
"shifts": ("morn", "noon"), "severity": "red", "trading_day": False,
"lookback_confirmed": 3, "enabled": True,
"title": "引擎会话日盘窗重生(§11.0,近3确认交易日 09:15-15:30)"},
{"key": "strategy.logs_error_storm", "fn": "logs_error_storm",
"shifts": ("morn", "noon"), "severity": "yellow", "trading_day": False,
"window_hours": 26, "max_errors": 20, "enabled": True,
"title": "live 日志 26h 窗 ERROR/Traceback 超带"},
"title": "引擎日志(live+shadow)26h 窗 ERROR/Traceback 超带"},
{"key": "strategy.logs_stale", "fn": "logs_stale",
"shifts": ("morn",), "severity": "yellow", "trading_day": False,
"weekday": True, "max_age_days": 4, "enabled": True,
"title": "live 日志停滞(>4 天无新写)"},
"title": "引擎日志(live+shadow)停滞(>4 天无新写)"},
{"key": "strategy.is_report_ran", "fn": "is_report_ran",
"shifts": ("eve",), "severity": "yellow", "trading_day": True,
"enabled": True,
@@ -46,7 +46,7 @@ REGISTRY = [
{"key": "strategy.fills_vs_paper", "fn": "fills_vs_paper",
"shifts": ("eve",), "severity": "red", "trading_day": True,
"amount_tol": 0.01, "enabled": True,
"title": "live vs paper 当日成交对账不平"},
"title": "双轨配对当日成交对账不平(live↔shadow 逐对)"},
{"key": "strategy.weekly_ran", "fn": "weekly_ran",
"shifts": ("eve",), "severity": "yellow", "trading_day": True,
"enabled": True,