feat: 研究Tab两个子Tab加温区横幅——策略子Tab(顶部当前市场温区+激活策略) + 组合子Tab(Section1.5温区自适应+激活/观察策略列表), 数据源/api/overview regime_weights
This commit is contained in:
@@ -2117,6 +2117,7 @@ function renderResearch() {
|
||||
'</div>' +
|
||||
'<div id="rsub-strategy">' +
|
||||
'<div id="evo-box" class="mb-3 bg-slate-800/60 border border-slate-700 rounded-lg p-3"><div class="text-xs text-slate-500">🧬 进化模块加载中...</div></div>' +
|
||||
'<div id="strategyRegimeBanner" class="mb-3"></div>' +
|
||||
'<div class="flex gap-2 items-center mb-3 flex-wrap">' +
|
||||
'<h2 class="text-lg font-bold">📋 策略研究 — 版本迭代对比</h2>' +
|
||||
'<select id="btPeriod" onchange="saveBtPeriod()" class="bg-slate-800 text-sm rounded-lg px-2 py-1 border border-slate-700">' +
|
||||
@@ -2224,6 +2225,45 @@ async function loadEvolutionBox() {
|
||||
// 研究Tab活跃时加载进化模块
|
||||
new MutationObserver(()=>{if(!document.getElementById('tab-research')?.classList?.contains('hidden'))loadEvolutionBox();}).observe(document.body,{attributes:true,subtree:true});
|
||||
|
||||
// ── 策略子Tab 温区横幅(2026-08-13)──
|
||||
async function loadStrategyRegimeBanner() {
|
||||
const box = document.getElementById('strategyRegimeBanner');
|
||||
if (!box) return;
|
||||
try {
|
||||
const d = await fetchJSON('/api/overview');
|
||||
const rw = d.regime_weights || null;
|
||||
if (!rw || !rw.state) { box.innerHTML = ''; return; }
|
||||
const regIcon = {trend_up:'📈', choppy:'〰️', trend_down:'📉'}[rw.state] || '🌐';
|
||||
const regName = {trend_up:'趋势市', choppy:'震荡市', trend_down:'下跌市'}[rw.state] || rw.state;
|
||||
const regColor = rw.state==='trend_up' ? 'text-emerald-400' : rw.state==='choppy' ? 'text-amber-400' : 'text-red-400';
|
||||
const bandIcon = {panic:'🔴', fear:'🟠', neutral:'🟡', greed:'🟢', euphoria:'🔵'}[rw.temp_band] || '⚪';
|
||||
const bandName = {panic:'恐慌', fear:'偏冷', neutral:'中性', greed:'偏热', euphoria:'亢奋'}[rw.temp_band] || rw.temp_band;
|
||||
const activeList = (rw.active || []).slice(0, 8);
|
||||
let h = '<div class="bg-indigo-900/25 border border-indigo-500/40 rounded-lg p-3">' +
|
||||
'<div class="flex items-center justify-between flex-wrap gap-2 mb-2">' +
|
||||
'<span class="text-sm font-semibold text-indigo-300">🌡️ 当前市场温区</span>' +
|
||||
'<span class="text-sm">' + regIcon + ' <span class="font-bold ' + regColor + '">' + regName + '</span>' +
|
||||
' · 温度: <b>' + bandIcon + ' ' + bandName + '</b>' +
|
||||
(rw.temp_rsi ? ' <span class="text-slate-500 text-xs">RSI=' + rw.temp_rsi + '</span>' : '') + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="text-xs text-slate-400 mb-1.5">以下策略在当前温区激活(适用温区由实测胜率动态决定):</div>' +
|
||||
'<div class="flex flex-wrap gap-1.5">';
|
||||
if (activeList.length) {
|
||||
for (const n of activeList) {
|
||||
const w = rw.weights && rw.weights[n];
|
||||
const wr = w && w.best_win_rate != null ? w.best_win_rate + '%' : '';
|
||||
h += '<span class="text-xs bg-emerald-500/15 text-emerald-300 border border-emerald-500/30 rounded px-2 py-0.5">' +
|
||||
'<span class="font-mono font-bold">' + n + '</span> ' + wr + '</span>';
|
||||
}
|
||||
} else {
|
||||
h += '<span class="text-xs text-slate-500">当前温区无激活策略</span>';
|
||||
}
|
||||
h += '</div></div>';
|
||||
box.innerHTML = h;
|
||||
} catch(e) { box.innerHTML = ''; }
|
||||
}
|
||||
new MutationObserver(()=>{if(!document.getElementById('tab-research')?.classList?.contains('hidden'))loadStrategyRegimeBanner();}).observe(document.body,{attributes:true,subtree:true});
|
||||
|
||||
// ══════════════════════════════════════════════════════
|
||||
// 组合方案子Tab(2026-08-02 新增)
|
||||
// 展示: 当前组合方案(v_next4趋势+v_mr超跌, regime分工) + 组合回测版本 + 组合自我进化
|
||||
@@ -2261,6 +2301,45 @@ async function renderResearchCombo() {
|
||||
}
|
||||
h += '</div></div>';
|
||||
|
||||
// ── 1.5 温区自适应横幅(2026-08-13)──
|
||||
const rw = d.regime_weights || null;
|
||||
if (rw && rw.state) {
|
||||
const bandIcon = {panic:'🔴', fear:'🟠', neutral:'🟡', greed:'🟢', euphoria:'🔵'}[rw.temp_band] || '⚪';
|
||||
const bandName = {panic:'恐慌', fear:'偏冷', neutral:'中性', greed:'偏热', euphoria:'亢奋'}[rw.temp_band] || rw.temp_band;
|
||||
const activeList = (rw.active || []).slice(0, 10);
|
||||
const allList = Object.entries(rw.weights || {});
|
||||
h += '<div class="bg-indigo-900/25 border border-indigo-500/40 rounded-lg p-3 mb-3">' +
|
||||
'<div class="flex items-center justify-between flex-wrap gap-2 mb-2">' +
|
||||
'<span class="text-sm font-semibold text-indigo-300">🌡️ 温区自适应 · 当前激活</span>' +
|
||||
'<span class="text-sm">平滑温区: <span class="font-bold ' + regColor + '">' + regName + '</span>' +
|
||||
' · 温度: <b>' + bandIcon + ' ' + bandName + '</b>' +
|
||||
(rw.temp_rsi ? ' <span class="text-slate-500 text-xs">RSI=' + rw.temp_rsi + '</span>' : '') +
|
||||
(rw.state_date ? ' <span class="text-slate-600 text-xs">(' + rw.state_date + ')</span>' : '') + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="text-xs text-slate-400 mb-2">策略全温区发信号,适用温区由实测胜率动态决定(strategy_regime_perf);组合按当前温区自动激活,选股/买卖/重评已温区感知</div>' +
|
||||
'<div class="flex flex-wrap gap-1.5 mb-2">';
|
||||
if (activeList.length) {
|
||||
for (const n of activeList) {
|
||||
const w = rw.weights && rw.weights[n];
|
||||
const wr = w && w.best_win_rate != null ? w.best_win_rate + '%' : '';
|
||||
h += '<span class="text-xs bg-emerald-500/15 text-emerald-300 border border-emerald-500/30 rounded px-2 py-0.5">' +
|
||||
'<span class="font-mono font-bold">' + n + '</span> ' + wr + ' <span class="text-emerald-500/70">✓</span></span>';
|
||||
}
|
||||
} else {
|
||||
h += '<span class="text-xs text-slate-500">当前温区无激活策略</span>';
|
||||
}
|
||||
h += '</div>';
|
||||
// 观察中策略(权重<0.5 或未匹配)
|
||||
const obsList = allList.filter(([k,v]) => !v.matched).slice(0, 8);
|
||||
if (obsList.length) {
|
||||
h += '<div class="flex flex-wrap gap-1.5">' + obsList.map(([k,v]) =>
|
||||
'<span class="text-[11px] bg-slate-800 text-slate-500 border border-slate-700 rounded px-2 py-0.5">' +
|
||||
'<span class="font-mono">' + k + '</span> ' + (v.best_win_rate != null ? v.best_win_rate + '%' : '') + ' 观察</span>'
|
||||
).join('') + '</div>';
|
||||
}
|
||||
h += '</div>';
|
||||
}
|
||||
|
||||
// ── 2. 组合成员策略指标 ──
|
||||
const members = d.members || {};
|
||||
if (Object.keys(members).length) {
|
||||
|
||||
Reference in New Issue
Block a user