Improve robust shadow band and trim header
This commit is contained in:
@@ -400,7 +400,7 @@ def html_template(data: dict[str, Any]) -> str:
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>NO.5 可更新数据 - 基本面评分面板</title>
|
||||
<title>基本面评分面板</title>
|
||||
<style>
|
||||
:root {{
|
||||
--yellow:#fff200; --blue:#1d4ed8; --red:#dc2626; --green:#059669;
|
||||
@@ -486,7 +486,6 @@ svg {{ width:100%; height:230px; display:block; }}
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="brand">NO.5 可更新数据</div>
|
||||
<label class="ctrl">品种 <input id="search" placeholder="搜索品种或指标"></label>
|
||||
<label class="ctrl">维度 <select id="dim"><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 id="refreshData">刷新数据</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>
|
||||
<section class="summary">
|
||||
<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();
|
||||
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;
|
||||
document.getElementById("generated").textContent = DATA.generated_at;
|
||||
document.getElementById("completion").textContent = `${{DATA.completion.count}}/${{DATA.completion.total}}(${{DATA.completion.ratio.toFixed(1)}}%)`;
|
||||
function todayString() {{
|
||||
const d = new Date();
|
||||
@@ -713,6 +711,26 @@ function quantile(sorted, q) {{
|
||||
if (lo === hi) return sorted[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) {{
|
||||
if (!band.length || radius <= 0) return band;
|
||||
const n = band.length;
|
||||
@@ -760,8 +778,8 @@ function buildNormalizedChart(years) {{
|
||||
}});
|
||||
const band = [];
|
||||
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);
|
||||
if (samples.length < 4) continue;
|
||||
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 < 3) continue;
|
||||
const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10));
|
||||
const mid = quantile(samples, 0.50);
|
||||
let low = quantile(samples, tail);
|
||||
|
||||
+25
-7
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user