平台突破:ETF 在某个价格水平多次震荡(形成阻力位),经过整理后放量突破,是强势上涨信号。
判断逻辑(80/20 框架):
默认分析范围:95个ETF(存储在 references/default_etf_list.json)
references/default_etf_list.json 文件
--etf-list 参数指定自定义清单
--all 参数分析所有可用的数据文件
ETF清单格式(JSON数组):
[
{"code": "513730", "name": "东南亚科技", "market": "sh"},
...
]
腾讯财经 API(主源,推荐):稳定可靠,无需任何依赖,95个ETF全部成功
cd <workspace>
python scripts/fetch_kline_akshare.py # 腾讯为主,AKShare备用
AKShare(备用):可获取更多数据(约160根),但今日曾全部连接失败
cd <workspace>
python scripts/fetch_kline_akshare.py --akshare-only # 强制只用AKShare
> 数据源策略:脚本优先使用腾讯财经API(今日100%成功),失败时自动降级到AKShare。
> end_date 动态计算为"今日+30天",AKShare会自动截断到最近交易日,无需手动修改。
cd <workspace>
python scripts/analyze.py # 默认:分析95个ETF
python scripts/analyze.py --all # 分析所有可用数据文件
python scripts/analyze.py --etf-list custom.json # 使用自定义ETF清单
输出:
full_scan_result.json:全量分析结果
scan_candidates.json:候选标的(含 K 线数据)
当已有数据文件但不是最新日期时,用此脚本仅补充缺失的K线天数:
cd <workspace>
python scripts/update_recent_data.py
> 此脚本读取现有 akshare_{market}{code}.json,从最新日期的次日开始,
> 用腾讯财经API追加数据,只更新必要的文件,速度远快于重新下载。
cd <workspace>
python scripts/gen_report.py
输出:etf_breakout_full_scan.html
QQ邮箱兼容版(静态图片,无需JS):
cd <workspace>
python scripts/gen_report_static.py
输出:etf_breakout_full_scan_static.html(K线图预渲染为静态图片,base64嵌入,QQ邮箱中可正常显示)
| 维度 | 满分 | 说明 |
|------|------|------|
| A. 平台质量 | 6分 | 触及次数(3分) + 持续时间(2分),非真实平台减半 |
| B. 突破力度 | 5分 | 创新高+站稳+突破幅度 |
| C. 趋势量能 | 4分 | 整体趋势(2分) + 量比(2分) |
| D. 中间段压制 | 1分 | 平台高点后15根K线持续受压制 |
| 分数 | 评级 | 附加条件 |
|------|------|----------|
| ≥ 11 | AA | 且 pattern_type == "平台突破" |
| ≥ 9 | A | 且 pattern_type in ("平台突破", "微幅突破") |
| ≥ 6 | B+ | 且 pattern_type in ("平台突破", "微幅突破", "平台蓄势") |
| ≥ 4 | B | — |
| ≥ 2.5| C+ | — |
| < 2.5| C | — |
评分 ≥ 5
且 形态类型 in ("平台突破", "微幅突破", "平台蓄势")
且 平台触及次数(±2.5%) ≥ 2 ← 隐藏规则,报告中已声明
| 参数 | 当前值 | 说明 |
|------|---------|------|
| 平台定义区间 | 前 80% K 线 | analyze.py platform_end = int(n * 0.80) |
| 触及区间 | ±2.5% | 判断"触及平台高点"的容忍度 |
| 站稳判断 | 最近3日均价 > 平台高点×0.995 | 避免单日假突破 |
| 中间段定义 | 平台末尾前15根K线 | 验证平台高点后的压制确认 |
references/
default_etf_list.json # 默认95个ETF清单
algorithm_notes.md # 算法细节和已知问题
scripts/
fetch_kline_akshare.py # 下载 K 线(腾讯为主,AKShare备用)
update_recent_data.py # 仅补充最新K线(腾讯API)
analyze.py # 核心分析脚本(支持--all和--etf-list参数)
gen_report.py # 生成 HTML 报告(含K线图,需JS)
gen_report_static.py # 生成静态HTML报告(matplotlib渲染K线图)
收紧标准(减少假阳性):
touches_25pct 阈值(analyze.py 评分A部分)
platform_duration 要求
is_real_platform 的趋势/振幅阈值
放松标准(增加候选):
is_real_platform 判断(algorithm_notes.md 中有说明)
问题:原脚本只使用AKShare,今日(2026-05-10)AKShare全部连接失败(RemoteDisconnected),0/95成功。
修复:重写 fetch_kline_akshare.py,优先使用腾讯财经API(今日95/95成功),失败时自动降级到AKShare。
end_date 改为动态计算(今日+30天),不再需要手动修改
akshare_{market}{code}.json),向后兼容 analyze.py
场景:已有数据文件但不是最新日期时,无需重新下载全部数据。
功能:读取现有JSON文件 → 提取最新日期 → 用腾讯API追加缺失天数 → 写回文件。
只更新必要的文件,速度远快于重新下载。
问题:从文件名提取ETF代码时,base[8:] 没有正确去掉 sh/sz 前缀,导致代码错误。
修复:改为 base[8+2:](即 base[10:]),正确提取6位代码。
akshare_sh510300.json → 代码应为 510300(去掉前10个字符)
问题:报告副标题写死 数据截至2026-04-30,与实际数据日期不符。
修复:从 full_scan_result.json 的所有K线数据中动态提取最新日期,填入副标题。
问题:代码 aa_list[:15] 硬截断,AA级共19个但表格只列15个,
科创芯片(588200)排第19名被截断看不到;标题写"共19个"但表格只有15行,互相矛盾。
修复:去掉 [:15] 限制,表格显示全部AA级标的。
问题:报告中的K线图左侧大面积空白,数据未填满图表。
根因:
time 字段使用日期字符串 '2025-09-01',Lightweight Charts v4 优先使用数字时间戳
clientWidth 可能为0,导致 fitContent() 计算错误
setVisibleRange 传入字符串而非数字时间戳
修复内容:
time 改为 Unix 时间戳整数(date_to_ts() 函数)
setInterval 轮询,等待容器有宽度再初始化
setVisibleRange 使用数字时间戳 from/to
rightOffset: 3 和 barSpacing: 5 让K线紧密排列
问题:部分ETF的K线数据返回字段名为 day 而非 qfqday,导致解析失败。
修复:优先读取 qfqday,若为空则降级读取 day:
klines = data.get('qfqday', []) or data.get('day', [])
问题:网页顶部声明的分析方法与代码实际逻辑不一致:
修复:以代码为准,更新 gen_report.py 中所有声明文字,与 analyze.py 实际逻辑完全一致。
共 3 个版本