diff --git a/server.py b/server.py
index e5d5b5fa..6d38c543 100644
--- a/server.py
+++ b/server.py
@@ -736,7 +736,8 @@ def api_research_strategies():
_entry['state_reason'] = '当前温区路由激活'
_entry['note'] = f"({pt or '当前周期'}无回测记录,显示10y数据)"
strats.append(_entry)
- return jsonify({'strategies': strats, 'regime_active': sorted(_active_set)})
+ return jsonify({'strategies': strats, 'regime_active': sorted(_active_set),
+ 'regime_state': _weights.get('state', 'unknown')})
except Exception as e:
return jsonify({'error': str(e)}), 500
diff --git a/static/index.html b/static/index.html
index 7f5420e8..f6a3bf2f 100644
--- a/static/index.html
+++ b/static/index.html
@@ -2179,6 +2179,7 @@ async function loadStrategyList() {
if (data.error) { listEl.innerHTML = '
' + data.error + '
'; return; }
// 2026-08-13 温区激活策略集合(供策略表高亮)
window._regimeActive = data.regime_active || [];
+ window._regimeState = data.regime_state || 'unknown';
const mkt = document.getElementById('btMarket')?.value || 'all';
let strats = data.strategies || [];
// 市场语义:'all'=未分市场的A股回测(港股回填前跑的),'hk'=港股宇宙,'a'=显式A股
@@ -2496,12 +2497,74 @@ function renderStrategyTable(strategies) {
ghtml += '';
return ghtml;
};
+ // ── 2026-08-15 按温区分组:当前温区激活区块改为温区优先(当前温区排最前高亮),
+ // 每个温区下列出所有在该温区有数据的策略(按该温区胜率降序)──
+ const REGIME_ORDER = ['trend_up', 'choppy', 'trend_down'];
+ const renderRegimeRow = (s, rg, isCur) => {
+ const rw = (s.regime_winrates || {})[rg] || {};
+ const rwPf = rw.portfolio || null;
+ const wr = rw.win_rate != null ? rw.win_rate : null;
+ const pnl = rw.avg_pnl != null ? rw.avg_pnl : null;
+ const tr = rw.trades != null ? rw.trades : null;
+ const smallSample = tr != null && tr < 40;
+ let comp = null;
+ if (rwPf) {
+ const ret = Math.min(rwPf.total_return_pct || 0, 100) / 100 * 30;
+ const w = (wr || 0) / 100 * 20;
+ const sh = Math.min(Math.max((rwPf.sharpe_ratio || 0), 0), 20) / 20 * 20;
+ const pfc = Math.min((rwPf.profit_factor || 0), 5) / 5 * 15;
+ const dd = (1 - Math.min(rwPf.portfolio_max_dd_pct || 0, 50) / 50) * 15;
+ comp = Math.round((ret + w + sh + pfc + dd) * (s._confidence || 1));
+ }
+ const cagr = rwPf ? rwPf.cagr_pct : null;
+ const ddV = rwPf ? rwPf.portfolio_max_dd_pct : null;
+ const univ = rw.universality ? rw.universality.score : null;
+ const univM = rw.universality ? (rw.universality.months + '/' + (rw.universality.regime_total_months || '?')) : null;
+ return '' +
+ '| ' + s.version + (isCur ? ' 激活' : '') + ((s.market && s.market !== 'all') ? ' ' + (s.market === 'hk' ? '港' : 'A') + '' : '') + ' | ' +
+ '' + (s.name || '') + (smallSample ? ' ⚠️' : '') + ' | ' +
+ cell('win_rate', wr, v => v + '%', wr != null && wr >= 50 ? 'text-green-400' : 'text-red-400') +
+ cell('avg_profit_pct', pnl, v => v + '%', (pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400') +
+ cell('total_trades', tr, v => v, '', false) +
+ cell('avg_hold_days', rw.avg_hold_days != null ? rw.avg_hold_days : null, v => v + 'd') +
+ cell('cagr_pct', cagr, v => v + '%', 'font-bold text-amber-300') +
+ cell('composite', comp, v => v, 'font-bold text-amber-300') +
+ cell('universality_score', univ, v => v + '/' + (univM || '?') + '月') +
+ cell('portfolio_max_dd_pct', ddV, v => v + '%', '') +
+ ' |
';
+ };
+ const renderGroupByRegime = (strats, currentRegime) => {
+ const order = [currentRegime, ...REGIME_ORDER.filter(r => r !== currentRegime)];
+ let g = '';
+ for (const rg of order) {
+ const metas = strats
+ .filter(s => (s.regime_winrates || {})[rg] && (s.regime_winrates || {})[rg].trades != null)
+ .sort((x, y) => ((y.regime_winrates || {})[rg].win_rate || -1) - ((x.regime_winrates || {})[rg].win_rate || -1));
+ if (!metas.length) continue;
+ const isCur = rg === currentRegime;
+ const meta = REGIME_META[rg] || {icon: '🌐', name: rg};
+ g += '' +
+ '
' +
+ meta.icon + ' ' + meta.name + (isCur ? ' 当前温区' : '') +
+ '(' + metas.length + '个策略)' +
+ '' + (isCur ? '当前温区匹配即激活' : '该温区下有数据(参考)') + '
' +
+ '
' +
+ th('version', '版本', 'text-left') + th('name', '名称', 'text-left') +
+ th('win_rate', '胜率', 'text-right') + th('avg_profit', '平均收益', 'text-right') + th('trades', '笔数', 'text-right') +
+ th('hold', '均持仓', 'text-right') + th('cagr', '年化', 'text-right') + th('composite', '综合', 'text-right') +
+ th('universality', '普适', 'text-right') + th('max_dd', '回撤', 'text-right') +
+ '| 操作 |
' +
+ metas.map(s => renderRegimeRow(s, rg, isCur)).join('') +
+ '
';
+ }
+ return g;
+ };
// 当前区域(展开)+ 历史区域(默认折叠)
let html = '';
if (currentStrats.length) {
html += '' +
- '
🟢 当前温区激活(' + currentStrats.length + '个)
' +
- renderGroup(currentStrats, '当前', '🟢', false) +
+ '
🟢 当前温区激活(' + currentStrats.length + '个) · 按温区分组 · 当前温区排最前高亮
' +
+ renderGroupByRegime(currentStrats, window._regimeState) +
'
';
}
if (availableStrats.length) {