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 += '' +
+ '| ' + 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 += '
';
+ h += '⚠️ v_combo 为历史组合实验:2年+47.9%但5年验证2023/24震荡转折年亏损(B动量族假突破),未采纳——当前组合采用 v_next4+v_mr regime 分工方案
';
+ } else {
+ h += '暂无组合回测记录
';
+ }
+
+ // ── 4. 组合自我进化 ──
+ h += '' +
+ '
🧬 组合方案自我进化
' +
+ '
组合方案 = 策略分工路由(regime → 策略),进化方向:
' +
+ '
' +
+ '- ① 市场阶段判定参数(MA20/ADX 阈值)随实盘验证迭代
' +
+ '- ② 各成员策略独立进化(v_next4 参数迭代见策略Tab)
' +
+ '- ③ 组合权重/接管时机按回测与实盘偏差调优
' +
+ '
' +
+ '
📌 组合级进化记录待接入:当前进化系统只跟踪单策略(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') +