Improve robust shadow band and trim header

This commit is contained in:
2026-06-09 18:23:06 +08:00
parent f8853d67c6
commit e101f6120c
2 changed files with 49 additions and 13 deletions
+24 -6
View File
@@ -400,7 +400,7 @@ def html_template(data: dict[str, Any]) -> str:
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>NO.5 可更新数据 - 基本面评分面板</title> <title>基本面评分面板</title>
<style> <style>
:root {{ :root {{
--yellow:#fff200; --blue:#1d4ed8; --red:#dc2626; --green:#059669; --yellow:#fff200; --blue:#1d4ed8; --red:#dc2626; --green:#059669;
@@ -486,7 +486,6 @@ svg {{ width:100%; height:230px; display:block; }}
</head> </head>
<body> <body>
<div class="topbar"> <div class="topbar">
<div class="brand">NO.5 可更新数据</div>
<label class="ctrl">品种 <input id="search" placeholder="搜索品种或指标"></label> <label class="ctrl">品种 <input id="search" placeholder="搜索品种或指标"></label>
<label class="ctrl">维度 <select id="dim"><option>全部</option></select></label> <label class="ctrl">维度 <select id="dim"><option>全部</option></select></label>
<label class="ctrl">板块 <select id="board"><option>全部</option></select></label> <label class="ctrl">板块 <select id="board"><option>全部</option></select></label>
@@ -499,7 +498,7 @@ svg {{ width:100%; height:230px; display:block; }}
<button class="tabbtn" id="historyTab">评分走势</button> <button class="tabbtn" id="historyTab">评分走势</button>
<button id="refreshData">刷新数据</button> <button id="refreshData">刷新数据</button>
<button id="reset">重置</button> <button id="reset">重置</button>
<div class="stamp">完成度:<span id="completion"></span> 核心修正:库存反向计分 生成时间:<span id="generated"></span></div> <div class="stamp">完成度:<span id="completion"></span></div>
</div> </div>
<section class="summary"> <section class="summary">
<div class="card"> <div class="card">
@@ -565,7 +564,6 @@ for (const b of DATA.boards) boardSel.append(new Option(b, b));
const maxChartYear = new Date(DATA.generated_at.slice(0,10)).getFullYear(); const maxChartYear = new Date(DATA.generated_at.slice(0,10)).getFullYear();
for (let y=2018; y<=maxChartYear; y++) chartStartYear.append(new Option(String(y), String(y))); for (let y=2018; y<=maxChartYear; y++) chartStartYear.append(new Option(String(y), String(y)));
chartStartYear.value = chartStartYear.querySelector('option[value="2021"]') ? "2021" : chartStartYear.options[0]?.value; chartStartYear.value = chartStartYear.querySelector('option[value="2021"]') ? "2021" : chartStartYear.options[0]?.value;
document.getElementById("generated").textContent = DATA.generated_at;
document.getElementById("completion").textContent = `${{DATA.completion.count}}/${{DATA.completion.total}}${{DATA.completion.ratio.toFixed(1)}}%`; document.getElementById("completion").textContent = `${{DATA.completion.count}}/${{DATA.completion.total}}${{DATA.completion.ratio.toFixed(1)}}%`;
function todayString() {{ function todayString() {{
const d = new Date(); const d = new Date();
@@ -713,6 +711,26 @@ function quantile(sorted, q) {{
if (lo === hi) return sorted[lo]; if (lo === hi) return sorted[lo];
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo); return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
}} }}
function robustAnnualSeasonValues(points) {{
const byYear = new Map();
for (const p of points) {{
if (!byYear.has(p.year)) byYear.set(p.year, []);
byYear.get(p.year).push(p.value);
}}
const annual = [...byYear.values()].map(values => {{
const sorted = values.filter(Number.isFinite).sort((a,b)=>a-b);
return quantile(sorted, 0.50);
}}).filter(Number.isFinite).sort((a,b)=>a-b);
if (annual.length < 4) return annual;
const center = quantile(annual, 0.50);
const keepCount = Math.max(3, Math.ceil(annual.length * 0.65));
return annual
.map(value => ({{value, distance: Math.abs(value - center)}}))
.sort((a,b)=>a.distance-b.distance)
.slice(0, keepCount)
.map(x=>x.value)
.sort((a,b)=>a-b);
}}
function smoothBand(band, radius) {{ function smoothBand(band, radius) {{
if (!band.length || radius <= 0) return band; if (!band.length || radius <= 0) return band;
const n = band.length; const n = band.length;
@@ -760,8 +778,8 @@ function buildNormalizedChart(years) {{
}}); }});
const band = []; const band = [];
for (let day=1; day<=366; day++) {{ for (let day=1; day<=366; day++) {{
const samples = rawPoints.filter(p => Number(p.year) >= params.startYear && Number(p.year) < params.excludeYear && circularDiff(p.day, day) <= params.window).map(p=>p.value).filter(Number.isFinite).sort((a,b)=>a-b); const samples = robustAnnualSeasonValues(rawPoints.filter(p => Number(p.year) >= params.startYear && Number(p.year) < params.excludeYear && circularDiff(p.day, day) <= params.window));
if (samples.length < 4) continue; if (samples.length < 3) continue;
const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10)); const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10));
const mid = quantile(samples, 0.50); const mid = quantile(samples, 0.50);
let low = quantile(samples, tail); let low = quantile(samples, tail);
File diff suppressed because one or more lines are too long