196 lines
26 KiB
HTML
196 lines
26 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}AlphaX Agent | Crypto — 策略进化{% endblock %}
|
||
{% block nav_links %}
|
||
<a class="sidebar-link" href="/app"><svg class="link-icon"><use href="#svg-dashboard"/></svg>看板</a>
|
||
<a class="sidebar-link" href="/sentiment"><svg class="link-icon"><use href="#svg-sentiment"/></svg>舆情</a>
|
||
<a class="sidebar-link" href="/subscription"><svg class="link-icon"><use href="#svg-subscribe"/></svg>订阅</a>
|
||
<a class="sidebar-link" href="/referral"><svg class="link-icon"><use href="#svg-referral"/></svg>推荐</a>
|
||
<div class="sidebar-section-label admin-link" style="display:none">研发</div>
|
||
<a class="sidebar-link admin-link" href="/pipeline" style="display:none"><svg class="link-icon"><use href="#svg-pipeline"/></svg>链路日志</a>
|
||
<a class="sidebar-link admin-link" href="/strategy" style="display:none"><svg class="link-icon"><use href="#svg-target"/></svg>策略</a>
|
||
<a class="sidebar-link active admin-link" href="/iteration" style="display:none"><svg class="link-icon"><use href="#svg-iterate"/></svg>迭代</a>
|
||
<a class="sidebar-link admin-link" href="/admin.html" style="display:none"><svg class="link-icon"><use href="#svg-admin"/></svg>管理</a>
|
||
{% endblock %}
|
||
|
||
{% block extra_head_css %}
|
||
<style>
|
||
.shell { width:min(100% - 40px,1180px); margin:0 auto; padding:28px 0 48px; }
|
||
.hero { display:flex; justify-content:space-between; align-items:flex-start; gap:20px; margin-bottom:18px; }
|
||
h2 { font-size:26px; font-weight:900; margin:0 0 8px; color:var(--ink); }
|
||
.subtitle { color:var(--stone); font-size:14px; line-height:1.7; max-width:780px; }
|
||
.badge { display:inline-flex; align-items:center; gap:6px; padding:5px 10px; border-radius:999px; font-size:12px; font-weight:800; border:1px solid var(--hairline-soft); background:var(--canvas); color:var(--slate); white-space:nowrap; }
|
||
.badge.release { background:var(--green-light); color:var(--green); border-color:rgba(29,166,122,.2); }
|
||
.badge.gray { background:rgba(66,98,255,.08); color:var(--blue); border-color:rgba(66,98,255,.18); }
|
||
.badge.hold { background:var(--yellow-light); color:var(--yellow-dark); border-color:rgba(245,158,11,.18); }
|
||
.badge.reject { background:rgba(216,75,75,.08); color:var(--red); border-color:rgba(216,75,75,.18); }
|
||
.toolbar { display:flex; gap:10px; flex-wrap:wrap; }
|
||
.btn { border:1px solid var(--hairline-soft); background:var(--canvas); color:var(--ink); border-radius:12px; min-height:44px; padding:9px 12px; font-size:13px; font-weight:800; cursor:pointer; }
|
||
.btn:hover { border-color:var(--hairline); box-shadow:0 4px 12px rgba(5,0,56,.05); }
|
||
.grid { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:18px 0; }
|
||
.kpi { background:var(--canvas); border:1px solid var(--hairline-soft); border-radius:18px; padding:16px; }
|
||
.kpi .label { color:var(--muted); font-size:12px; font-weight:700; margin-bottom:8px; }
|
||
.kpi .value { color:var(--ink); font-size:28px; font-weight:900; letter-spacing:-.04em; }
|
||
.kpi .note { color:var(--stone); font-size:12px; margin-top:6px; line-height:1.55; }
|
||
.gate-card { background:linear-gradient(135deg, rgba(66,98,255,.08), rgba(255,208,47,.12)); border:1px solid var(--hairline-soft); border-radius:20px; padding:16px; margin:14px 0 18px; }
|
||
.gate-head { display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:10px; }
|
||
.gate-title { font-size:15px; font-weight:900; color:var(--ink); }
|
||
.gate-text { color:var(--slate); font-size:13px; line-height:1.7; }
|
||
.gate-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; margin-top:12px; }
|
||
.gate-mini { background:rgba(255,255,255,.7); border:1px solid var(--hairline-soft); border-radius:14px; padding:10px; }
|
||
.gate-mini span { display:block; color:var(--muted); font-size:11px; font-weight:800; margin-bottom:4px; }
|
||
.gate-mini b { color:var(--ink); font-size:16px; }
|
||
.tabs { display:flex; gap:8px; margin:22px 0 14px; flex-wrap:wrap; }
|
||
.tab { padding:9px 13px; border-radius:999px; border:1px solid var(--hairline-soft); background:var(--surface); color:var(--slate); font-size:13px; font-weight:900; cursor:pointer; min-height:44px; }
|
||
.tab.active { background:var(--primary); color:white; border-color:var(--primary); }
|
||
.panel { display:none; }
|
||
.panel.active { display:block; }
|
||
.board { display:grid; grid-template-columns:1.05fr .95fr; gap:14px; }
|
||
.card { background:var(--canvas); border:1px solid var(--hairline-soft); border-radius:20px; padding:18px; margin-bottom:14px; }
|
||
.card-title { font-size:15px; font-weight:900; margin-bottom:12px; color:var(--ink); display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap; }
|
||
.timeline { position:relative; padding-left:34px; }
|
||
.timeline::before { content:""; position:absolute; left:13px; top:0; bottom:0; width:2px; background:var(--hairline); }
|
||
.iter { position:relative; border:1px solid var(--hairline-soft); background:var(--canvas); border-radius:18px; padding:16px; margin-bottom:12px; }
|
||
.iter::before { content:""; position:absolute; left:-26px; top:22px; width:10px; height:10px; border-radius:50%; background:var(--yellow); border:3px solid var(--canvas); box-shadow:0 0 0 1px var(--hairline); }
|
||
.iter.release::before { background:var(--green); }
|
||
.iter.gray::before { background:var(--blue); }
|
||
.iter-head { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
|
||
.ver { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:13px; font-weight:900; color:var(--primary); background:var(--yellow-light); padding:3px 9px; border-radius:10px; }
|
||
.title { font-size:14px; font-weight:900; color:var(--ink); }
|
||
.time { margin-left:auto; color:var(--muted); font-size:12px; }
|
||
.metrics { display:flex; gap:12px; flex-wrap:wrap; margin:12px 0; color:var(--slate); font-size:12px; }
|
||
.metric b { color:var(--ink); }
|
||
.summary { color:var(--slate); font-size:13px; line-height:1.7; }
|
||
.detail { margin-top:12px; padding-top:12px; border-top:1px solid var(--hairline-soft); display:none; }
|
||
.iter.open .detail { display:block; }
|
||
.section { margin-top:10px; }
|
||
.section-label { font-size:11px; font-weight:900; color:var(--muted); letter-spacing:.04em; margin-bottom:7px; }
|
||
.item { color:var(--slate); font-size:13px; line-height:1.65; padding:6px 0 6px 13px; position:relative; }
|
||
.item::before { content:""; position:absolute; left:0; top:15px; width:4px; height:4px; border-radius:50%; background:var(--hairline-strong); }
|
||
.item.good { color:var(--green); } .item.warn { color:var(--red); }
|
||
.table { width:100%; border-collapse:separate; border-spacing:0 8px; }
|
||
.table th { color:var(--muted); font-size:11px; text-align:left; padding:0 8px; font-weight:900; }
|
||
.table td { background:var(--surface); border-top:1px solid var(--hairline-soft); border-bottom:1px solid var(--hairline-soft); padding:10px 8px; font-size:12px; color:var(--slate); vertical-align:top; }
|
||
.table td:first-child { border-left:1px solid var(--hairline-soft); border-radius:12px 0 0 12px; }
|
||
.table td:last-child { border-right:1px solid var(--hairline-soft); border-radius:0 12px 12px 0; }
|
||
.rule-name { color:var(--ink); font-weight:900; max-width:380px; white-space:normal; line-height:1.55; }
|
||
.reason { max-width:360px; white-space:normal; line-height:1.55; color:var(--slate); }
|
||
.score { font-weight:900; color:var(--primary); }
|
||
.failure-chip { display:inline-flex; margin:3px; padding:6px 9px; border-radius:999px; background:rgba(216,75,75,.08); color:var(--red); font-size:12px; font-weight:800; }
|
||
.empty,.loading { text-align:center; padding:44px 20px; color:var(--stone); font-size:14px; }
|
||
@media(max-width:860px){ .shell{width:min(100% - 24px,1180px); padding-top:20px;} .hero{display:block;} .toolbar{margin-top:12px;} .grid,.gate-grid{grid-template-columns:repeat(2,1fr);} .board{grid-template-columns:1fr;} .time{margin-left:0;width:100%;} .table{display:block; overflow-x:auto; white-space:nowrap;} .rule-name,.reason{min-width:260px;} }
|
||
@media(max-width:480px){ .grid,.gate-grid{grid-template-columns:1fr;} .kpi .value{font-size:24px;} .tabs{gap:6px;} .tab{padding:8px 10px;} .timeline{padding-left:26px;} .timeline::before{left:9px;} .iter::before{left:-22px;} }
|
||
|
||
/* ===== USER REPORT VIEW ===== */
|
||
.summary-report { display:grid; grid-template-columns:1.1fr .9fr; gap:14px; margin:16px 0 18px; }
|
||
.report-card { background:var(--canvas); border:1px solid var(--hairline-soft); border-radius:22px; padding:18px; }
|
||
.report-title { font-size:15px; font-weight:900; color:var(--ink); margin-bottom:10px; display:flex; align-items:center; justify-content:space-between; gap:10px; }
|
||
.report-answer { font-size:28px; font-weight:900; letter-spacing:-.04em; color:var(--ink); margin:8px 0; }
|
||
.report-answer.release { color:var(--green); } .report-answer.gray { color:var(--blue); } .report-answer.hold { color:var(--yellow-dark); } .report-answer.reject { color:var(--red); }
|
||
.report-text { color:var(--slate); font-size:13px; line-height:1.75; }
|
||
.report-list { display:flex; flex-direction:column; gap:8px; margin-top:12px; }
|
||
.report-item { border:1px solid var(--hairline-soft); background:var(--surface); border-radius:14px; padding:10px 12px; }
|
||
.report-item b { display:block; color:var(--ink); font-size:13px; margin-bottom:4px; }
|
||
.report-item span { color:var(--stone); font-size:12px; line-height:1.55; }
|
||
.user-tabs-note { color:var(--stone); font-size:12px; margin:-6px 0 8px; line-height:1.6; }
|
||
.rule-quality { display:inline-flex; padding:4px 8px; border-radius:999px; font-size:11px; font-weight:900; }
|
||
.rule-quality.good { background:var(--green-light); color:var(--green); }
|
||
.rule-quality.wait { background:var(--yellow-light); color:var(--yellow-dark); }
|
||
.rule-quality.bad { background:var(--red-light); color:var(--red); }
|
||
@media(max-width:860px){ .summary-report{grid-template-columns:1fr;} }
|
||
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="shell">
|
||
<div class="hero">
|
||
<div>
|
||
<h2>策略进化</h2>
|
||
<p class="subtitle">这里展示 AlphaX Agent | Crypto 策略是否真的在变聪明:本轮有没有发布、为什么没发布、哪些规律还在观察、哪些错误正在减少。</p>
|
||
</div>
|
||
<div class="toolbar">
|
||
<button class="btn" onclick="refreshCandidates()">刷新规则表现</button>
|
||
<button class="btn" onclick="loadAll()">刷新页面</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid" id="kpis"><div class="loading">加载中…</div></div>
|
||
<div id="gateBox" class="gate-card"><div class="loading">加载发布闸门…</div></div>
|
||
<div id="userReport" class="summary-report"><div class="loading">生成用户版进化报告…</div></div>
|
||
|
||
<div class="tabs">
|
||
<button class="tab active" data-tab="timeline" onclick="switchTab('timeline')">本轮结论</button>
|
||
<button class="tab" data-tab="candidates" onclick="switchTab('candidates')">发现的规律</button>
|
||
<button class="tab" data-tab="dryrun" onclick="switchTab('dryrun')">发布预演</button>
|
||
<button class="tab" data-tab="failures" onclick="switchTab('failures')">错误复盘</button>
|
||
<button class="tab" data-tab="versions" onclick="switchTab('versions')">版本表现</button>
|
||
</div>
|
||
|
||
<div class="panel active" id="panel-timeline"><div class="timeline" id="timeline"><div class="loading">加载中…</div></div></div>
|
||
<div class="panel" id="panel-candidates"><div class="card"><div class="card-title">发现的规律 <span class="badge hold">未达标不发布</span></div><div class="user-tabs-note">这些是系统复盘后发现的可能规律。只有样本、成功率、收益和稳定性都达标,才会进入线上策略。</div><div id="candidates"></div></div></div>
|
||
<div class="panel" id="panel-dryrun"><div class="card"><div class="card-title">发布预演 <span class="badge hold">只读评估,不改线上策略</span></div><div id="dryrun"></div></div></div>
|
||
<div class="panel" id="panel-failures"><div class="board"><div class="card"><div class="card-title">主要失败原因</div><div id="failureSummary"></div></div><div class="card"><div class="card-title">失败样本</div><div id="failures"></div></div></div></div>
|
||
<div class="panel" id="panel-versions"><div class="card"><div class="card-title">版本表现</div><div id="versions"></div></div></div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_script %}
|
||
<script>
|
||
var API = '';
|
||
var $ = function(id){ return document.getElementById(id); };
|
||
var state = { data:null };
|
||
function esc(v){ return String(v==null?'':v).replace(/[&<>"']/g,function(c){return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c];}); }
|
||
function fmtTime(t){ if(!t)return '--'; var d=new Date(t); return (d.getMonth()+1)+'/'+d.getDate()+' '+('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2); }
|
||
function badge(status){ var cls=status==='release'||status==='active'?'release':status==='gray'?'gray':status==='rejected'?'reject':'hold'; var txt={release:'正式发布',gray:'灰度观察',hold:'只研究不发布',candidate:'候选研究',active:'正式生效',rejected:'已淘汰',blocked:'发布阻断',unknown:'旧日志',dirty_history:'污染历史'}[status]||status||'研究中'; return '<span class="badge '+cls+'">'+esc(txt)+'</span>'; }
|
||
function switchTab(tab){ document.querySelectorAll('.tab').forEach(function(x){x.classList.toggle('active',x.dataset.tab===tab);}); document.querySelectorAll('.panel').forEach(function(x){x.classList.remove('active');}); $('panel-'+tab).classList.add('active'); }
|
||
async function loadUser(){ try{ var r=await fetch(API+'/api/auth/me'); if(!r.ok)return; var d=await r.json(); var email=(d.user&&d.user.email)||''; if(!email)return; $('userInitial').textContent=email.charAt(0).toUpperCase(); $('userEmailShort').textContent=email.length>14?email.slice(0,12)+'…':email; $('ddEmail').textContent=email; }catch(e){} }
|
||
function toggleUserMenu(){ $('userDropdown').classList.toggle('show'); }
|
||
document.addEventListener('click',function(e){ if(!e.target.closest('.sidebar-user')&&!e.target.closest('.user-dropdown')) $('userDropdown').classList.remove('show'); });
|
||
async function doLogout(){ await fetch(API+'/api/auth/logout',{method:'POST'}); location.href='/auth'; }
|
||
async function refreshCandidates(){ var old=document.querySelector('.toolbar .btn'); try{ old.textContent='刷新中…'; await fetch(API+'/api/strategy/candidates/refresh',{method:'POST'}); await loadAll(); }catch(e){ alert('刷新失败'); } finally{ old.textContent='刷新候选评分'; } }
|
||
async function loadAll(){ try{ var r=await fetch(API+'/api/strategy/lifecycle?days=60'); var d=await r.json(); state.data=d; renderAll(d); }catch(e){ $('timeline').innerHTML='<div class="empty">加载失败</div>'; } }
|
||
function renderAll(d){ renderKpis(d); renderGate(d); renderUserReport(d); renderTimeline(d.logs||[]); renderCandidates(d.candidates||[], d.dry_run||{}); renderDryRun(d.dry_run||{}); renderFailures(d); renderVersions(((d.summary||{}).version_stats)||[]); }
|
||
|
||
function decisionText(decision){
|
||
var map={release:'正式发布新策略',gray:'进入灰度观察',hold:'只研究,不发布',blocked:'暂不发布',rejected:'暂不采纳',unknown:'等待复盘'};
|
||
return map[decision]||map.unknown;
|
||
}
|
||
function decisionClass(decision){ return decision==='release'?'release':decision==='gray'?'gray':(decision==='blocked'||decision==='rejected')?'reject':'hold'; }
|
||
function renderUserReport(d){
|
||
var ov=d.overview||{}, dry=d.dry_run||{}, ds=ov.dry_run_summary||{};
|
||
var decision=ov.latest_release_decision || (dry.would_bump_version?'release':'hold');
|
||
var reason=ov.latest_release_reason || dry.release_reason || '样本仍在积累,暂不改变线上策略。';
|
||
var candidates=(d.candidates||[]).slice(0,3);
|
||
var failures=((ov.failure_type_counts)||[]).slice(0,3);
|
||
var candHtml=candidates.length?candidates.map(function(c){
|
||
var name=c.rule_description||c.signal_name||'待验证规律';
|
||
var conf=Number(c.confidence_score||0);
|
||
var q=conf>=70?'good':conf>=40?'wait':'bad';
|
||
var qtxt=conf>=70?'接近可用':conf>=40?'继续观察':'证据不足';
|
||
return '<div class="report-item"><b>'+esc(name)+'</b><span><span class="rule-quality '+q+'">'+qtxt+'</span> 样本 '+esc(c.sample_size||0)+' · 置信 '+esc(c.confidence_score||0)+' · 平均表现 '+esc(c.avg_pnl||0)+'</span></div>';
|
||
}).join(''):'<div class="report-item"><b>暂无新规律</b><span>当前没有足够证据支持策略改动。</span></div>';
|
||
var failHtml=failures.length?failures.map(function(f){return '<div class="report-item"><b>'+esc(f.type||'失败模式')+'</b><span>出现 '+esc(f.count||0)+' 次,后续复盘会重点观察是否重复发生。</span></div>';}).join(''):'<div class="report-item"><b>暂无集中失败模式</b><span>当前失败样本不足,先继续观察。</span></div>';
|
||
$('userReport').innerHTML = '<div class="report-card"><div class="report-title">本轮策略结论 '+badge(decision)+'</div><div class="report-answer '+decisionClass(decision)+'">'+decisionText(decision)+'</div><div class="report-text">'+esc(reason)+'</div><div class="report-list">'+candHtml+'</div></div>' +
|
||
'<div class="report-card"><div class="report-title">最近最该关注的错误</div><div class="report-text">系统不只看成功因子,也会记录反复导致失败的原因,避免下一轮继续犯同样的错。</div><div class="report-list">'+failHtml+'</div></div>';
|
||
}
|
||
|
||
function renderKpis(d){ var ov=d.overview||{}, st=ov.candidate_status_counts||{}, rd=ov.release_decision_counts||{}, dry=ov.dry_run_summary||{}, latest=(d.logs&&d.logs[0]&&d.logs[0].metrics)||{}; $('kpis').innerHTML=[
|
||
['有效计权', latest.effective_review_count!=null?latest.effective_review_count:(dry.review_sample_count||0), '只统计有48h窗口的交易样本'],
|
||
['样本不足', latest.insufficient_tracking_count||0, '缺少price_tracking时只记录,不调权'],
|
||
['待验证规律', ov.candidate_count||0, '观察中 '+(st.candidate||0)+' / 灰度 '+(st.gray||0)+' / 旧样本参考 '+(dry.dirty_history_candidate_count||0)],
|
||
['可灰度规律', dry.gray_ready_count||0, '达到门槛才会进入灰度'],
|
||
['正式发布', (rd.release||0), '真正改变线上策略的次数']
|
||
].map(function(k){return '<div class="kpi"><div class="label">'+k[0]+'</div><div class="value">'+k[1]+'</div><div class="note">'+k[2]+'</div></div>';}).join(''); }
|
||
function renderGate(d){ var ov=d.overview||{}, dry=(d.dry_run||{}), ds=ov.dry_run_summary||{}; var latest=ov.latest_release_decision||'hold'; $('gateBox').innerHTML='<div class="gate-head"><div class="gate-title">本轮是否发布</div>'+badge(latest)+'</div><div class="gate-text"><b>干净样本起点:</b>'+esc(ds.clean_started_at||dry.clean_started_at||'未设置')+';样本窗口:'+esc(ds.sample_window||dry.sample_window||'all_history')+'。旧污染样本只作解释,不会直接改变线上策略。</div><div class="gate-text"><b>最近发布原因:</b>'+esc(ov.latest_release_reason||'暂无发布决策说明')+'</div><div class="gate-text"><b>预演结论:</b>'+esc(dry.release_reason||ds.release_reason||'只读评估,不写库、不升版')+'</div><div class="gate-grid"><div class="gate-mini"><span>干净复盘样本</span><b>'+esc(ds.review_sample_count||dry.review_sample_count||0)+'</b></div><div class="gate-mini"><span>污染历史候选</span><b>'+esc(ds.dirty_history_candidate_count||dry.dirty_history_candidate_count||0)+'</b></div><div class="gate-mini"><span>可灰度</span><b>'+esc(ds.gray_ready_count||dry.gray_ready_count||0)+'</b></div><div class="gate-mini"><span>是否发布</span><b>'+(dry.would_bump_version?'是':'否')+'</b></div></div>'; }
|
||
function renderTimeline(items){ if(!items.length){$('timeline').innerHTML='<div class="empty">暂无迭代记录</div>';return;} $('timeline').innerHTML=items.map(function(it){ var decision=it.release_decision||'unknown'; var metrics=it.metrics||{}; var cls=decision==='release'?' release':decision==='gray'?' gray':''; return '<div class="iter'+cls+'" onclick="this.classList.toggle(\'open\')"><div class="iter-head"><span class="ver">'+esc(it.strategy_version||'--')+'</span><span class="title">'+esc(it.title||'复盘迭代')+'</span>'+badge(decision)+'<span class="time">'+fmtTime(it.created_at)+'</span></div><div class="metrics"><span class="metric">有效 <b>'+(metrics.effective_review_count!=null?metrics.effective_review_count:((metrics.hit_count||0)+(metrics.flat_count||0)+(metrics.fail_count||0)))+'</b></span><span class="metric">爆发 <b>'+(metrics.hit_count||0)+'</b></span><span class="metric">横盘 <b>'+(metrics.flat_count||0)+'</b></span><span class="metric">失败 <b>'+(metrics.fail_count||0)+'</b></span><span class="metric">样本不足 <b>'+(metrics.insufficient_tracking_count||0)+'</b></span><span class="metric">候选 <b>'+((it.candidate_rules||[]).length)+'</b></span><span class="metric">置信 <b>'+esc(it.confidence_level||'--')+'</b></span></div><div class="summary">'+esc((it.release_reason||it.version_change_summary||it.summary||'').slice(0,280))+'</div><div class="detail">'+renderSection('成功因子',(it.success_analysis&&it.success_analysis.top_success_factors)||[],'good')+renderSection('失败模式',(it.failure_analysis&&it.failure_analysis.failure_types)||[],'warn')+renderCandidateMini(it.candidate_rules||[])+renderSection('动作',it.actions||[],'')+renderSection('问题',it.problems||[],'warn')+'</div></div>'; }).join(''); }
|
||
function renderSection(label,items,cls){ if(!items||!items.length)return ''; return '<div class="section"><div class="section-label">'+label+'</div>'+items.slice(0,10).map(function(x){ var t=typeof x==='string'?x:(x.label||x.type||x.signal||x.description||JSON.stringify(x)); var c=x.count?(' · '+x.count):''; return '<div class="item '+cls+'">'+esc(t+c)+'</div>'; }).join('')+'</div>'; }
|
||
function renderCandidateMini(items){ if(!items.length)return ''; return '<div class="section"><div class="section-label">本轮候选规则</div>'+items.slice(0,8).map(function(x){return '<div class="item">'+esc(x.description||x.signal||'候选规则')+' · 置信 '+esc(x.confidence_score||0)+' · 样本 '+esc(x.sample_size||0)+' · '+esc(x.status||'candidate')+'</div>';}).join('')+'</div>'; }
|
||
function sourceLabel(c){ var s=String(c.source||''); if(s==='reverse_analysis')return '涨幅榜逆向'; if(s.indexOf('dual_attribution_success')===0)return '成功复盘'; if(s.indexOf('dual_attribution_failure')===0)return '失败复盘'; if(s.indexOf('signal_deprecation')===0)return '低绩效信号'; if(s.indexOf('dirty_history')===0)return '历史参考'; return s||'研究池'; }
|
||
function renderCandidates(items,dry){ if(!items.length){$('candidates').innerHTML='<div class="empty">暂无待验证规律</div>';return;} var dryMap={}; (dry.evaluated_candidates||[]).forEach(function(x){dryMap[x.id]=x;}); $('candidates').innerHTML='<table class="table"><thead><tr><th>来源</th><th>当前阶段</th><th>预演结论</th><th>规律</th><th>样本</th><th>成功/失败</th><th>可信度</th><th>平均表现</th><th>为什么还没发布</th></tr></thead><tbody>'+items.map(function(c){var d=dryMap[c.id]||{};return '<tr><td><span class="tag">'+esc(sourceLabel(c))+'</span></td><td>'+badge(c.status||'candidate')+'</td><td>'+badge(d.dry_run_status||c.status||'candidate')+'</td><td class="rule-name">'+esc(c.rule_description||c.signal_name||'--')+'</td><td>'+esc(d.sample_size!=null?d.sample_size:(c.sample_size||0))+'</td><td>'+esc(d.success_count!=null?d.success_count:(c.success_count||0))+' / '+esc(d.fail_count!=null?d.fail_count:(c.fail_count||0))+'</td><td class="score">'+esc(d.confidence_score!=null?d.confidence_score:(c.confidence_score||0))+'</td><td>'+esc(d.avg_pnl!=null?d.avg_pnl:(c.avg_pnl||0))+'</td><td class="reason">'+esc(d.gate_reason||'等待样本验证')+'</td></tr>';}).join('')+'</tbody></table>'; }
|
||
function renderDryRun(dry){ var items=dry.evaluated_candidates||[]; if(!items.length){$('dryrun').innerHTML='<div class="empty">暂无待验证规律可评估</div>';return;} $('dryrun').innerHTML='<div class="gate-text">当前版本 '+esc(dry.current_version||'--')+';干净样本起点 '+esc(dry.clean_started_at||'未设置')+';干净复盘样本 '+esc(dry.review_sample_count||0)+';污染历史候选 '+esc(dry.dirty_history_candidate_count||0)+';可灰度 '+esc(dry.gray_ready_count||0)+';是否发布:'+(dry.would_bump_version?'是':'否')+'。</div><div class="gate-text"><b>灰度标准:</b>'+esc((dry.gate_policy&&dry.gate_policy.gray)||'--')+'</div><table class="table"><thead><tr><th>预演结论</th><th>规律</th><th>样本</th><th>成功/失败</th><th>可信度</th><th>平均表现</th><th>原因</th></tr></thead><tbody>'+items.map(function(x){return '<tr><td>'+badge(x.dry_run_status||'candidate')+'</td><td class="rule-name">'+esc(x.rule_description||x.signal_name||'--')+'</td><td>'+esc(x.sample_size||0)+'</td><td>'+esc(x.success_count||0)+' / '+esc(x.fail_count||0)+'</td><td class="score">'+esc(x.confidence_score||0)+'</td><td>'+esc(x.avg_pnl||0)+'</td><td class="reason">'+esc(x.gate_reason||'--')+'</td></tr>';}).join('')+'</tbody></table>'; }
|
||
function renderFailures(d){ var fs=(d.overview&&d.overview.failure_type_counts)||[]; $('failureSummary').innerHTML=fs.length?fs.map(function(f){return '<span class="failure-chip">'+esc(f.type)+' · '+esc(f.count)+'</span>';}).join(''):'<div class="empty">暂无失败模式</div>'; var items=d.failures||[]; $('failures').innerHTML=items.length?items.slice(0,30).map(function(f){return '<div class="item warn"><b>'+esc(f.symbol||'--')+'</b> · '+esc(f.failure_type||'未分类')+' · '+esc((f.failure_reason||'').slice(0,90))+' · PnL '+esc(f.pnl_pct||0)+'</div>';}).join(''):'<div class="empty">暂无失败样本</div>'; }
|
||
function renderVersions(items){ if(!items.length){$('versions').innerHTML='<div class="empty">暂无版本表现</div>';return;} $('versions').innerHTML='<table class="table"><thead><tr><th>版本</th><th>推荐数</th><th>成功</th><th>失败</th><th>待观察</th><th>成功率</th><th>均值收益</th></tr></thead><tbody>'+items.map(function(v){return '<tr><td class="rule-name">'+esc(v.strategy_version)+'</td><td>'+esc(v.recommendation_count)+'</td><td>'+esc(v.success_count)+'</td><td>'+esc(v.failed_count)+'</td><td>'+esc(v.pending_count)+'</td><td class="score">'+esc(v.success_rate_pct)+'</td><td>'+esc(v.avg_pnl_pct)+'</td></tr>';}).join('')+'</tbody></table>'; }
|
||
loadUser(); loadAll();
|
||
</script>
|
||
{% endblock %}
|