从 Point-in-Time 股票筛选到统一 Web 前端的完整链路: 筛选 → 画像 → 策略 → 回测 → Walk-forward → 绩效分析 → 报告/前端。 架构 - 数据层与策略层分离;策略代码不写 SQL,只经 data/repo.py 取数 - 所有业务阈值集中在 config/*.yml,代码零硬编码(字段写错直接报错) - 报告只做「run_id → SQL → 渲染」,不做任何计算,数字可追溯 - 前后端分离:output/ 静态站点 + hdiv web 提供的 REST API 数据安全 - 只增不删:SQL 钩子拦截 DELETE/DROP/TRUNCATE,并有源码扫描测试守护 - qlib 原有表只读,本项目数据写入 hd_ 前缀表 - 回补使用 INSERT IGNORE,保证既有行零改动 - .env 存密钥且已 gitignore;output/、logs/、.venv/ 不入库 交付物 - 30 张 hd_* 表、7 个 YAML 配置、283 项自动化测试 - 统一 Web 前端(hash 路由 SPA)+ nginx 部署配置与 launchd 托管脚本 如实声明的限制 - 策略缺少稳定的样本外超额收益(Walk-forward 7 窗口均值 -0.95%, 基准 +2.29%);其价值体现在回撤控制,而非超额收益 - 涨跌停/停牌约束仅覆盖 2019 年起;index_weight 尚未填充 - AI Agent 层(plan.md 第四版 P8)未实现 详见 docs/user-guide.md 与 docs/implementation-status.md。
201 lines
6.9 KiB
HTML
201 lines
6.9 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block content %}
|
||
|
||
<div class="callout {{ 'ok' if overall == 'OK' else ('warn' if overall == 'WARN' else 'fail') }}">
|
||
<strong>总体结论:{{ overall }}</strong> —
|
||
共 {{ counts.OK + counts.WARN + counts.FAIL }} 项检查,
|
||
通过 <strong>{{ counts.OK }}</strong>,
|
||
警告 <strong>{{ counts.WARN }}</strong>,
|
||
失败 <strong>{{ counts.FAIL }}</strong>。
|
||
{% if fail_codes %}未通过项:<span class="mono">{{ fail_codes }}</span>{% endif %}
|
||
</div>
|
||
|
||
<div class="kpi-grid">
|
||
<div class="kpi">
|
||
<div class="label">检查项</div>
|
||
<div class="value">{{ counts.OK + counts.WARN + counts.FAIL }}</div>
|
||
<div class="note">覆盖缺口 G1–G6 与一致性</div>
|
||
</div>
|
||
<div class="kpi">
|
||
<div class="label">通过率</div>
|
||
<div class="value">{{ pass_rate }}</div>
|
||
<div class="note">{{ counts.OK }} / {{ counts.OK + counts.WARN + counts.FAIL }}</div>
|
||
</div>
|
||
<div class="kpi">
|
||
<div class="label">阻塞项 (FAIL)</div>
|
||
<div class="value {{ 'loss' if counts.FAIL == 0 else 'gain' }}">{{ counts.FAIL }}</div>
|
||
<div class="note">为 0 才可进入策略回测</div>
|
||
</div>
|
||
<div class="kpi">
|
||
<div class="label">审计编号</div>
|
||
<div class="value small mono" style="font-size:14px">{{ audit_id }}</div>
|
||
<div class="note">可在 hd_data_audit 表回溯</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>缺口总览</h2>
|
||
<div class="table-scroll">
|
||
<table class="data">
|
||
<thead>
|
||
<tr>
|
||
<th>检查码</th><th>检查项</th><th>结论</th><th>数据表</th>
|
||
<th style="text-align:left">说明</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in gap_results %}
|
||
<tr>
|
||
<td class="mono">{{ r.code }}</td>
|
||
<td style="text-align:left">{{ r.name }}</td>
|
||
<td><span class="badge {{ r.severity }}">{{ r.severity }}</span></td>
|
||
<td class="mono small">{{ r.table or '—' }}</td>
|
||
<td style="text-align:left" class="small">{{ r.message }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{% if coverage_rows %}
|
||
<div class="card">
|
||
<h2>数据覆盖率</h2>
|
||
<div class="table-scroll">
|
||
<table class="data">
|
||
<thead>
|
||
<tr>
|
||
<th>表</th><th>行数</th><th>起始</th><th>截止</th>
|
||
<th>交易日最少股票数</th><th style="text-align:left">目标</th><th>状态</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for c in coverage_rows %}
|
||
<tr>
|
||
<td class="mono">{{ c.table }}</td>
|
||
<td class="num">{{ c.rows }}</td>
|
||
<td class="num">{{ c.min or '—' }}</td>
|
||
<td class="num">{{ c.max or '—' }}</td>
|
||
<td class="num">{{ c.min_per_day if c.min_per_day is not none else '—' }}</td>
|
||
<td style="text-align:left" class="small muted">{{ c.target }}</td>
|
||
<td><span class="badge {{ c.severity }}">{{ c.severity }}</span></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="card">
|
||
<h2>分红数据明细(G1 · 高股息策略的数据基座)</h2>
|
||
{% if dividend_summary %}
|
||
<div class="kpi-grid">
|
||
<div class="kpi"><div class="label">覆盖股票</div><div class="value">{{ dividend_summary.symbols }}</div><div class="note">只</div></div>
|
||
<div class="kpi"><div class="label">可用分红记录</div><div class="value">{{ dividend_summary.valid }}</div><div class="note">div_proc=实施 且有除权/派息日</div></div>
|
||
<div class="kpi"><div class="label">报告期最早</div><div class="value" style="font-size:16px">{{ dividend_summary.min }}</div><div class="note">end_date</div></div>
|
||
<div class="kpi"><div class="label">报告期最晚</div><div class="value" style="font-size:16px">{{ dividend_summary.max }}</div><div class="note">end_date</div></div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if dividend_by_proc %}
|
||
<h3>按 div_proc 分布</h3>
|
||
<div class="table-scroll">
|
||
<table class="data">
|
||
<thead><tr><th>div_proc</th><th>记录数</th><th>占比</th></tr></thead>
|
||
<tbody>
|
||
{% for r in dividend_by_proc %}
|
||
<tr>
|
||
<td class="mono">{{ r.div_proc }}</td>
|
||
<td class="num">{{ r.n }}</td>
|
||
<td class="num">{{ r.share }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="callout">
|
||
口径说明(决策 D6):连续性判定只认 <code>div_proc = '实施'</code> 且存在
|
||
<code>ex_date</code> 或 <code>pay_date</code> 的记录,且以<strong>税前</strong>现金分红
|
||
<code>cash_div_tax > 0</code> 计一次分红。其余状态(预案/股东大会通过/股东提议)
|
||
保留在库中供研究,但不参与筛选判定。
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if dividend_by_year %}
|
||
<h3>按除权年度分布(近 15 年)</h3>
|
||
<div id="chart-div-year" class="chart short"></div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>全部检查结果</h2>
|
||
<div class="table-scroll">
|
||
<table class="data">
|
||
<thead>
|
||
<tr><th>检查码</th><th>检查项</th><th>结论</th><th>数据表</th><th style="text-align:left">说明</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in all_results %}
|
||
<tr>
|
||
<td class="mono">{{ r.code }}</td>
|
||
<td style="text-align:left">{{ r.name }}</td>
|
||
<td><span class="badge {{ r.severity }}">{{ r.severity }}</span></td>
|
||
<td class="mono small">{{ r.table or '—' }}</td>
|
||
<td style="text-align:left" class="small">{{ r.message }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<details>
|
||
<summary>查看各项检查的原始指标(metric_json)</summary>
|
||
<div class="table-scroll">
|
||
<table class="data">
|
||
<thead><tr><th>检查码</th><th style="text-align:left">指标</th></tr></thead>
|
||
<tbody>
|
||
{% for r in all_results %}
|
||
{% if r.metrics %}
|
||
<tr>
|
||
<td class="mono">{{ r.code }}</td>
|
||
<td style="text-align:left"><pre class="mono small" style="margin:0;white-space:pre-wrap">{{ r.metrics_pretty }}</pre></td>
|
||
</tr>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
{% if dividend_by_year %}
|
||
<script>
|
||
(function () {
|
||
var el = document.getElementById('chart-div-year');
|
||
if (!el || typeof echarts === 'undefined') return;
|
||
var chart = echarts.init(el);
|
||
var data = {{ dividend_by_year | json }};
|
||
chart.setOption({
|
||
color: {{ chart_colors | json }},
|
||
grid: { left: 60, right: 24, top: 32, bottom: 40 },
|
||
tooltip: { trigger: 'axis' },
|
||
xAxis: { type: 'category', data: data.map(function (d) { return d.year; }),
|
||
axisLabel: { color: '#64748B' } },
|
||
yAxis: { type: 'value', name: '分红实施次数', axisLabel: { color: '#64748B' },
|
||
splitLine: { lineStyle: { color: '#E9EEF6' } } },
|
||
series: [{
|
||
type: 'bar', data: data.map(function (d) { return d.n; }),
|
||
itemStyle: { color: '#1E40AF' }, barMaxWidth: 28
|
||
}]
|
||
});
|
||
window.addEventListener('resize', function () { chart.resize(); });
|
||
})();
|
||
</script>
|
||
{% endif %}
|
||
{% endblock %}
|