From aa053b5b2264b66f259b0b49c449937ba8145b1b Mon Sep 17 00:00:00 2001 From: xxm Date: Mon, 17 Aug 2026 02:13:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AD=96=E7=95=A5=E8=A1=A8=E8=A1=A8?= =?UTF-8?q?=E5=A4=B4=E5=8A=A0=E2=93=98=C2=A7=E6=8C=89=E9=92=AE=E2=80=94?= =?UTF-8?q?=E2=80=94=E2=93=98=E6=98=BE=E7=A4=BA=E8=AF=A5=E5=88=97=E5=90=AB?= =?UTF-8?q?=E4=B9=89(columns=20spec),=C2=A7=E6=98=BE=E7=A4=BA=E6=A8=A1?= =?UTF-8?q?=E5=9D=97spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/index.html | 77 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/static/index.html b/static/index.html index 41c73943..deafa8b4 100644 --- a/static/index.html +++ b/static/index.html @@ -32,6 +32,9 @@ body { background: #0f0f13; color: #e2e8f0; } .spec-btn.ai { color: #d29922; background: rgba(210,153,34,0.1); } .spec-btn.ai:hover { background: rgba(210,153,34,0.25); } .spec-btns { display: inline-flex; gap: 4px; } +.spec-btn.colinfo { color: #58a6ff; background: rgba(88,166,255,0.1); } +.spec-btn.colinfo:hover { background: rgba(88,166,255,0.25); } +.spec-th-help { margin-left: 3px; display: inline-flex; gap: 3px; vertical-align: middle; } ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #0f0f13; } ::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; } @@ -1198,6 +1201,39 @@ function closeSpecModal() { if (el) { el.classList.add('hidden'); el.classList.remove('flex'); } } +function showColumnHelp(colKey) { + // 显示策略表某列的含义说明(数据源:specs/strategy.json human_help.columns[colKey]) + const cacheKey = 'strategy:human'; + const cached = _specCache[cacheKey]; + const modal = document.getElementById('specModal'); + modal.classList.remove('hidden'); + modal.classList.add('flex'); + document.getElementById('specModalTitle').textContent = '加载中...'; + document.getElementById('specModalSubtitle').textContent = ''; + document.getElementById('specModalBody').innerHTML = '
正在获取列说明...
'; + const render = function(spec) { + const cols = (spec && spec.human_help && spec.human_help.columns) || {}; + const col = cols[colKey]; + if (!col) { + document.getElementById('specModalBody').innerHTML = `
未找到列说明: ${colKey}
`; + return; + } + document.getElementById('specModalTitle').textContent = col.label || colKey; + document.getElementById('specModalSubtitle').textContent = '列说明 · 这列是干嘛的'; + let html = '
这列是什么

' + (col.desc || '—') + '

'; + if (col.see) html += '
怎么读 / 注意

' + col.see + '

'; + html += '
来源:specs/strategy.json → human_help.columns.' + colKey + '(SSOT)
'; + document.getElementById('specModalBody').innerHTML = html; + }; + if (cached) { render(cached); } + else { + fetchJSON('/api/module-spec/strategy').then(spec => { + if (spec && typeof spec === 'object') { _specCache[cacheKey] = spec; render(spec); } + else document.getElementById('specModalBody').innerHTML = '
无规范数据
'; + }); + } +} + function showModuleHelp(module, type) { const cacheKey = module + ':' + type; const cached = _specCache[cacheKey]; @@ -2469,9 +2505,10 @@ function renderStrategyTable(strategies) { const sty = bar(c, v, invert); return `${v != null ? fmt(v) : '—'}`; }; - const th = (key, label, cls, title) => { + const th = (key, label, cls, title, colKey) => { const arrow = window._sortKey === key ? (window._sortDir === 'desc' ? '▼' : '▲') : ''; - return `${label}${arrow ? ' ' + arrow : ''}`; + const helpBtns = colKey ? '§' : ''; + return `${label}${helpBtns}${arrow ? ' ' + arrow : ''}`; }; // 2026-08-13 温区自适应:按"适应温区"展开——每策略每温区一行 // 当前 = 当前温区激活(后端 s.current 已改为 regime_weights.active 判断);历史 = 其余,默认折叠 @@ -2479,19 +2516,19 @@ function renderStrategyTable(strategies) { const renderGroup = (groupStrats, groupTitle, groupIcon, collapsed) => { let ghtml = '
' + '' + - th('version', '版本', 'text-left') + th('name', '名称', 'text-left') + + th('version', '版本', 'text-left', '策略版本标识', 'version') + th('name', '名称', 'text-left', '策略人话名称', 'name') + th('sizing_slots', '仓位', 'text-center') + '' + - th('composite', '综合', 'text-right') + - th('universality', '普适', 'text-right') + - th('full_return', '全参与', 'text-right') + - th('capital_final', '最终资产', 'text-right') + - th('cagr', '年化', 'text-right') + - th('trades', '执行数', 'text-right', '实际参与组合模拟的交易笔数;信号总数=为鼠标悬停查看') + - th('hold', '均持仓', 'text-right') + - th('win_rate', '胜率', 'text-right') + - th('avg_profit', '平均收益', 'text-right') + th('sharpe', '夏普', 'text-right') + - th('profit_factor', '盈亏比', 'text-right') + th('max_dd', '资产回撤', 'text-right') + + th('composite', '综合', 'text-right', '多维度综合评分', 'composite') + + th('universality', '普适', 'text-right', '跨独立年份有效性(非特例)', 'universality') + + th('full_return', '全参与', 'text-right', '全参与模拟总收益', 'full_return') + + th('capital_final', '最终资产', 'text-right', '组合模拟期末资产', 'capital_final') + + th('cagr', '年化', 'text-right', '年化收益率', 'cagr') + + th('trades', '执行数', 'text-right', '实际参与组合模拟的交易笔数;信号总数=为鼠标悬停查看', 'trades') + + th('hold', '均持仓', 'text-right', '平均持仓天数', 'hold') + + th('win_rate', '胜率', 'text-right', '盈利笔数占比', 'win_rate') + + th('avg_profit', '平均收益', 'text-right', '每笔平均收益率', 'avg_profit') + th('sharpe', '夏普', 'text-right', '每单位波动超额收益', 'sharpe') + + th('profit_factor', '盈亏比', 'text-right', '总盈利/总亏损', 'profit_factor') + th('max_dd', '资产回撤', 'text-right', '组合模拟最大回撤', 'max_dd') + ''; for (const s of groupStrats) { const st = s.summary_stats || {}; @@ -2701,13 +2738,13 @@ function renderStrategyTable(strategies) { '' + '' + '
适应温区说明操作
' + - th('version', '版本', 'text-left') + th('name', '名称', 'text-left') + - '' + - '' + - th('win_rate', '胜率', 'text-right') + th('avg_profit', '平均收益', 'text-right') + th('trades', '成交/信号', 'text-right', '实际入场笔数 / 触发信号数(组合模拟10等分仓位下实际成交)') + - th('hold', '均持仓', 'text-right') + th('cagr', '年化', 'text-right') + th('composite', '综合', 'text-right') + - th('sharpe', '夏普', 'text-right') + th('profit_factor', '盈亏比', 'text-right') + - th('universality', '普适', 'text-right') + th('max_dd', '回撤', 'text-right') + + th('version', '版本', 'text-left', '策略版本标识', 'version') + th('name', '名称', 'text-left', '策略人话名称', 'name') + + '' + + '' + + th('win_rate', '胜率', 'text-right', '盈利笔数占比', 'win_rate') + th('avg_profit', '平均收益', 'text-right', '每笔平均收益率', 'avg_profit') + th('trades', '成交/信号', 'text-right', '实际入场笔数 / 触发信号数(组合模拟10等分仓位下实际成交)', 'trades') + + th('hold', '均持仓', 'text-right', '平均持仓天数', 'hold') + th('cagr', '年化', 'text-right', '年化收益率', 'cagr') + th('composite', '综合', 'text-right', '多维度综合评分', 'composite') + + th('sharpe', '夏普', 'text-right', '每单位波动超额收益', 'sharpe') + th('profit_factor', '盈亏比', 'text-right', '总盈利/总亏损', 'profit_factor') + + th('universality', '普适', 'text-right', '跨独立年份有效性(非特例)', 'universality') + th('max_dd', '回撤', 'text-right', '组合模拟最大回撤', 'max_dd') + '' + metas.map(s => renderRegimeRow(s, rg)).join('') + '
资格可用资格§可用§操作
';