feat(hub): show key/trend/roll counts on monitor cards

Add per-card strategy stats chips for breakout, fib, and watch key levels plus trend and roll plan counts when non-zero.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-03 23:25:43 +08:00
parent 7957a62c65
commit 98038b1945
3 changed files with 102 additions and 2 deletions
+33
View File
@@ -1280,6 +1280,39 @@ body.market-chart-fs-open {
border-bottom: 1px dashed var(--border-soft);
}
.card-strategy-stats {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin: 10px 0 4px;
padding-top: 8px;
border-top: 1px dashed var(--border-soft);
}
.card-stat-chip {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 6px;
font-size: 11px;
line-height: 1.3;
color: var(--accent);
background: var(--accent-dim);
border: 1px solid var(--border-soft);
}
.hub-tile .card-strategy-stats {
margin: 4px 0 0;
padding-top: 6px;
border-top: none;
gap: 4px;
}
.hub-tile .card-stat-chip {
font-size: 10px;
padding: 2px 6px;
}
.pos-action-group {
display: inline-flex;
flex-direction: row;
+67
View File
@@ -1606,6 +1606,68 @@
</div>`;
}
const KEY_BUCKET_FIB_TYPES = new Set([
"斐波回调0.618",
"斐波回调0.786",
"关键位斐波0.618",
"关键位斐波0.786",
]);
const KEY_BUCKET_BREAKOUT_TYPES = new Set([
"箱体突破",
"收敛突破",
"关键位箱体突破",
"关键位收敛突破",
"关键位收敛结构",
]);
const KEY_BUCKET_WATCH_TYPES = new Set([
"关键阻力位",
"关键支撑位",
"关键位监控",
]);
function classifyKeyMonitorBucket(monitorType) {
const t = String(monitorType || "").trim();
if (!t) return "watch";
if (KEY_BUCKET_FIB_TYPES.has(t) || /斐波/.test(t)) return "fib";
if (KEY_BUCKET_BREAKOUT_TYPES.has(t) || /突破/.test(t)) return "breakout";
if (KEY_BUCKET_WATCH_TYPES.has(t) || /阻力|支撑/.test(t)) return "watch";
return "watch";
}
function countKeyMonitorsByBucket(keys) {
const counts = { breakout: 0, fib: 0, watch: 0 };
(keys || []).forEach((k) => {
if (!k || typeof k !== "object") return;
const bucket = classifyKeyMonitorBucket(k.monitor_type || k.type);
if (bucket === "breakout") counts.breakout += 1;
else if (bucket === "fib") counts.fib += 1;
else counts.watch += 1;
});
return counts;
}
function renderCardStrategyStats(row, hm, flaskOk) {
if (!flaskOk || !hm || typeof hm !== "object") return "";
const caps = row.capabilities || [];
const chips = [];
if (caps.includes("key")) {
const kc = countKeyMonitorsByBucket(hm.keys || []);
if (kc.breakout > 0) chips.push(`突破 ${kc.breakout}`);
if (kc.fib > 0) chips.push(`斐波 ${kc.fib}`);
if (kc.watch > 0) chips.push(`监控 ${kc.watch}`);
}
if (caps.includes("trend")) {
const trendN = Array.isArray(hm.trends) ? hm.trends.length : 0;
if (trendN > 0) chips.push(`趋势回调 ${trendN}`);
}
const rollN = Array.isArray(hm.rolls) ? hm.rolls.length : 0;
if (rollN > 0) chips.push(`顺势加仓 ${rollN}`);
if (!chips.length) return "";
return `<div class="card-strategy-stats">${chips
.map((label) => `<span class="card-stat-chip">${esc(label)}</span>`)
.join("")}</div>`;
}
function renderGridPositionsTable(exchangeId, exchangeKey, positions, orders, trends, tickMap) {
const rows = positions
.map((p) =>
@@ -1646,6 +1708,7 @@
} else {
inner += '<div class="empty-hint">无持仓</div>';
}
inner += renderCardStrategyStats(row, hm, flaskOk);
inner += `<div class="card-expand-hint">点击标题栏进入全屏 · 委托 / 关键位 / 下单监控 / 趋势回调 / 顺势加仓</div>`;
return inner;
}
@@ -1925,6 +1988,9 @@
const tsShort = ts ? ts.slice(-8) : "—";
const posLine =
openCount > 0 ? `${openCount}仓 · ${alert.summary}` : alert.summary;
const hm = row.hub_monitor || {};
const flaskOk = row.flask_ok !== false && hm.ok !== false;
const strategyStats = renderCardStrategyStats(row, hm, flaskOk);
return `<div class="card hub-tile ${tileCls}" data-ex-id="${esc(row.id)}">
<div class="hub-tile-body card-expand-zone" title="点击进入全屏详情">
<div class="hub-tile-top">
@@ -1933,6 +1999,7 @@
</div>
<div class="hub-tile-pnl ${pnlCls(upnl)}">${fmt(upnl, 2)} <small>U</small></div>
<div class="hub-tile-meta">${esc(posLine)}</div>
${strategyStats}
<div class="hub-tile-foot">UPD ${esc(tsShort)}</div>
</div>
</div>`;
+2 -2
View File
@@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
<link rel="stylesheet" href="/assets/app.css?v=20260604-hub-mobile-tiles" />
<link rel="stylesheet" href="/assets/app.css?v=20260604-hub-card-stats" />
</head>
<body>
<div class="app-bg" aria-hidden="true"></div>
@@ -246,6 +246,6 @@
<div id="toast"></div>
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart.js?v=20260603-hub-binance-tick"></script>
<script src="/assets/app.js?v=20260604-hub-pos-table"></script>
<script src="/assets/app.js?v=20260604-hub-card-stats"></script>
</body>
</html>