diff --git a/DEVLOG.md b/DEVLOG.md
index a080c80..8c5a94c 100644
--- a/DEVLOG.md
+++ b/DEVLOG.md
@@ -30,6 +30,54 @@
---
+## 2026-08-18 · 上游信号面板改版:删下线框 + 按重要度分档 + 资金异动 z 可读化 + 待触发条
+
+**做了什么**
+重排管理页面「上游信号」面板(app/web/static/index.html 单页,纯前端,不动后端)。四件事。
+一,删掉「放量异动」框:该源(volume_capitulation/breakout)6/15 已被资金分布取代,
+生产端(intraday_timing orchestrator 方向源清单)不再发它,框永远只显示历史残留键。
+二,卡片按重要度分三档、大小不一:行动级(决策买卖 c-wide、择时入场、风控卖出、多源升级)
+有数据才出大卡;判读级(资金异动、QRS、资金分布、资金流强度)中卡两列;参考级(股价异动
+小卡、实况分榜宽卡)。用 12 列网格 + c-wide/c-half/c-third 跨列实现。
+三,资金异动「异动z」换成净额领读 + 强度带:净额折成万/亿带正负号打头,强度按 z_dd 绝对值
+分档(显著 2–3 / 强 3–5 / 极强 ≥5)用小方块条 + 中文标签表达,原始 z 收进悬停提示。
+四,空的「在用」信号收进一条「在用·今日待触发」横条(armed 待命,不再各占空框)——这是
+本次核心:此前四个空框和一个死框摆一起,看不出「活着但安静」和「已下线」的区别,用户误以为
+下线了。逐个追到生产端核实:只有放量异动真下线,多源升级/择时层BUY/风控卖出/资金流强度都是
+在用但罕见或条件触发。「其他」桶只在非空时出现(空=所有源已登记=健康)。
+
+**动了哪些文件**
+app/web/static/index.html:新增 CSS(sig-tier/sig-grid/c-*/intens/ibar/armed 等);新增 JS
+(moneyCn、zBand、zBars、dirUp、SIG 组装、armed 待触发、CARDS_B、tierHasA/B/C 计算属性,
+并加入 setup return);替换整个 up-grid 模板为三档 + 待触发条 + 其他桶。旧的 alertGridFixed
+等计算属性留着未删(无害,减少改动面)。
+
+**部署方式**
+factorevaluation 上 `make deploy`(前端资源打进镜像)。纯前端改动,不动后端与取数口径。
+回滚只需还原 index.html 一个文件。
+
+**真机判收**
+开发机已做无头渲染判收(Playwright 加载真页面 + 造数):三档正确渲染、待触发条正确收纳空信号、
+强度带与万/亿格式生效、放量异动消失、无任何 Vue 编译/运行告警;node 语法检查与括号平衡通过;
+全页 Vue 模板编译无错。真机待看:盘中有真数据时各档卡片与截图一致,且级别筛选条仍联动。
+
+**还欠着什么**
+待触发条目前只标「今日待触发」,未显示「最近一次触发时间」(那需要后端跨日扫历史键,会给每次
+刷新加延迟,暂不做;用户认可先不加)。强度分档三个阈值(2–3/3–5/≥5)可按盘中手感再调。
+
+**同日修订(改版二,用户反馈:分档布局太乱、要固定成对)**
+把"按重要度分三档 + 卡片宽度随内容变(c-wide/c-third)"改成**固定成对网格**,位置稳定不再随
+数据重排,整体更对称干净。三对(用户指定)各占一行、两两并排等宽等高:① mtf 资金异动 |
+mtf 实况分榜(这对通常列表长,单独拉高到 min-height 340,两边等高并排);② 决策系统买卖 |
+日QRS对称;③ 资金分布 | 股价异动。这六个常用框**常驻**(空了框内显示"暂无",不再消失/重排)。
+四个稀有或条件触发的信号(风控卖出、多源升级、择时层BUY、资金流强度)归入下方「事件信号」区:
+触发了才出卡,没触发的收进一条「在用·今日待触发」;「其他/未登记源」桶也并进这条、非空才显示。
+去掉了三档标题与 c-wide/c-half/c-third 变宽卡,新增 .sig2(成对行)/.sig2.tall/.sig-ev(事件卡);
+删掉不再用的 tierHasA/B/C、CARDS_B、armed 计算属性。判收:无头渲染六框全在、事件卡与待触发条
+各自正确、零 Vue 告警。旧的"分档版"未曾部署,改版二是最终形态。
+
+---
+
## 2026-08-17 · 清仓择时改造:紧急直通 + 卖出分桶收口 + 自主卖出窗口不作废
**做了什么**
diff --git a/app/web/static/index.html b/app/web/static/index.html
index 6838f38..1e846a0 100644
--- a/app/web/static/index.html
+++ b/app/web/static/index.html
@@ -109,6 +109,38 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
.up-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:16px;}
.up-cell{border:1px solid var(--hair);border-radius:11px;padding:12px 14px;background:var(--surface-2);}
.up-h{font-size:13px;font-weight:640;color:var(--ink-2);margin-bottom:8px;}
+/* 上游信号改版 (2026-08-18): 按重要度分档 + 空的在用信号收进待触发条 */
+.sig-tier{margin-top:16px;}
+.sig-tier-h{display:flex;align-items:baseline;gap:8px;margin:0 0 9px;flex-wrap:wrap;}
+.sig-tier-h .tt{font-size:10px;font-weight:700;padding:1px 7px;border-radius:5px;letter-spacing:.3px;}
+.tt-a{background:var(--stop-weak);color:var(--stop);} .tt-b{background:var(--accent-weak);color:var(--accent);} .tt-c{background:rgba(138,136,130,.14);color:var(--ink-2);}
+.sig-tier-h b{font-size:13px;} .sig-tier-h .d{font-size:11px;color:var(--ink-2);}
+.sig-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:14px;}
+.sig-grid .up-cell{margin:0;}
+.c-wide{grid-column:span 8;} .c-half{grid-column:span 6;} .c-third{grid-column:span 4;}
+@media(max-width:960px){.c-wide,.c-half,.c-third{grid-column:span 12;}}
+.sig-rows{max-height:210px;overflow:auto;}
+.sig-row{display:flex;align-items:center;gap:8px;padding:6px 0;border-top:1px solid var(--hair);font-size:13px;}
+.sig-row:first-child{border-top:none;}
+.sig-nm{font-weight:600;} .sig-sp{flex:1;}
+.intens{display:inline-flex;align-items:center;gap:5px;}
+.ibar{display:inline-flex;gap:2px;}
+.ibar i{width:4px;height:11px;border-radius:1px;background:var(--hair);display:inline-block;}
+.ibar i.on{background:var(--ink-2);}
+.armed{display:flex;align-items:center;gap:9px;flex-wrap:wrap;border:1px dashed var(--hair);border-radius:10px;padding:9px 13px;background:var(--surface-2);}
+.armed .lead{font-size:12px;color:var(--ink-2);font-weight:600;}
+.armed .chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:3px 10px;border-radius:14px;background:var(--surface);border:1px solid var(--hair);}
+.armed .chip .dot{width:6px;height:6px;border-radius:50%;background:#c9c7c0;}
+.armed .chip.dead{text-decoration:line-through;opacity:.55;}
+/* 固定成对网格 (2026-08-18 改版二): 三对各占一行, 位置稳定不重排; mtf 一对并排拉高 */
+.sig2{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px;align-items:stretch;}
+@media(max-width:880px){.sig2{grid-template-columns:1fr;}}
+.sig2 .up-cell{min-height:200px;display:flex;flex-direction:column;margin:0;}
+.sig2.tall .up-cell{min-height:340px;}
+.sig2 .sig-rows{flex:1;max-height:none;}
+.sig-ev{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px;margin-top:14px;}
+.sig-ev .up-cell{margin:0;}
+.sig-none{color:var(--ink-2);padding:10px 0;font-size:12.5px;}
.up-h .src-err{color:var(--stop);font-weight:400;font-size:11.5px;}
.mr-two{display:grid;grid-template-columns:1fr 1fr;gap:16px;font-size:12.5px;}
.mr-row{padding:3px 0;border-bottom:1px solid var(--hair);}
@@ -231,72 +263,156 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
{{ L.label }} {{ levelCount(L.key) }}
点一下切换显隐,只前端过滤、不查后端
-
-
-
{{ g.label }} · {{ g.items.length }}
-
- {{ nm(s.row.ts_code) }} {{ s.row.ts_code }}
- {{ s.row.direction }}—
- {{ lvLabel(s.row.level) }}
- {{ s.row.time }}{{ ageText(s.row) }}
-
-
-
+
+
+
-
决策系统 买/卖 · 读取失败:{{ usrc('intraday').error }}
-
- {{ nm(s.row.ts_code) }} {{ s.row.ts_code }}
- {{ s.row.action }} · {{ s.row.signal_type }}
- {{ s.row.time }}
-
+
mtf 资金异动 · {{ SIG.metrics.length }} · 读取失败
+
+
+ {{ nm(r.ts_code) }}{{ r.ts_code }}
+ {{ flowText(r.direction) }}
+
+ {{ moneyCn(r.window_net) }}
+ {{ zBand(r.z_dd).label }}
+
+
暂无资金异动
+
+
强度=横截面异动 z_dd:显著(2–3) · 强(3–5) · 极强(≥5)。悬停看原始 z。
-
-
-
盘中择时层 · BUY 入场 · 读取失败
-
- {{ nm(s.row.ts_code) }} {{ s.row.ts_code }}
- {{ s.row.price==null?'—':s.row.price }}
- {{ s.row.confidence==null?'—':s.row.confidence }}
- {{ s.row.time }}
-
-
-
-
-
mtf 资金异动 · 读取失败
-
- {{ nm(s.row.ts_code) }} {{ s.row.ts_code }}
- {{ flowText(s.row.direction) }}—
- {{ s.row.z_dd==null?'—':s.row.z_dd }}
- {{ s.row.window_net==null?'—':money(s.row.window_net) }}
-
-
-
-
-
决策系统 · 风控卖出 · 读取失败
-
- {{ nm(s.row.ts_code) }}
- {{ s.row.confidence==null?'—':s.row.confidence }}
- {{ s.row.reason || '—' }}
- {{ s.row.time }}
-
-
-
mtf 实况分 · 关注 / 回避榜 · 读取失败
-
+
关注(高分)
-
{{ nm(r.ts_code) }} {{ r.ts_code }}{{ r.score }}
-
暂无
+
{{ nm(r.ts_code) }} {{ r.ts_code }}{{ r.score }}
+
暂无
回避(低分)
-
{{ nm(r.ts_code) }} {{ r.ts_code }}{{ r.score }}
-
暂无
+
{{ nm(r.ts_code) }} {{ r.ts_code }}{{ r.score }}
+
暂无
+
+
+
+
+
决策系统 买/卖 · {{ SIG.decision.length }} · 读取失败
+
+
+ {{ nm(r.ts_code) }}{{ r.ts_code }}
+
+ {{ r.action }} · {{ r.signal_type }}
+ {{ r.time }}
+
+
暂无买卖广播
+
+
+
+
日QRS对称 · {{ SIG.qrs.length }}
+
+
+ {{ nm(r.ts_code) }}{{ r.ts_code }}
+ {{ r.direction }}
+
+ {{ lvLabel(r.level) }}
+ {{ r.time }}{{ ageText(r) }}
+
+
暂无 QRS 告警
+
+
+
+
+
+
+
+
资金分布 · {{ SIG.capital.length }}
+
+
+ {{ nm(r.ts_code) }}{{ r.ts_code }}
+ {{ r.direction }}
+
+ {{ lvLabel(r.level) }}
+ {{ r.time }}{{ ageText(r) }}
+
+
暂无资金分布告警
+
+
+
+
股价异动 · {{ SIG.price_notice.length }}
+
+
+ {{ nm(r.ts_code) }}{{ r.ts_code }}
+ {{ r.direction }}
+
+ {{ lvLabel(r.level) }}
+ {{ r.time }}{{ ageText(r) }}
+
+
暂无股价异动
+
+
+
+
+
+
+
+
决策系统 · 风控卖出 · {{ SIG.sell.length }}
+
+
+
{{ nm(r.ts_code) }}
+
SELL {{ r.confidence==null?'':r.confidence }}
+
+
{{ r.time }}
+
{{ r.reason }}
+
+
+
+
+
多源升级 · {{ SIG.multi.length }}
+
+
+ {{ nm(r.ts_code) }}
+ {{ r.direction }}
+
+ {{ lvLabel(r.level) }}
+ {{ r.time }}
+
+
+
+
+
盘中择时层 · BUY 入场 · {{ SIG.timing.length }}
+
+
+ {{ nm(r.ts_code) }}
+
+ 参考价 {{ r.price==null?'—':r.price }}
+ 置信 {{ r.confidence==null?'—':r.confidence }}
+ {{ r.time }}
+
+
+
+
+
资金流强度 · {{ SIG.money_flow.length }}
+
+
+ {{ nm(r.ts_code) }}
+ {{ r.direction }}
+
+ {{ lvLabel(r.level) }}
+ {{ r.time }}
+
+
+
+
+
+
+
+ 在用 · 今日待触发:
+ {{ e.label }}
+ 其他/未登记源 · {{ SIG.other.length }}
@@ -1870,6 +1986,47 @@ createApp({
const lvClass = v => v === 'DANGER' ? 'deny' : ((v === 'WARNING' || v === 'IMPORTANT') ? 'queue' : 'wait');
const flowText = d => d === 'inflow' ? '流入' : (d === 'outflow' ? '流出' : (d || '—'));
const flowClass = d => d === 'inflow' ? 'clr-up' : (d === 'outflow' ? 'clr-down' : 'muted');
+ // ── 上游信号改版 (2026-08-18): 净额领读 + 强度带 + 按重要度分档 + 待触发条 ──
+ // 净额折成万/亿 (带正负号); 原始 z 值移进悬浮提示, 正文不堆数字。
+ const moneyCn = v => {
+ if (v === null || v === undefined || v === '') return '—';
+ const n = Number(v); if (!isFinite(n)) return '—';
+ const s = n < 0 ? '-' : '+', a = Math.abs(n);
+ if (a >= 1e8) return s + (a / 1e8).toFixed(2) + '亿';
+ if (a >= 1e4) return s + Math.round(a / 1e4) + '万';
+ return s + Math.round(a);
+ };
+ // 强度带: 横截面异动 z_dd 绝对值分档。显著(2–3) / 强(3–5) / 极强(≥5)。
+ const zBand = z => {
+ const a = Math.abs(Number(z) || 0);
+ if (a >= 5) return { label: '极强', on: 4 };
+ if (a >= 3) return { label: '强', on: 3 };
+ if (a >= 2) return { label: '显著', on: 2 };
+ return { label: '一般', on: 1 };
+ };
+ const zBars = z => { const on = zBand(z).on; return [1, 2, 3, 4].map(i => i <= on); };
+ const dirUp = d => ['看涨', '涨', 'inflow', '流入'].includes(d);
+ // 一处组装: 各信号当前条目 (告警按级别筛选后的口径, 与筛选条联动)。
+ // 已下线的 volume(放量异动) 不再单列 —— 若流里还有历史残留键, 归入 other。
+ const SIG = computed(() => {
+ const up = upstream.value.sources || {};
+ const by = {}; for (const a of alertItemsF.value) (by[a.cat] || (by[a.cat] = [])).push(a);
+ const KNOWN = new Set(['money_flow', 'capital', 'qrs', 'multi', 'price_notice']);
+ const other = []; Object.keys(by).forEach(k => { if (!KNOWN.has(k)) other.push(...(by[k] || [])); });
+ return {
+ decision: ((up.intraday || {}).decision) || [], timing: ((up.intraday || {}).timing) || [],
+ sell: ((up.sell_actions || {}).items) || [], multi: by.multi || [],
+ metrics: ((up.metrics || {}).items) || [], qrs: by.qrs || [], capital: by.capital || [],
+ money_flow: by.money_flow || [], price_notice: by.price_notice || [],
+ mr: up.mr || { watch: [], avoid: [] }, other,
+ };
+ });
+ // 改版二 (2026-08-18): 六个常用框固定成对常驻; 四个稀有/条件触发信号进"事件"区,
+ // 触发了出卡、没触发的收进一条待触发条。money_flow(资金流强度)频繁为空, 归入事件区。
+ const EVENT_SIGS = [
+ { key: 'sell', label: '决策系统 风控卖出' }, { key: 'multi', label: '多源升级' },
+ { key: 'timing', label: '盘中择时层 BUY 入场' }, { key: 'money_flow', label: '资金流强度' }];
+ const eventsQuiet = computed(() => EVENT_SIGS.filter(e => !SIG.value[e.key].length));
// 告警固定分类格子: 6 类常驻 + 其他(catch-all), 永远这几个框、空的显示暂无 —— 不再随数据冒出冒消。
const ALERT_CATS_FIXED = [
{ key:'money_flow', label:'资金流强度' }, { key:'capital', label:'资金分布' }, { key:'qrs', label:'日QRS对称' },
@@ -2194,7 +2351,8 @@ createApp({
upstream, showUpstream, usrc, loadUpstream,
showAlerts, alertItems, alertLatest, alertLevelClass, alertGroups, alertStripItems,
alertStripCount, alertTotalCount, catShown, toggleCat, ageText, alertRowClass,
- alertItemsF, levelShown, toggleLevel, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel, flowText, flowClass };
+ alertItemsF, levelShown, toggleLevel, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel, flowText, flowClass,
+ SIG, moneyCn, zBand, zBars, dirUp, eventsQuiet };
}
}).use(ElementPlus).mount('#app');