feat: 港股策略研究体系——253只港股通宇宙回填+v7.1港股基线(61.5%胜率26笔)+h1.0过拟合教训+h1.1三强信号版+引擎/ API/UI分市场开关(全部/A股/港股)

This commit is contained in:
hmo
2026-07-29 11:03:27 +08:00
parent 910b1d521a
commit fdaff1cd50
3 changed files with 60 additions and 13 deletions
+9 -3
View File
@@ -1934,6 +1934,8 @@ function renderResearch() {
'<select id="btPeriod" class="bg-slate-800 text-sm rounded-lg px-2 py-1 border border-slate-700">' +
'<option value="1m">近 1 个月</option><option value="6m">近 6 个月</option>' +
'<option value="1y">近 1 年</option><option value="2y" selected>近 2 年</option></select>' +
'<select id="btMarket" class="bg-slate-800 text-sm rounded-lg px-2 py-1 border border-slate-700" onchange="loadStrategyList()">' +
'<option value="all">全部市场</option><option value="a">A股</option><option value="hk">港股</option></select>' +
'<span class="text-xs text-slate-500">每版策略基于上一版归因分析迭代 · 点击行展开明细</span></div>' +
'<div id="strategyList"><div class="text-slate-500 text-sm">加载中...</div></div>' +
'<div id="strategyDetail" class="mt-4"></div>';
@@ -1946,7 +1948,10 @@ async function loadStrategyList() {
const resp = await fetch('/api/research/strategies');
const data = await resp.json();
if (data.error) { listEl.innerHTML = '<div class="text-red-400">' + data.error + '</div>'; return; }
renderStrategyTable(data.strategies || []);
const mkt = document.getElementById('btMarket')?.value || 'all';
let strats = data.strategies || [];
if (mkt !== 'all') strats = strats.filter(s => (s.market || 'all') === mkt || (s.market || 'all') === 'all');
renderStrategyTable(strats);
} catch(e) {
listEl.innerHTML = '<div class="text-red-400">加载失败: ' + e.message + '</div>';
}
@@ -2035,7 +2040,7 @@ function renderStrategyTable(strategies) {
const retCls = pf.total_return_pct == null ? '' : (pf.total_return_pct >= 0 ? 'text-green-400' : 'text-red-400');
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 += '<tr class="' + rowCls + '" onclick="showStrategyDetail(\'' + s.version + '\')">' +
'<td class="px-2 py-1.5 font-mono font-bold ' + (isCurrent ? 'text-emerald-400' : 'text-blue-400') + '">' + s.version + (isCurrent ? ' <span class="text-[10px] bg-emerald-500/20 text-emerald-300 px-1 rounded">当前</span>' : '') + '</td>' +
'<td class="px-2 py-1.5 font-mono font-bold ' + (isCurrent ? 'text-emerald-400' : 'text-blue-400') + '">' + s.version + (isCurrent ? ' <span class="text-[10px] bg-emerald-500/20 text-emerald-300 px-1 rounded">当前</span>' : '') + ((s.market && s.market !== 'all') ? ' <span class="text-[10px] bg-cyan-500/20 text-cyan-300 px-1 rounded">' + (s.market === 'hk' ? '港' : 'A') + '</span>' : '') + '</td>' +
'<td class="px-2 py-1.5" title="' + (s.hypothesis || s.summary || '').replace(/"/g, '&quot;') + '">' + (s.name || '') + (smallSample ? ' <span class="text-amber-500" title="样本<40笔">⚠️</span>' : '') + '</td>' +
cell('composite', s._composite, v => v, 'font-bold text-amber-300') +
cell('universality_score', (st.universality || {}).score, v => v + '<span class="text-slate-500">/' + (st.universality || {}).months + '月</span>') +
@@ -2063,7 +2068,8 @@ async function runStrategyBacktest(version) {
const detail = document.getElementById('strategyDetail');
detail.innerHTML = '<div class="text-blue-400 animate-pulse text-sm">⏳ 正在回测 ' + version + '(约15-30秒)...</div>';
try {
const resp = await fetch('/api/research/backtest?strategy=' + version + '&period=' + period);
const mkt = document.getElementById('btMarket')?.value || 'all';
const resp = await fetch('/api/research/backtest?strategy=' + version + '&period=' + period + '&market=' + mkt);
const data = await resp.json();
if (data.error) { detail.innerHTML = '<div class="text-red-400">' + data.error + '</div>'; return; }
await loadStrategyList();