diff --git a/app/services/param_store.py b/app/services/param_store.py index 42f7bdc..f4fa522 100644 --- a/app/services/param_store.py +++ b/app/services/param_store.py @@ -47,6 +47,11 @@ RUNTIME_EXTRA = { # (它只回 ok=False 不抛异常, 调用点又把返回值丢了)。后果是 prev_ymd 永远读回 0, # 「连续 N 日」的按日推进彻底失效, 修复形同虚设。加表时**必须同步加这里**。 "PMS_RECON_STREAK_YMD": (0, int, "上次推进连续天数的交易日 YYYYMMDD (同日不重复计数)"), + # 2026-08-14: 上游信号告警面板的三个显示参数。原来只在 upstream_signals.py 里用 get_int + # 兜底、没登记, 所以设置页调不到、改一次得重新部署。注册进来后可在页面实时调 (即时生效)。 + "PMS_UPSTREAM_ALERT_SCAN": (300, int, "告警扫描窗: 每轮从告警流取最近 N 条再分类限量 (窗要大于各类上限之和, 防 price_notice 等高频源把风控告警挤出最新N)"), + "PMS_UPSTREAM_ALERT_PER_CAT": (20, int, "告警每类上限: 同一类型最多显示最新 N 条 (砍单类刷屏, 不挤别的类)"), + "PMS_UPSTREAM_ALERT_MAX_AGE_MIN": (60, int, "告警时间窗(分钟): 超过此龄判为陈旧并在页面置灰标注(不删除, 风控不丢信息), 0=不限龄。与每类上限正交, 先按龄标记再每类限量"), } # **读不到时必须按"已暂停"处理的键 (fail-closed)。** @@ -316,6 +321,8 @@ _RANGES = { "PMS_OPEN_REF_DRIFT_MAX": (0, 0.5), "PMS_JUDGE_TICK_BUDGET_SEC": (0, 240), # 上限 240 = scheduler 给调度任务设的软超时。填得比它还大, 预算就形同虚设, # 任务会先被 celery 打死 (而且是在已经落了一部分表之后)。 + "PMS_UPSTREAM_ALERT_SCAN": (1, 20000), "PMS_UPSTREAM_ALERT_PER_CAT": (1, 1000), + "PMS_UPSTREAM_ALERT_MAX_AGE_MIN": (0, 1440), # 0=不限龄, 上限 24 小时 } diff --git a/app/services/upstream_signals.py b/app/services/upstream_signals.py index 8f56a25..ab19f82 100644 --- a/app/services/upstream_signals.py +++ b/app/services/upstream_signals.py @@ -46,6 +46,12 @@ def _alert_per_cat() -> int: return max(1, param_store.get_int("PMS_UPSTREAM_ALERT_PER_CAT", 20)) +def _alert_max_age_sec() -> int: + # 告警时间窗(秒): 0=不限龄。只用于给每条打"陈旧"标记(超龄置灰), **不删条目** —— + # 用户拍板"置灰标注"而非隐藏, 风控面板不丢信息。与每类上限正交: 先按龄标 stale, 再每类限量。 + return max(0, param_store.get_int("PMS_UPSTREAM_ALERT_MAX_AGE_MIN", 60)) * 60 + + def _c208(db: int): """208(与 SIGNAL_REDIS 同实例)的只读客户端, 按库缓存。""" key = ("208", db) @@ -138,6 +144,8 @@ _ALERT_META = { def _read_alerts(c, ymd): key = "intraday_alerts:%s" % ymd per_cat = _alert_per_cat() + max_age = _alert_max_age_sec() # 秒; 0=不限龄。超龄不删, 只标 stale 供页面置灰。 + now = time.time() counts = {} # cat -> 已收数量; 每类只留最新 per_cat 条(仍按 newest-first) out = [] for _id, f in c.xrevrange(key, count=_alert_scan()): @@ -157,10 +165,19 @@ def _read_alerts(c, ymd): d = "看跌" if mdir in ("down", "short", "bear") else ("看涨" if mdir in ("up", "long", "bull") else "—") else: d = dir_fixed + # 分钟龄 + 陈旧标记: 时间窗只做"置灰标注", 不删条目(超龄由页面按 stale 置灰)。 + # trigger_time 是 epoch 毫秒; 缺失/非法 → age_min=None 且不判陈旧(风控从宽, 不误灰)。 + try: + age_sec = now - float(f.get("trigger_time")) / 1000.0 + age_min = int(age_sec // 60) + except (TypeError, ValueError): + age_sec, age_min = None, None + stale = bool(max_age) and age_sec is not None and age_sec > max_age counts[cat] = counts.get(cat, 0) + 1 out.append({"ts_code": f.get("ts_code"), "source": src, "direction": d, "cat": cat, "cat_label": cat_label, - "level": f.get("level"), "value": _num(f.get("value")), "time": _hm(f.get("trigger_time"))}) + "level": f.get("level"), "value": _num(f.get("value")), + "time": _hm(f.get("trigger_time")), "age_min": age_min, "stale": stale}) return {"key": key, "items": out} diff --git a/app/web/static/index.html b/app/web/static/index.html index c948636..4508765 100644 --- a/app/web/static/index.html +++ b/app/web/static/index.html @@ -134,6 +134,11 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius: .alert-win .aw-title{font-size:13px;font-weight:640;color:var(--ink-2);} .alert-win .aw-toggle{font-size:11px;font-weight:560;color:var(--accent);cursor:pointer;border:1px solid var(--accent);border-radius:999px;padding:1px 9px;background:var(--accent-weak);white-space:nowrap;} .alert-win .aw-toggle.off{color:var(--muted);border-color:var(--hair);background:var(--surface-2);} +/* 陈旧告警置灰 (超龄不隐藏, 只灰: 风控面板不丢信息)。灰=陈旧, 不动红绿(红绿只留给方向)。 */ +.alert-bar .ab-stale{flex:none;font-size:11px;color:var(--muted);border:1px solid var(--hair);border-radius:999px;padding:0 7px;} +.alert-win .el-table__row.alert-stale{opacity:.5;} +.alert-win .el-table__row.alert-stale:hover{opacity:.72;} +.alert-age{color:var(--muted);font-size:10.5px;margin-left:5px;}
@@ -196,7 +201,8 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius: {{ alertLatest.direction }} {{ alertLatest.cat_label }} {{ alertLatest.level }} - {{ alertLatest.time }} + {{ alertLatest.time }} · {{ ageText(alertLatest) }} + 陈旧 · 折叠条已隐藏当前类型(展开后可切回) @@ -215,12 +221,12 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius: {{ g.label }} · {{ g.items.length }} {{ catShown(g.key) ? '折叠条显示 ✓' : '折叠条隐藏' }} -