feat: 研究Tab组合方案展示 — 市场阶段分工(v_next4趋势+v_mr超跌)可视化
- 后端: evolution_api.py 新增 get_combo_dashboard() (regime+组合回测+成员指标+routing) - 后端: server.py 新增 /api/evolution/combo 路由 - 前端: 研究Tab加子Tab(策略/组合), 组合Tab展示当前regime路由+成员策略10y指标+v_combo回测列表+组合自我进化 - 前端: 策略表格名称列启用换行(whitespace-normal), 消除长名称撑出水平滚动条 - 修复: market_regime 查询用 date 排序(id列不存在)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user