From 9f1185fc0dd11f5bf8bb842a6daf2e771807bc23 Mon Sep 17 00:00:00 2001 From: hmo Date: Thu, 13 Aug 2026 11:41:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A0=94=E7=A9=B6Tab=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E2=80=94=E2=80=94=E7=AD=96=E7=95=A5=E5=AD=90Tab=E6=8C=89?= =?UTF-8?q?=E9=80=82=E5=BA=94=E6=B8=A9=E5=8C=BA=E5=B1=95=E5=BC=80=E5=A4=9A?= =?UTF-8?q?=E8=A1=8C(=E6=AF=8F=E7=AD=96=E7=95=A5=E6=AF=8F=E6=B8=A9?= =?UTF-8?q?=E5=8C=BA=E4=B8=80=E8=A1=8C+=E6=B8=A9=E5=8C=BA=E5=88=97+?= =?UTF-8?q?=E6=B8=A9=E5=8C=BA=E8=83=9C=E7=8E=87/=E5=9D=87=E7=9B=88/?= =?UTF-8?q?=E6=A0=B7=E6=9C=AC),=20=E5=BD=93=E5=89=8D=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=BD=93=E5=89=8D=E6=B8=A9=E5=8C=BA=E6=BF=80?= =?UTF-8?q?=E6=B4=BB(regime=5Fweights.active),=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E7=BB=84=E5=90=88=E5=AD=90Tab(=E6=B8=A9=E5=8C=BA=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E6=BF=80=E6=B4=BB=E6=9B=BF=E4=BB=A3=E5=9B=BA=E5=AE=9A?= =?UTF-8?q?=E7=BB=84=E5=90=88=E6=96=B9=E6=A1=88),=20/api/research/strategi?= =?UTF-8?q?es=E8=BF=94=E5=9B=9Eregime=5Fwinrates+regime=5Factive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.py | 23 ++- static/index.html | 365 +++++++--------------------------------------- 2 files changed, 78 insertions(+), 310 deletions(-) diff --git a/server.py b/server.py index bdfafb4f..eaa4fbfe 100644 --- a/server.py +++ b/server.py @@ -540,9 +540,30 @@ def api_research_strategies(): from strategy_lab import list_strategies, STRATEGY_DESCRIPTIONS pt = request.args.get('period_tag') strats = list_strategies(period_tag=pt) + # 2026-08-13 温区自适应:为每个策略附加各温区表现(strategy_regime_perf) + # + 当前温区激活状态(regime_weights)。前端按"适应温区"展开多行。 + _weights = _load_json(DATA_DIR / "strategy_weights.json", None) or {} + _active_set = set(_weights.get("active") or []) + _regime_winrates = {} + try: + import sqlite3 as _sq + _c = _sq.connect(str(DATA_DIR / "mofin.db"), timeout=10) + _c.execute("PRAGMA busy_timeout=10000") + for _r in _c.execute( + "SELECT strategy, regime, trades, win_rate, avg_pnl FROM strategy_regime_perf" + ).fetchall(): + _regime_winrates.setdefault(_r[0], {})[_r[1]] = { + "trades": _r[2], "win_rate": _r[3], "avg_pnl": _r[4] + } + _c.close() + except Exception: + pass for s in strats: s['description'] = STRATEGY_DESCRIPTIONS.get(s['version'], {}) - return jsonify({'strategies': strats}) + s['regime_winrates'] = _regime_winrates.get(s['version'], {}) + # current = 当前温区激活(替代旧的 CURRENT_VERSIONS 硬编码) + s['current'] = s['version'] in _active_set + return jsonify({'strategies': strats, 'regime_active': sorted(_active_set)}) except Exception as e: return jsonify({'error': str(e)}), 500 diff --git a/static/index.html b/static/index.html index 049c59bb..ec773bf7 100644 --- a/static/index.html +++ b/static/index.html @@ -1,4 +1,4 @@ - + @@ -2111,9 +2111,8 @@ function renderResearch() { if (!el) return; el.innerHTML = '
' + - '' + - '' + - '策略=版本迭代对比 · 组合=市场阶段分工方案' + + '📋 策略研究' + + '按温区自适应展开 · 每策略每温区一行 · 🟢=当前温区激活' + '
' + '
' + '
🧬 进化模块加载中...
' + @@ -2128,19 +2127,11 @@ function renderResearch() { '每版策略基于上一版归因分析迭代 · 点击行展开明细
' + '
加载中...
' + '
' + - '' + - ''; - // 子Tab切换 - el.querySelectorAll('.rsub-tab-btn').forEach(btn => { - btn.onclick = () => { - el.querySelectorAll('.rsub-tab-btn').forEach(b => { b.classList.remove('bg-slate-800', 'text-blue-400'); b.classList.add('text-slate-400'); }); - btn.classList.add('bg-slate-800', 'text-blue-400'); btn.classList.remove('text-slate-400'); - const target = btn.dataset.rtab; - el.querySelectorAll('[id^="rsub-"]').forEach(div => div.classList.add('hidden')); - document.getElementById('rsub-' + target).classList.remove('hidden'); - if (target === 'combo') renderResearchCombo(); - }; - }); + ''; + // 首次渲染即加载策略列表(无子Tab切换) + loadStrategyList(); + loadEvolutionBox(); + loadStrategyRegimeBanner(); // 恢复用户上次选择的周期(防自动刷新重置,2026-07-30老爸抓包) const savedPt = window._btPeriodVal || '2y'; const ptSel = document.getElementById('btPeriod'); @@ -2162,6 +2153,8 @@ async function loadStrategyList() { const resp = await fetch('/api/research/strategies?period_tag=' + pt); const data = await resp.json(); if (data.error) { listEl.innerHTML = '
' + data.error + '
'; return; } + // 2026-08-13 温区激活策略集合(供策略表高亮) + window._regimeActive = data.regime_active || []; const mkt = document.getElementById('btMarket')?.value || 'all'; let strats = data.strategies || []; // 市场语义:'all'=未分市场的A股回测(港股回填前跑的),'hk'=港股宇宙,'a'=显式A股 @@ -2264,274 +2257,6 @@ async function loadStrategyRegimeBanner() { } 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分工) + 组合回测版本 + 组合自我进化 -// ══════════════════════════════════════════════════════ -async function renderResearchCombo() { - const el = document.getElementById('rsub-combo'); - if (!el) return; - el.innerHTML = '
🧬 组合方案加载中...
'; - try { - const r = await fetch('/api/evolution/combo'); - const d = await r.json(); - if (d.error) { el.innerHTML = '
' + d.error + '
'; return; } - - let h = ''; - - // ── 1. 当前市场阶段 + 组合方案路由卡片 ── - const regime = d.regime || {}; - const regName = {trend_up:'📈 趋势市', choppy:'〰️ 震荡市', trend_down:'📉 下跌市'}[regime.regime] || (regime.regime || '未知'); - const regColor = regime.regime === 'trend_up' ? 'text-emerald-400' : regime.regime === 'choppy' ? 'text-amber-400' : 'text-red-400'; - h += '
' + - '
' + - '🧬 当前组合方案' + - '市场阶段: ' + regName + '' + (regime.adx ? ' ADX=' + regime.adx + '' : '') + '' + - '
' + - '
当前组合(2026-08-11):v_weak(弱市超跌确认,实盘)+ p_oversold(预测超跌反弹,新策略)+ 12维重评(持仓管理)——选股与持仓管理分离,超跌家族并行
' + - '
'; - for (const rt of (d.routing || [])) { - const active = regime.regime === rt.regime; - h += '
' + - '' + ({trend_up:'📈 趋势市', choppy:'〰️ 震荡市', trend_down:'📉 下跌市'}[rt.regime] || rt.regime) + '' + - '' + rt.active + '' + - '' + rt.action + ' · ' + rt.desc + '' + - (active ? '当前生效' : '') + - '
'; - } - h += '
'; - - // ── 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 += '
' + - '
' + - '🌡️ 温区自适应 · 当前激活' + - '平滑温区: ' + regName + '' + - ' · 温度: ' + bandIcon + ' ' + bandName + '' + - (rw.temp_rsi ? ' RSI=' + rw.temp_rsi + '' : '') + - (rw.state_date ? ' (' + rw.state_date + ')' : '') + '' + - '
' + - '
策略全温区发信号,适用温区由实测胜率动态决定(strategy_regime_perf);组合按当前温区自动激活,选股/买卖/重评已温区感知
' + - '
'; - 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 += '' + - '' + n + ' ' + wr + ' '; - } - } else { - h += '当前温区无激活策略'; - } - h += '
'; - // 观察中策略(权重<0.5 或未匹配) - const obsList = allList.filter(([k,v]) => !v.matched).slice(0, 8); - if (obsList.length) { - h += '
' + obsList.map(([k,v]) => - '' + - '' + k + ' ' + (v.best_win_rate != null ? v.best_win_rate + '%' : '') + ' 观察' - ).join('') + '
'; - } - h += '
'; - } - - // ── 2. 组合成员策略指标 ── - const members = d.members || {}; - if (Object.keys(members).length) { - h += '
成员策略(10年回测,组合收益已扣费)
' + - '
'; - for (const [v, m] of Object.entries(members)) { - const u = m.universality || {}; - const yearly5 = m.positions_taken_5slot && m.positions_taken_5slot > 0 ? Math.round(m.positions_taken_5slot / 10) : '—'; - h += '
' + - '
' + v + (m.is_sel ? ' ✅推荐' : '') + '' + - '' + (m.role || '') + '
' + - // 盈利率大字突出(用户关注的核心指标) - '
' + - '
单笔均收益(毛利)
' + (m.avg_profit_pct != null ? m.avg_profit_pct + '%' : '—') + '
' + - '
组合年化(扣费)
' + (m.cagr_pct != null ? m.cagr_pct + '%' : '—') + '
' + - '
组合10年收益
' + (m.return_pct != null ? m.return_pct + '%' : '—') + '
' + - '
' + - '
' + - '
胜率
' + (m.win_rate != null ? m.win_rate + '%' : '—') + '
' + - '
信号总数
' + (m.trades != null ? (m.trades > 999 ? Math.round(m.trades/1000) + 'k' : m.trades) : '—') + '
' + - '
组合执行
' + (m.positions_taken_5slot != null ? m.positions_taken_5slot + '笔' : '—') + '
' + - '
覆盖月
' + (u.months || '—') + '
' + - '
' + - '
均持' + (m.avg_hold_days != null ? m.avg_hold_days + '天' : '—') + ' · 回撤' + (m.max_dd_pct != null ? m.max_dd_pct + '%' : '—') + ' · 组合年均' + yearly5 + '笔(人工可操作)
' + - (v === 'v_next4' ? '
⚠️ 适用池:系统跟踪池(267只,科创板/沪市为主)——2026-08-04验证:v_next4全部历史回测跑在跟踪池上(当时stock_daily未全市场扩表),全市场复跑失效(wr 35.9%/组合-34.3%)。edge=池子策展×v8.1评分×信念叠乘,不可外推全市场。实盘范围=跟踪池,池内数字有效。
' : '') + - (v === 'v_mr' ? '
v_weak 弱市策略(2026-08-03 六步方法论+12维框架定稿):大盘弱市(MA20下)+ADX∈[25,30]甜区 + 个股bias60∈[-35,-20]深超跌 + RSI≤25极度超卖 + 5日急跌确认;出场tp30%/sl12%/40日。回测:234笔/单笔avg+7.17%/胜率62.4%/去2018仍+8.02%/正贡献8/11年/组合8槽收益178.3%/年化12.8%/回撤16.4%(每月≤30信号,等权8槽,收益/回撤比10.9)。关键认知:弱市深超跌反弹的edge前提=大盘ADX∈[25,30](v_mr 缺失的大盘前置条件),负面剔除(过深/RSI偏高/阴跌)比正面排序更有效。
' : '') + - '
'; - } - h += '
'; - } - - // ── 3. 组合回测版本列表 ── - const combos = d.combos || []; - h += '
组合方案版本(回测记录)
'; - if (combos.length) { - h += '
' + - '' + - '' + - '' + - '' + - ''; - for (const c of combos) { - const st = c.summary_stats || {}; - const pf = st.portfolio_full || {}; - const u = st.universality || {}; - const name = c.version === 'v_combo' ? 'v8.1波段+B20动量' : c.version; - h += '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - ''; - } - h += '
版本名称区间执行数胜率平均收益年化回撤普适
' + c.version + '' + name + '' + (c.period_tag || '') + '' + ((pf.positions_taken || 0)) + '/' + (st.total_trades != null ? (st.total_trades > 999 ? Math.round(st.total_trades/1000) + 'k' : st.total_trades) : '—') + '' + (st.win_rate != null ? st.win_rate + '%' : '—') + '' + (st.avg_profit_pct != null ? st.avg_profit_pct + '%' : '—') + '' + (pf.cagr_pct != null ? pf.cagr_pct + '%' : '—') + '' + (pf.portfolio_max_dd_pct != null ? pf.portfolio_max_dd_pct + '%' : '—') + '' + (u.score != null ? u.score + '/' + (u.months || 0) + '月' : '—') + '
'; - h += '
⚠️ v_combo 为历史组合实验:2年+47.9%但5年验证2023/24震荡转折年亏损(B动量族假突破),未采纳——当前组合采用 v_next4+v_mr regime 分工方案
'; - } else { - h += '
暂无组合回测记录
'; - } - - // ── 4. 组合自我进化 ── - h += '
' + - '
🧬 组合方案自我进化
' + - '
组合方案 = 策略分工路由(regime → 策略),进化方向:
' + - '' + - '
📌 组合级进化记录待接入:当前进化系统只跟踪单策略(strategy_evolution 表),组合路由的自动调优需要新的 evolution 数据表。
' + - '
'; - - el.innerHTML = h; - } catch(e) { - el.innerHTML = '
组合方案加载失败: ' + e.message + '
'; - } -} - -function showDesc(version) { - const s = (window._strats || []).find(x => x.version === version); - const d = s && s.description; - if (!d || !d.title) { showToast('暂无说明'); return; } - const modal = document.createElement('div'); - modal.className = 'fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm'; - modal.onclick = () => modal.remove(); - modal.innerHTML = ` -
-
-

${d.title}

- -
-
-
算法

${d.algorithm || ''}

-
思路

${d.rationale || ''}

-
证据

${d.evidence || ''}

-
-
`; - document.body.appendChild(modal); -} -function showConviction(version) { - const s = (window._strats || []).find(x => x.version === version); - const st = (s && s.summary_stats) || {}; - const model = st.conviction_model; - if (!model) { showToast('该策略无信念分级仓位'); return; } - const slots = st.sizing_slots || 10; - const isStack = model.includes('叠乘'); - // boost分布(从trades统计在前端没有, 用summary里的说明) - const rules = isStack ? [ - ['动量基因 DNA', '收缩突破形态(ATR处20日最低1/4位+破20日新高+量比>1.2)', '×2'], - ['行业确认牛', '行业板块ADX>25(行业级趋势强化)', '×2'], - ['资金加速度', 'flow_delta>0(主力资金净流入在加速)', '×2'], - ] : [ - ['动量基因 DNA', '收缩突破形态(ATR处20日最低1/4位+破20日新高+量比>1.2)', '任一命中 ×2'], - ['行业趋势强', '行业板块ADX≥20', '任一命中 ×2'], - ['资金加速度', 'flow_delta>0(主力资金净流入在加速)', '任一命中 ×2'], - ]; - const modal = document.createElement('div'); - modal.className = 'fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm'; - modal.onclick = () => modal.remove(); - modal.innerHTML = ` -
-
-

⚡ 信念分级仓位

- -
-
-
${model}
-
基准 ${slots} 仓(总资金分${slots}份,每份一票)。命中信念因子的票按规则放大单票仓位:
- ${rules.map(r => `
-
${r[0]}
-
${r[1]}
-
${r[2]}
-
`).join('')} -
${isStack ? '多个因子同时命中则叠乘(如 DNA+行业牛 = ×4),封顶 ×4;均不命中 ×1(基准仓)。' : '多个因子同时命中不叠乘,统一 ×2;均不命中 ×1(基准仓)。'}
-
-
`; - document.body.appendChild(modal); -} -function showToast(msg) { - const t = document.createElement('div'); - t.className = 'fixed top-4 right-4 z-50 bg-slate-800 border border-slate-600 rounded-lg px-4 py-2 text-sm text-slate-200 shadow-lg'; - t.textContent = msg; - document.body.appendChild(t); - setTimeout(() => t.remove(), 2000); -} - -function sortBy(key) { - if (window._sortKey === key) { - window._sortDir = window._sortDir === 'desc' ? 'asc' : 'desc'; - } else { - window._sortKey = key; - window._sortDir = ASC_DEFAULT.has(key) ? 'asc' : 'desc'; - } - renderStrategyTable(window._strats || []); -} - -function sortStrategies(strategies, key, dir) { - const get = (s) => { - const st = s.summary_stats || {}; const pf = st.portfolio || {}; - switch(key) { - case 'version': return s.version || ''; - case 'name': return s.name || ''; - case 'sizing_slots': return st.sizing_slots || 10; - case 'composite': return s._composite != null ? s._composite : -999; - case 'universality': return (st.universality || {}).score || 0; - case 'total_return': return pf.total_return_pct != null ? pf.total_return_pct : -999; - case 'full_return': return (st.portfolio_full || {}).total_return_pct != null ? st.portfolio_full.total_return_pct : -999; - case 'capital_final': return (st.portfolio_full || pf).capital_final || 0; - case 'cagr': return (st.portfolio_full || pf).cagr_pct != null ? (st.portfolio_full || pf).cagr_pct : -999; - case 'trades': return st.total_trades || 0; - case 'hold': return st.avg_hold_days || 0; - case 'win_rate': return st.win_rate != null ? st.win_rate : -999; - case 'avg_profit': return st.avg_profit_pct != null ? st.avg_profit_pct : -999; - case 'sharpe': return st.sharpe_ratio != null ? st.sharpe_ratio : -999; - case 'profit_factor': return st.profit_factor != null ? st.profit_factor : -999; - case 'max_dd': return (st.portfolio_full || pf).portfolio_max_dd_pct != null ? (st.portfolio_full || pf).portfolio_max_dd_pct : 999; - default: return 0; - } - }; - const arr = [...strategies]; - arr.sort((a, b) => { - const va = get(a), vb = get(b); - let cmp = (typeof va === 'string') ? va.localeCompare(vb) : va - vb; - return dir === 'desc' ? -cmp : cmp; - }); - return arr; -} - function renderStrategyTable(strategies) { const el = document.getElementById('strategyList'); if (!strategies.length) { el.innerHTML = '
暂无策略版本
'; return; } @@ -2623,12 +2348,14 @@ function renderStrategyTable(strategies) { const arrow = window._sortKey === key ? (window._sortDir === 'desc' ? '▼' : '▲') : ''; return `${label}${arrow ? ' ' + arrow : ''}`; }; - // 2026-08-11 新增:拆分当前/历史区域(老莫需求) - // 当前 = current=true(实盘/新策略);历史 = 其余,默认折叠 + // 2026-08-13 温区自适应:按"适应温区"展开——每策略每温区一行 + // 当前 = 当前温区激活(后端 s.current 已改为 regime_weights.active 判断);历史 = 其余,默认折叠 const currentStrats = strategies.filter(s => s.current === true); const historyStrats = strategies.filter(s => s.current !== true); + const REGIME_META = {trend_up:{icon:'📈',name:'趋势市'}, choppy:{icon:'〰️',name:'震荡市'}, trend_down:{icon:'📉',name:'下跌市'}}; const renderGroup = (groupStrats, groupTitle, groupIcon, collapsed) => { let ghtml = '
' + + '' + th('version', '版本', 'text-left') + th('name', '名称', 'text-left') + th('sizing_slots', '仓位', 'text-center') + '' + @@ -2650,27 +2377,47 @@ function renderStrategyTable(strategies) { const smallSample = hasResult && st.total_trades < 40; const isCurrent = s.current === true; const deprecatedReason = DEPRECATED[s.version]; - const rowCls = 'border-b border-slate-800/50 hover:bg-slate-800/30 cursor-pointer' + (isCurrent ? ' bg-emerald-900/20 border-l-2 border-l-emerald-400' : ''); - ghtml += '' + - '' + - '' + - '' + - '' + - cell('composite', s._composite, v => v + (s._confidence != null && s._confidence < 1 ? '×' + s._confidence.toFixed(2) + '' : ''), 'font-bold text-amber-300') + - cell('universality_score', (st.universality || {}).score, v => v + '/' + (st.universality || {}).months + '月') + - cell('full_return_pct', (st.portfolio_full || {}).total_return_pct, v => v + '%' + ((st.portfolio_full||{}).std ? '±' + (st.portfolio_full||{}).std + '' : ''), 'font-bold text-amber-300') + - cell('capital_final', (st.portfolio_full||pf).capital_final, v => '¥' + (v/10000).toFixed(0) + '万') + - cell('cagr_pct', (st.portfolio_full||pf).cagr_pct, v => v + '%', hl((st.portfolio_full||pf).cagr_pct, best.cagr_pct)) + - cell('total_trades', st.total_trades, v => ((st.portfolio_full||{}).positions_taken ?? (pf.positions_taken||0)) + '/' + (v > 999 ? Math.round(v/1000) + 'k' : v), '', false) + - cell('avg_hold_days', st.avg_hold_days, v => v + 'd') + - cell('win_rate', st.win_rate, v => v + '%', hl(st.win_rate, best.win_rate)) + - cell('avg_profit_pct', st.avg_profit_pct, v => v + '%', (st.avg_profit_pct || 0) >= 0 ? 'text-green-400' : 'text-red-400') + - cell('sharpe_ratio', st.sharpe_ratio, v => v, hl(st.sharpe_ratio, best.sharpe_ratio)) + - cell('profit_factor', st.profit_factor, v => v, hl(st.profit_factor, best.profit_factor)) + - cell('portfolio_max_dd_pct', (st.portfolio_full||pf).portfolio_max_dd_pct, v => v + '%', hl((st.portfolio_full||pf).portfolio_max_dd_pct, best.portfolio_max_dd_pct, true), true) + - ''; + // 温区展开:有 regime_winrates 则每温区一行;无则单行("整体") + const rws = s.regime_winrates || {}; + const regimes = Object.keys(rws).length ? Object.keys(rws).sort() : ['overall']; + for (const rg of regimes) { + const rw = rws[rg]; + const isOverall = rg === 'overall'; + const rgMeta = REGIME_META[rg] || {icon:'🌐', name: rg === 'overall' ? '整体' : rg}; + const rgActive = !isOverall && isCurrent && (window._regimeActive || []).includes(s.version); + // 温区行数据:该温区胜率/均盈/样本(有则覆盖,无则用整体) + const wrShow = rw && rw.win_rate != null ? rw.win_rate : (isOverall ? st.win_rate : null); + const pnlShow = rw && rw.avg_pnl != null ? rw.avg_pnl : (isOverall ? st.avg_profit_pct : null); + const tradesShow = rw && rw.trades != null ? rw.trades : (isOverall ? st.total_trades : null); + const rowCls = 'border-b border-slate-800/50 hover:bg-slate-800/30 cursor-pointer' + (isCurrent && !isOverall ? ' bg-emerald-900/20 border-l-2 border-l-emerald-400' : ''); + ghtml += '' + + '' + + '' + + '' + + '' + + '' + + cell('composite', s._composite, v => v + (s._confidence != null && s._confidence < 1 ? '×' + s._confidence.toFixed(2) + '' : ''), 'font-bold text-amber-300') + + cell('universality_score', (st.universality || {}).score, v => v + '/' + (st.universality || {}).months + '月') + + cell('full_return_pct', (st.portfolio_full || {}).total_return_pct, v => v + '%' + ((st.portfolio_full||{}).std ? '±' + (st.portfolio_full||{}).std + '' : ''), 'font-bold text-amber-300') + + cell('capital_final', (st.portfolio_full||pf).capital_final, v => '¥' + (v/10000).toFixed(0) + '万') + + cell('cagr_pct', (st.portfolio_full||pf).cagr_pct, v => v + '%', hl((st.portfolio_full||pf).cagr_pct, best.cagr_pct)) + + cell('total_trades', tradesShow, v => (v > 999 ? Math.round(v/1000) + 'k' : v) + (isOverall ? '' : ''), '', false) + + cell('avg_hold_days', st.avg_hold_days, v => v + 'd') + + cell('win_rate', wrShow, v => v + '%', (isOverall ? hl(wrShow, best.win_rate) : (wrShow >= 50 ? 'text-green-400' : 'text-red-400'))) + + cell('avg_profit_pct', pnlShow, v => v + '%', (pnlShow || 0) >= 0 ? 'text-green-400' : 'text-red-400') + + cell('sharpe_ratio', st.sharpe_ratio, v => v, hl(st.sharpe_ratio, best.sharpe_ratio)) + + cell('profit_factor', st.profit_factor, v => v, hl(st.profit_factor, best.profit_factor)) + + cell('portfolio_max_dd_pct', (st.portfolio_full||pf).portfolio_max_dd_pct, v => v + '%', hl((st.portfolio_full||pf).portfolio_max_dd_pct, best.portfolio_max_dd_pct, true), true) + + ''; + } } ghtml += '
适应温区说明
' + s.version + (isCurrent ? ' 当前' : '') + (deprecatedReason ? ' 已证伪' : '') + ((s.market && s.market !== 'all') ? ' ' + (s.market === 'hk' ? '港' : 'A') + '' : '') + '' + (s.name || '') + (smallSample ? ' ⚠️' : '') + '' + (st.sizing_slots ? st.sizing_slots + '仓' : '10仓') + (st.conviction_model ? '' : '') + '' + ((s.description && s.description.title) ? '' : '') + '' + - '' + - '
' + + (isOverall ? '整体' : + '' + rgMeta.icon + ' ' + rgMeta.name + '') + + (rw && rw.win_rate != null ? ' 胜率' + rw.win_rate + '%' : '') + + (rw && rw.avg_pnl != null ? ' ' + (rw.avg_pnl >= 0 ? '+' : '') + rw.avg_pnl + '%' : '') + + (rw && rw.trades != null ? ' ' + rw.trades + '笔' : '') + + '' + s.version + (isCurrent ? ' 激活' : '') + (deprecatedReason ? ' 已证伪' : '') + ((s.market && s.market !== 'all') ? ' ' + (s.market === 'hk' ? '港' : 'A') + '' : '') + '' + (s.name || '') + (smallSample ? ' ⚠️' : '') + '' + (st.sizing_slots ? st.sizing_slots + '仓' : '10仓') + (st.conviction_model ? '' : '') + '' + ((s.description && s.description.title) ? '' : '') + '' + + '' + + '
'; return ghtml; @@ -2679,7 +2426,7 @@ function renderStrategyTable(strategies) { let html = ''; if (currentStrats.length) { html += '
' + - '
🟢 当前使用(' + currentStrats.length + '个)
' + + '
🟢 当前温区激活(' + currentStrats.length + '个)
' + renderGroup(currentStrats, '当前', '🟢', false) + '
'; }