feat: 三维共振层落地(技术×资金×消息合成+signal_veto_log全程记录) + 综合评分列(收益30/胜率20/夏普20/盈亏比15/回撤15) + 普适性列(月份分布熵)

This commit is contained in:
hmo
2026-07-29 03:23:25 +08:00
parent 6ee7d348ce
commit c9e7079ee6
4 changed files with 212 additions and 2 deletions
+17 -2
View File
@@ -1969,6 +1969,17 @@ function renderStrategyTable(strategies) {
if (pf.cagr_pct != null) best.cagr_pct = Math.max(best.cagr_pct, pf.cagr_pct);
if (pf.portfolio_max_dd_pct != null) best.portfolio_max_dd_pct = Math.min(best.portfolio_max_dd_pct, pf.portfolio_max_dd_pct);
}
// 综合评分:总收益30% + 胜率20% + 夏普20% + 盈亏比15% + 资产回撤15%(反向)
for (const s of strategies) {
const st = s.summary_stats || {}; const pf = st.portfolio || {};
if (st.total_trades == null) { s._composite = null; continue; }
const ret = Math.min(pf.total_return_pct || 0, 100) / 100 * 30;
const wr = (st.win_rate || 0) / 100 * 20;
const sh = Math.min(Math.max(st.sharpe_ratio || 0, 0), 20) / 20 * 20;
const pfc = Math.min(st.profit_factor || 0, 5) / 5 * 15;
const dd = (1 - Math.min(pf.portfolio_max_dd_pct || 0, 50) / 50) * 15;
s._composite = Math.round(ret + wr + sh + pfc + dd);
}
const hl = (val, bestVal, invert) => {
if (val == null) return '';
const isBest = invert ? (val === bestVal && bestVal !== 999) : (val === bestVal && bestVal !== -999);
@@ -1977,12 +1988,12 @@ function renderStrategyTable(strategies) {
// 条件格式色条:计算每列 min/max,单元格背景按相对大小画色条
const CURRENT_STRATEGY = 'v7.1';
const ranges = {};
const cols = ['total_return_pct','capital_final','cagr_pct','total_trades','avg_hold_days','win_rate','avg_profit_pct','sharpe_ratio','profit_factor','portfolio_max_dd_pct'];
const cols = ['composite','universality_score','total_return_pct','capital_final','cagr_pct','total_trades','avg_hold_days','win_rate','avg_profit_pct','sharpe_ratio','profit_factor','portfolio_max_dd_pct'];
for (const c of cols) {
let mn = Infinity, mx = -Infinity;
for (const s of strategies) {
const st = s.summary_stats || {}; const pf = st.portfolio || {};
const v = (c in pf) ? pf[c] : st[c];
const v = c === 'composite' ? s._composite : (c === 'universality_score' ? (st.universality || {}).score : ((c in pf) ? pf[c] : st[c]));
if (v != null) { mn = Math.min(mn, v); mx = Math.max(mx, v); }
}
ranges[c] = { mn: mn === Infinity ? 0 : mn, mx: mx === -Infinity ? 1 : mx };
@@ -2004,6 +2015,8 @@ function renderStrategyTable(strategies) {
};
let html = '<div class="overflow-x-auto"><table class="w-full text-xs whitespace-nowrap"><thead><tr class="text-slate-500 border-b border-slate-700">' +
'<th class="text-left px-2 py-1.5">版本</th><th class="text-left px-2 py-1.5">名称</th>' +
'<th class="text-right px-2 py-1.5 text-amber-300">综合</th>' +
'<th class="text-right px-2 py-1.5">普适</th>' +
'<th class="text-right px-2 py-1.5 text-amber-400">总收益</th>' +
'<th class="text-right px-2 py-1.5 text-amber-400">最终资产</th>' +
'<th class="text-right px-2 py-1.5">年化</th>' +
@@ -2024,6 +2037,8 @@ function renderStrategyTable(strategies) {
html += '<tr class="' + rowCls + '" onclick="showStrategyDetail(\'' + s.version + '\')">' +
'<td class="px-2 py-1.5 font-mono font-bold ' + (isCurrent ? 'text-emerald-400' : 'text-blue-400') + '">' + s.version + (isCurrent ? ' <span class="text-[10px] bg-emerald-500/20 text-emerald-300 px-1 rounded">当前</span>' : '') + '</td>' +
'<td class="px-2 py-1.5" title="' + (s.hypothesis || s.summary || '').replace(/"/g, '&quot;') + '">' + (s.name || '') + (smallSample ? ' <span class="text-amber-500" title="样本<40笔">⚠️</span>' : '') + '</td>' +
cell('composite', s._composite, v => v, 'font-bold text-amber-300') +
cell('universality_score', (st.universality || {}).score, v => v + '<span class="text-slate-500">/' + (st.universality || {}).months + '月</span>') +
cell('total_return_pct', pf.total_return_pct, v => v + '%', 'font-bold ' + retCls + ' ' + hl(pf.total_return_pct, best.total_return_pct)) +
cell('capital_final', pf.capital_final, v => '¥' + (v/10000).toFixed(0) + '万') +
cell('cagr_pct', pf.cagr_pct, v => v + '%', hl(pf.cagr_pct, best.cagr_pct)) +