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:
@@ -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>`;
|
||||
|
||||
Reference in New Issue
Block a user