ETF 平台突破形态分析
核心概念
平台突破:ETF 在某个价格水平多次震荡(形成阻力位),经过整理后放量突破,是强势上涨信号。
判断逻辑(80/20 框架):
- 前 80% K 线:寻找"平台高点"(被多次触及的最高价位)
- 后 20% K 线:验证是否有效突破
使用步骤
0. ETF清单配置(重要)
默认分析范围:95个ETF(存储在 references/default_etf_list.json)
- 默认行为:如果不指定分析范围,自动使用这95个ETF
- 自定义范围:可通过以下方式指定:
- 修改
references/default_etf_list.json文件 - 使用
--etf-list参数指定自定义清单 - 使用
--all参数分析所有可用的数据文件
- 修改
ETF清单格式(JSON数组):
[
{"code": "513730", "name": "东南亚科技", "market": "sh"},
...
]
1. 下载 K 线数据(首次或数据过期时)
腾讯财经 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会自动截断到最近交易日,无需手动修改。
2. 运行分析
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 线数据)
3. 补充最新数据(数据不是最新时)
当已有数据文件但不是最新日期时,用此脚本仅补充缺失的K线天数:
cd <workspace>
python scripts/update_recent_data.py
此脚本读取现有
akshare_{market}{code}.json,从最新日期的次日开始, 用腾讯财经API追加数据,只更新必要的文件,速度远快于重新下载。
4. 生成 HTML 报告
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邮箱中可正常显示)
评分标准(0-15 分)
评分维度
| 维度 | 满分 | 说明 | |------|------|------| | 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 中有说明)
已知问题与修复记录
fetch_kline_akshare.py:腾讯API为主源重写(2026-05-10)
问题:原脚本只使用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
update_recent_data.py:新增补充数据脚本(2026-05-10)
场景:已有数据文件但不是最新日期时,无需重新下载全部数据。
功能:读取现有JSON文件 → 提取最新日期 → 用腾讯API追加缺失天数 → 写回文件。 只更新必要的文件,速度远快于重新下载。
analyze.py:ETF代码提取bug修复(2026-05-10)
问题:从文件名提取ETF代码时,base[8:] 没有正确去掉 sh/sz 前缀,导致代码错误。
修复:改为 base[8+2:](即 base[10:]),正确提取6位代码。
- 涉及文件:
akshare_sh510300.json→ 代码应为510300(去掉前10个字符)
gen_report_static.py:Two bugs fixed(2026-05-10)
Bug 1:副标题日期硬编码
问题:报告副标题写死 数据截至2026-04-30,与实际数据日期不符。
修复:从 full_scan_result.json 的所有K线数据中动态提取最新日期,填入副标题。
Bug 2:AA级表格只显示前15个
问题:代码 aa_list[:15] 硬截断,AA级共19个但表格只列15个,
科创芯片(588200)排第19名被截断看不到;标题写"共19个"但表格只有15行,互相矛盾。
修复:去掉 [:15] 限制,表格显示全部AA级标的。
gen_report.py K线图左侧空白(2026-05-04修复)
问题:报告中的K线图左侧大面积空白,数据未填满图表。
根因:
- K线数据
time字段使用日期字符串'2025-09-01',Lightweight Charts v4 优先使用数字时间戳 - 图表初始化时容器
clientWidth可能为0,导致fitContent()计算错误 setVisibleRange传入字符串而非数字时间戳
修复内容:
- K线数据
time改为 Unix 时间戳整数(date_to_ts()函数) - JS 初始化增加
setInterval轮询,等待容器有宽度再初始化 setVisibleRange使用数字时间戳from/to- 增加
rightOffset: 3和barSpacing: 5让K线紧密排列
腾讯财经API字段兼容性
问题:部分ETF的K线数据返回字段名为 day 而非 qfqday,导致解析失败。
修复:优先读取 qfqday,若为空则降级读取 day:
klines = data.get('qfqday', []) or data.get('day', [])
报告声明与代码规则不一致(2026-05-04修复)
问题:网页顶部声明的分析方法与代码实际逻辑不一致:
- 声明写"前60%/后40%",代码实际是"前80%/后20%"
- 声明写"AA≥13分",代码实际是"AA≥11分"
- 声明写"B+≥7分",代码实际是"B+≥6分"
- 未声明中间段压制加分(+1分)和触及次数≥2过滤条件
修复:以代码为准,更新 gen_report.py 中所有声明文字,与 analyze.py 实际逻辑完全一致。
Scan to join WeChat group