diff --git a/evolution/evolution_api.py b/evolution/evolution_api.py index b6055252..6238d802 100644 --- a/evolution/evolution_api.py +++ b/evolution/evolution_api.py @@ -68,6 +68,82 @@ def get_evolution_dashboard(): } +def get_combo_dashboard(): + """组合方案 Dashboard 数据 (2026-08-02 新增) + 返回: 当前组合方案(v_next4+v_mr按regime分工) + 组合回测版本(v_combo) + 市场阶段 + """ + conn = sqlite3.connect(DB) + conn.row_factory = sqlite3.Row + + # 1. 当前市场阶段 (market_regime) + regime = None + r = conn.execute("SELECT * FROM market_regime ORDER BY date DESC LIMIT 1").fetchone() + if r: + regime = dict(r) + + # 2. 组合回测版本 (v_combo 家族) + combos = [] + rows = conn.execute( + "SELECT id, version, market, period_tag, created_at, results_json" + " FROM strategy_research WHERE version LIKE '%combo%' OR version LIKE 'v_combo%'" + " ORDER BY id DESC" + ).fetchall() + for r in rows: + d = dict(r) + res = json.loads(d.pop("results_json") or "{}") + s = res.get("summary", {}) + pf = s.get("portfolio_full", {}) + p5 = s.get("portfolio", {}) + d["summary_stats"] = { + "total_trades": s.get("total_trades"), + "win_rate": s.get("win_rate"), + "avg_profit_pct": s.get("avg_profit_pct"), + "avg_hold_days": s.get("avg_hold_days"), + "sharpe_ratio": s.get("sharpe_ratio"), + "profit_factor": s.get("profit_factor"), + "universality": s.get("universality", {}), + "portfolio": p5, + "portfolio_full": pf, + } + combos.append(d) + + # 3. 组合成员策略的独立指标 + members = {} + for v in ["v_next4", "v_mr"]: + r = conn.execute( + "SELECT results_json FROM strategy_research" + " WHERE version=? AND period_tag='10y' ORDER BY id DESC LIMIT 1", + (v,), + ).fetchone() + if r: + res = json.loads(r[0]) + s = res.get("summary", {}) + pf = s.get("portfolio_full", {}) + members[v] = { + "role": "趋势市主战" if v == "v_next4" else "震荡/下跌市接管", + "trades": s.get("total_trades"), + "win_rate": s.get("win_rate"), + "avg_profit_pct": s.get("avg_profit_pct"), + "avg_hold_days": s.get("avg_hold_days"), + "cagr_pct": pf.get("cagr_pct"), + "return_pct": pf.get("total_return_pct"), + "max_dd_pct": pf.get("portfolio_max_dd_pct"), + "universality": s.get("universality", {}), + } + + conn.close() + return { + "regime": regime, + "combos": combos, + "members": members, + "routing": [ + {"regime": "trend_up", "active": "v_next4", "action": "追涨买入/加仓放行", "desc": "大盘MA20上方+ADX强, 趋势追涨主战场"}, + {"regime": "choppy", "active": "v_mr", "action": "追涨降级为关注", "desc": "震荡市, 超跌反弹主战场, 趋势追涨让位"}, + {"regime": "trend_down", "active": "v_mr", "action": "禁止追涨", "desc": "深超跌主战场, 只做均值回复"}, + ], + } + + def get_health_trend(version='v_next4', days=30): """健康度趋势""" conn = sqlite3.connect(DB) diff --git a/server.py b/server.py index b37724af..8bf01a74 100644 --- a/server.py +++ b/server.py @@ -1823,6 +1823,17 @@ def api_evolution_health_trend(): return jsonify({'error': str(e)}), 500 +@app.route("/api/evolution/combo") +def api_evolution_combo(): + """组合方案 Dashboard (v_next4+v_mr regime分工 + v_combo回测)""" + try: + sys.path.insert(0, '/home/hmo/MoFin/evolution') + from evolution_api import get_combo_dashboard + return jsonify(get_combo_dashboard()) + except Exception as e: + return jsonify({'error': str(e)}), 500 + + if __name__ == "__main__": port = int(os.environ.get("PORT", 8899)) print(f"🚀 MoFin Dashboard → http://0.0.0.0:{port}") diff --git a/static/index.html b/static/index.html index 6968e98f..142f314e 100644 --- a/static/index.html +++ b/static/index.html @@ -1929,17 +1929,37 @@ function inMd(t) { function renderResearch() { const el = document.getElementById('tab-research'); if (!el) return; - el.innerHTML = '
🧬 进化模块加载中...
' + - '
' + - '

📋 策略研究 — 版本迭代对比

' + - '' + - '' + - '每版策略基于上一版归因分析迭代 · 点击行展开明细
' + - '
加载中...
' + - '
'; + el.innerHTML = + '
' + + '' + + '' + + '策略=版本迭代对比 · 组合=市场阶段分工方案' + + '
' + + '
' + + '
🧬 进化模块加载中...
' + + '
' + + '

📋 策略研究 — 版本迭代对比

' + + '' + + '' + + '每版策略基于上一版归因分析迭代 · 点击行展开明细
' + + '
加载中...
' + + '
' + + '
' + + ''; + // 子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(); + }; + }); // 恢复用户上次选择的周期(防自动刷新重置,2026-07-30老爸抓包) const savedPt = window._btPeriodVal || '2y'; const ptSel = document.getElementById('btPeriod'); @@ -2024,6 +2044,116 @@ 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-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 + '' : '') + '' + + '
' + + '
v_next4 趋势追涨 + v_mr 均值回复,按大盘市场阶段自动分工(strategy_lifecycle 6.5 实盘落地)
' + + '
'; + 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 += '
'; + + // ── 2. 组合成员策略指标 ── + const members = d.members || {}; + if (Object.keys(members).length) { + h += '
成员策略(10年回测)
' + + '
'; + for (const [v, m] of Object.entries(members)) { + const u = m.universality || {}; + h += '
' + + '
' + v + '' + + '' + (m.role || '') + '
' + + '
' + + '
年化
' + (m.cagr_pct != null ? m.cagr_pct + '%' : '—') + '
' + + '
胜率
' + (m.win_rate != null ? m.win_rate + '%' : '—') + '
' + + '
笔数
' + (m.trades != null ? (m.trades > 999 ? Math.round(m.trades/1000) + 'k' : m.trades) : '—') + '
' + + '
覆盖
' + (u.months || '—') + '月
' + + '
' + + '
均持' + (m.avg_hold_days != null ? m.avg_hold_days + '天' : '—') + ' · 均收益' + (m.avg_profit_pct != null ? m.avg_profit_pct + '%' : '—') + ' · 回撤' + (m.max_dd_pct != null ? m.max_dd_pct + '%' : '—') + '
' + + '
'; + } + 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; @@ -2243,7 +2373,7 @@ function renderStrategyTable(strategies) { 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' : ''); html += '' + '' + s.version + (isCurrent ? ' 当前' : '') + ((s.market && s.market !== 'all') ? ' ' + (s.market === 'hk' ? '港' : 'A') + '' : '') + '' + - '' + (s.name || '') + (smallSample ? ' ⚠️' : '') + '' + + '' + (s.name || '') + (smallSample ? ' ⚠️' : '') + '' + '' + (st.sizing_slots ? st.sizing_slots + '仓' : '10仓') + (st.conviction_model ? '' : '') + '' + '' + ((s.description && s.description.title) ? '' : '') + '' + cell('composite', s._composite, v => v + (s._confidence != null && s._confidence < 1 ? '×' + s._confidence.toFixed(2) + '' : ''), 'font-bold text-amber-300') +