← 返回 Skill 列表
extension
分类: 数据与分析无需 API Key

通达信本地全市场扫描

基于本地通达信二进制数据的全市场技术形态扫描工作流。当用户要「全市场扫描」「跑一遍所有票」「周末筛选」「按形态/指标找出股票」「本地通达信数据怎么读」「tdx .day 文件解析」「板块共鸣」「bug/@他的票选通达信自选股」时使用。核心价值:绕过 MCP 逐只拉 K 线导致的上下文爆炸(成交额>1亿就有 2891 只),直接读 `C:\new_tdx` 本地文件,5259 只全量扫描约 10 秒、零 API 调用。内含四类缠论买点 + 板块共鸣的完整实现,以及通达信二进制格式的完整踩坑记录(极易丢失,务必照抄 scripts/tdx_reader.py,不要重写)。英文触发 scan all stocks, local tdx data, blocks file parse, china stock screener offline, chanlun scan.

person作者: user_43b0b86fhubcommunity

通达信本地全市场扫描

0. 前置条件(必须,缺一不可)

本技能强依赖本机通达信数据 —— 它不是能独立出结果的分析技能。

| 前置 | 说明 | |---|---| | 操作系统 | Windows(通达信客户端仅有 Windows 版) | | 已安装通达信 | 默认路径 C:\new_tdx;装在别处可用 ls -d /c/*/ \| grep -iE "tdx\|zd_\|tong" 找 | | 已下载盘后数据 | C:\new_tdx\vipdoc\sh\lday\ 下应有上万个 .day 文件。为空 → 提示用户先在通达信里做「盘后数据下载」 | | 可选:板块文件 | 板块共鸣需要 T0002\hq_cache\block_zs.dat 等;缺失时单票形态扫描仍可跑 |

反向说明(同样重要):

  • 不需要任何 API Key、不需要联网(除可选的 sync_tdx_official.py 拉官方数据)—— 所有计算都在本地二进制上完成,这正是它相对 MCP 逐只拉 K 线的核心优势;
  • 脚本几乎零第三方依赖:生产流水线只用 Python 标准库,无需 pip install。 唯一例外是 smoke_test.py(浏览器冒烟验收)需要 playwright——它不参与生产链路, 只有要自动验证看板渲染时才用得上;
  • 不依赖 MCP 连接器做扫描。tdx-connector 之类走的是另一条路(实时/单票),本技能走本地全量; 但若做盘中实时复核(非盘后扫描),需另装 WorkBuddy 的 westock-mcp(腾讯自选股) 连接器 拉实时报价/资金流——本地 .day 不含当日未收盘数据,盘中等级判断要联网补;
  • 没有通达信就用不了。此时应改走在线数据源(iFinD / westock / 东财), 或先请用户安装通达信并下载盘后数据——别硬跑,会得到空结果。

配套:算完形态要画图 / 判买点细节用 chanlun-kline。 本技能筛出候选,它负责单票结构计算与交互图——两者是「整车 vs 引擎」关系。

何时用

  • 需要全市场扫描(几百只以上)→ MCP 逐只拉 K 线必爆上下文,只能用本地数据
  • 用户装了通达信(默认路径 C:\new_tdx,可用 ls -d /c/*/ | grep -iE "tdx|zd_|tong" 找)
  • 盘后静态筛选、周末筛选、历史回算

先确认数据源存在:C:\new_tdx\vipdoc\sh\lday\ 应有上万个 .day 文件。若为空,说明用户没下载盘后数据,需提示他先做「盘后数据下载」。

为什么不能用 MCP

| 路径 | 结论 | |---|---| | westock tool_filter / data_kline | 批量 K 线返回体巨大,2891 只全拉直接爆上下文 | | ifind search_securities 自然语言选股 | 扛不住复合逻辑。"MACD底背离 + 创新低 + 市值>50亿" 这类多条件查询返回空,提示降低筛选难度 | | westock data_sector constituent 批量 | 服务端不稳定,会随机 service error |

→ 唯一可行路径:读本地二进制。 5259 只全量约 10 秒。

数据源同步:让 .day 每天自己更新(前置条件)

扫描 100% 依赖本地 .day,数据新鲜度就是一切。三条路实测结论(2026-09):

| 方案 | 结论 | |---|---| | 通达信自带定时下载(首选) | 官方、增量(每只加 1 根,约 170KB)、免费。开关在 C:\new_tdx\T0002\user.ini:TimeDown=1 开启,DownMin=960 = 16:00(分钟数),DownAllABGp=1 全 A/B 股。UI:系统设置 → 设置1 →「定时自动下载日线 16:00」 | | 官方全量包兜底 | https://data.tdx.com.cn/vipdoc/hsjday.zip,524MB,每交易日约 15:58 更新。不依赖通达信运行,适合出差/关机后补齐、换机重建 | | pytdx / mootdx / tdxpy | 已废,别再试。公开行情服务器 7709 已收紧:connect 和 get_security_count 能通,但 get_security_bars / get_security_quotes 一律返回空,mootdx 的 bestip() 也选不出可用节点 |

关键坑(都踩过)

  • 改 user.ini 必须先关闭通达信 —— 运行时改会被它退出时的内存配置覆盖回去。
  • 官方包地址挂在 TencentEdgeOne 后面带 JS 反爬:urllib / requests 一律被拦(返回 987 字节 EO_Bot_Ssid 混淆脚本,还伪装成 HTTP 200),只有 curl 能过(TLS 指纹差异)。Windows 自带 C:\Windows\System32\curl.exe,HEAD 也要用 curl -sI。
  • 定时下载要求 16:00 通达信在运行。要无人值守就用计划任务 15:55 拉起它(scripts/tdx_autostart.ps1),ps1 必须存成 UTF-8 with BOM —— 否则 PowerShell 5.1 按 GBK 解码中文会报莫名其妙的语法错误(报的位置完全不对,极具误导性)。
  • Windows 侧工具坑:schtasks.exe 常被沙箱程序黑名单拦截(不可重试、不得绕道),改用 PowerShell 的 Register-ScheduledTask;且 PowerShell 工具的 stdout 不回显,验证结果要写文件再读。
  • 覆盖 vipdoc 时只覆盖、不删除:官方包 12411 个品种(沪 5945/深 5857/京 609),本地通常 18536 个,差集多是债券回购(sh01xxxx、sh018xxx)。A 股实测零缺失(可以放心兜底),但删掉那些债券文件会影响通达信自身。
  • 下载实测 523.9MB 仅 10 秒,没必要长期留缓存;脚本把它放在系统临时目录,不进项目。

用法见 scripts/sync_tdx_official.py:无参=体检,--download 只下,--apply 覆盖,--apply --dry 演练,--force 忽略阻拦。内置防倒退校验(包内日期 < 本地就中止)。

二进制格式(踩坑记录,照抄不要重写)

1. 日线 vipdoc/{sh,sz,bj}/lday/*.day

  • 文件名带市场前缀:sh600519.day,取代码要剥掉前 2 位
  • 32 字节/条定长:date,open,high,low,close 各 uint32(÷100 得价格)、amount float32、volume uint32、reserved
  • struct.unpack("<IIIII", rec[:20])

2. 交易日历

  • ⚠️ 指数文件 sh000001.day 更新滞后(实测慢 2 个交易日),用它当日历会把大量正常票误判为停牌
  • ✅ 用一只全历史个股代替,如 sh600519.day

3. 股票档案 T0002/hq_cache/base.dbf(标准 dBaseIII)

  • 字段:SC(市场,单字符 0=深 / 1=沪 / 2=京,不是 "sh"/"sz")、GPDM、HY、LTAG(流通股本,单位万股)、SSDATE
  • 验证方法:平安银行 LTAG=1940568.50 万股 = 194 亿股 ✓
  • 解析:读 32 字节头拿 nrec/hlen/rlen,再从 32 循环读字段描述符直到遇到 0x0d

4. 股票名称 T0002/hq_cache/{shm,szm}.tnf

  • 定长 314 字节/条(相邻代码偏移恒为 314)—— 不是变长!
  • 代码在记录头,名称在代码末尾 +23 字节处(即 +6+17),GBK,0x00 结尾
  • ⚠️ 致命坑:必须按 市场+代码复合索引。shm.tnf 的 000039 是「上证信息」指数,若只按 6 位代码建 dict,会覆盖深市 000039(中航精装),导致名单里混进「上证周期」「科创芯片」这类指数名
  • ⚠️ 用全局正则扫 6 位数字会命中记录内部的其他数值(数值字段),读到串位垃圾。必须确认 +23 偏移落在有效范围内
  • 名称含全角空格(万 科A),需清洗 .replace("\u3000","").replace(" ","").replace("\uff21","A")
  • 上市次新标记:N=上市首日,C=未满 5 日 → 用 ^[NC][汉字] 过滤

5. 概念板块 T0002/hq_cache/block_gn.dat(自研 "Registry ver:1.0" 树形二进制)

此前误判为 Windows registry 不可解析,实际结构很简单:

名称(GBK) + 0x00 + uint16_le 成分数 + 0x00 0x02 0x00 + [6位ASCII代码 + 0x00] * n
  • 解析出 303 个板块,declared 与 parsed 零误差(以此自检是否解析正确)
  • 用 GBK 字节范围找中文名:[\xb0-\xf7][\xa1-\xfe],ASCII 用 [\x41-\x5a\x61-\x7a\x30-\x39]
  • ⚠️ 通达信成分上限 400 只,超出的大板块(国防军工 / 人工智能 / 消费电子 / 一带一路)成分被截断 → 渗透率虚高,报告必须标注
  • block_fg.dat 是风格/财务标签(融资融券、业绩预增、破净资产),不是资金主线,做板块共鸣必须剔除,只用 block_gn.dat(概念)

6. 板块指数名称 T0002/hq_cache/shs.tnf

  • ⚠️ 与 shm.tnf/szm.tnf 结构不同:这里是步长 360 字节、名称在代码末尾 +25(记录起点 +31)。 沿用 314/+23 会解析出 0 条;用 +30 也会差一个字节、结果全部为空
  • 确定偏移的通用方法:扫描「代码之后第一个非 0x00 字节」的众数偏移,实测稳定为 31
  • 覆盖 880/881/882 段 652 个板块,是画板块 K 线的唯一名称来源
  • 板块指数日线直接读 vipdoc/sh/lday/sh880xxx.day,格式同普通日线
  • ⚠️ 部分细分概念(三代半导、人形机器、可控核变…)在 shs.tnf 里没有对应指数,覆盖率约 87%, 界面上必须提示「通达信无对应板块指数」

7. incon.dat

T 码分类(T1001|银行),与 base.dbf 的 HY 数字码不是一套,别指望用它做行业映射。

工作流:缠论四类买点 + 板块共鸣

缠论结构本身(包含→分型→笔→中枢→背驰→四类买点的算法与画图)已抽成独立技能 chanlun-kline,那里有可直接复用的 scripts/chanlun_core.py、 scripts/kline_canvas.js、scripts/case_svg.py。 本节只讲它在全市场扫描流水线里怎么串。

核心纪律:四类信号必须彻底分表统计,不能混在一个池子里。 已用数据验证 —— 四类 P0 相差数十倍, 混算只能用单一 P0,渗透率倍数全部失真。Top10 Jaccard:一类vs二类=0.000、二类vs全部=0.000、 一类vs突破=0.176(唯一有意义的跨榜组合:同时具备"底部结构+突破"的交接板块)。

cd <工作目录>
python chanlun_scan.py     # ① 全市场结构扫描 -> chanlun_hits.csv(约 10 秒)
python fund_flow.py        # ② 全市场资金流 -> fund_flow.json(约 50 秒,可缓存复用)
python resonance4.py       # ③ 四类分别统计 + 末端资金确认 -> 4 CSV + resonance_data.json
python export_kdata.py     # ④ 导出 K 线 -> kdata.json
python build_dashboard.py  # ⑤ 渲染单文件看板 -> chanlun_dashboard.html
python export_tdx_blocks.py            # ⑥ 生成 .blk(先 --install 需关闭通达信)

资金必须在「末端」确认,绝不能前置过滤(本流水线最大的设计坑): resonance4.py 的 FUND_MODE 默认 "post" —— 共鸣榜按结构统计(N / 渗透率 / 时间系数 不受资金影响),资金只作为「能不能下手」的确认标记。

  • ❌ FUND_MODE="pre"(前置砍掉 d5≤0 的票再统计共鸣)会制造统计事故: 二类买点 13 只 × 全市场 d5>0 占比 32.7% ≈ 4 只,4 只票要凑够同板块 N≥3,概率接近 0 —— 结果就是二类买点整类消失。这不是市场没有二类,是被过滤顺序抹掉的。 对大样本类别(中枢突破 246 只)无害,所以这个 bug 极具隐蔽性,只会杀死小样本类别。
  • ✅ post:结构榜完整保留,每只票带 fund_ok,每个板块带 n_fund(命中股中资金为正的数量) 与 confirmed(n_fund >= CONFIRM_MIN_FUND,默认 2)。可交易层在 export_tdx_blocks.py 的 CLPICK 补齐:板块 fnet>0 + 广度≥40% + confirmed + 个股 fund_ok。

资金面要点:

  • 数据源必须走东方财富资金流接口:通达信本地 vipdoc 下没有 zjlx 目录,拿不到资金流
  • 字段:f62 今日主力净额、f164 5日主力净额、f165 5日净占比(f165 是百分比不是金额,别混)
  • 板块级资金用全成分口径(不是命中股口径——命中股已被过滤必为正,没有信息量): fnet = 板块全部有效样本 5 日净额合计(可为负)、fbread = 净流入为正的成分占比
  • 共鸣分高但板块资金为负 = 结构到位、资金不认,必须降级——这是本流水线最核心的判定

看板(推荐交付形态):四类各自成卡片、点击板块名弹出板块指数 K 线(并列出该板块命中个股)、 点击个股名弹出个股日线,自动叠加 MA5/10/20、中枢 ZG/ZD 区间、止损位、买点位置竖线。 全部数据内嵌、零 CDN,双击即可离线打开。

弹窗导航(必须实现,否则用户会迷路)——三级可回溯:

  • ⚠️ 派生名单必须一起导 K 线:二类候选池这类名单不在 resonance_data.json 的 sectors 里, export_kdata.py 要单独把它们读进来 pack(),否则看板上"能看见点不动"—— 用户点个股名只会弹 alert('该标的不在本次K线数据范围内')。
  • ⚠️ 同一只票可能命中多种信号(既是「一类买点」又是「二类候选」), STK 必须按信号类型分桶存 notes / stops / zgs / dates,弹窗用 pick(field, 当前ty) 取值。 只存一份的话,点「二类候选」会看到一类的话术("底背驰"),语义直接错乱。
  • ⚠️ 候选/命中要两张表(SECSTK / WSECSTK):面包屑"该股同属"要合并两家, 但板块弹窗的"命中个股"不能混入候选(候选不是买点)。
  • ⚠️ details 折叠块 + [data-sec] 会與榜单选择器冲突:冒烟测试里 pg.click('[data-sec="XX"]') 因 strict mode 命中多个而失败,要写成 .card table:not(.mini) [data-sec=...]。
  • 面包屑 ← 返回 <板块名> › <个股名>,点击返回;Backspace 等效(Esc 仍是直接关闭)
  • 个股弹窗内保留同板块其他命中个股 chips,当前项 .on 高亮,可在个股间横向切换
  • 个股若同属多个板块,面包屑里额外列出(最多 3 个),点哪个跳哪个
  • 实现要点:STK2SEC 由 SECSTK 反查(同一股在同一板块可能命中多种信号,必须按 code 去重); 导航栈 NAV=[{f:openSec,a:[sec]}],个股层的上级永远是板块层

术语区:页面底部折叠的「术语速查」(ZG/ZD/中枢/渗透率/共鸣分), 个股弹窗 kv 行也直接列出 ZG/ZD 数值并带 title 提示——用户第一次看必然问这两个缩写是什么意思。

必做冒烟(K 线渲染坑多,务必验证 Canvas 真的画出内容):

# 用系统 Python(已装 playwright + 浏览器),managed python 没有
python smoke_test.py chanlun_dashboard.html

它检查:卡片数、板块弹窗、个股弹窗、三类买点中枢带、Esc 关闭、控制台报错, 以及导航链路(面包屑点击返回 / Backspace 返回 / 同板块个股横向切换)。 用 getImageData 采样判断画布非空——只看 DOM 存在不算通过。

共鸣强度公式

Score = N × (P / P0) × T_adj
N     板块内命中数,主榜门槛 N ≥ 3(**含 3**,代码是 `if N < MIN_HITS: continue`,不是 >3)
P     渗透率 = 命中数 / 板块有效样本数
P0    该类信号的全市场基准命中率(四类各自独立!)
T_adj ≤10日 1.0 / 11~15日 0.6 / 16~25日 0.3 / 26~30日 0.15 / >30日 0

主榜 / 观察区必须分区:WATCH_HITS = 2 —— N==2 的板块进观察区并打 watch 标记, 排在正式榜之后,不进 Jaccard 重叠矩阵、不进通达信自选股。 不分区的话,小样本类别(二类只有个位数)会被 N=2 的凑数项"救活",看板看起来有内容, 实际是拿不到共鸣门槛的线索 —— 这会掩盖"该类信号当前确实没有板块效应"这个真实结论。

信号定义要点(易错)

  • 一类买点:下跌 MACD 面积背驰 + 创新低 + 缩量。左侧,仓位最小
  • 二类买点:必须是「确认 → 回踩 → 不破」完整结构 —— 先有 ≥3% 反弹,再回踩不破一类低点。 ⚠️ 只要求"没创新低"是不完整的,会把横盘误判为二类 ⚠️ 信号日期必须取回踩低点当日,不能用最后一根 K 线 —— 否则所有二类 span=0、时间系数全满分 🔴 最容易漏的时序校验(v5 修复):算出 peak_rel(反弹高点相对一类低点的偏移)后, 必须与回踩低点位置 j 比较,写成 j > i_b1 + peak_rel。 只写 peak_rel > 0(即定义了却没参与比较)= 要件形同虚设:一类低点刚过、还在磨底的低点 会被当成"回踩低点",那不是回踩是没起来。实测这个漏判会把伪二类从 6 只放大到 33 只(+450%)。
  • 二类天然稀少,别急着调参:二类依赖"一类后先反弹再回踩"这个两段式结构,与一类在时间上错开。 典型漏斗(2026-09-12 全市场 5259 只):一类 392 → 过反弹门槛 365 → 过回踩时序 73 → 不破前低 6。 中间 292 只(75%)是"一类后一路上行、还没回踩",它们才是二类的预备弹药库 —— chanlun_scan.py 已单独输出为 chanlun_2buy_watch.csv(不进四类分表,导出通达信 CL2W)。 所以"二类为 0"往往说明回踩还没发生(一类刚集中爆发),而不是参数太严。 ⚠️ 候选池必须排除现价已跌回一类低点下方的票(close > low1)—— 那是一类买点失败, 不是"待回踩"。实测 91 只里有 21 只(23%)属于这类,混进来会严重污染候选池质量。
  • 候选池的板块分布 = 二类买点的前瞻指标:候选越集中、资金越正的板块, 回踩一旦发生就会率先批量转化为二类。这是"二类当前为 0"时唯一有预测价值的输出, 比盯着空的二类榜有用得多。
  • 中枢突破:与前两类互斥在后;属突破交易,需板块共振
  • 三类买点:突破后回抽不落回中枢,四个要件缺一不可(v4 起收紧): ① 回抽低点 > ZG ② 真实回撤 ≥5%(排除"刚突破还没回调") ③ 回踩低点须在突破高点之后(时序) ④ 现价距 ZG ≤30%(涨太远则止损太远,风险收益比不成立) ⚠️ 信号日期取回抽低点当日,不是突破日

已知局限(每次交付必须声明)

  1. 结构筛选器固有偏好:突破 / 三类天然偏向低波动、长期横盘的票(银行 / 高速 / 公用), 会"像主线"但实际只是适合被这种几何捕捉到。渗透率归一化只能削弱,无法消除 (实测:叠加资金硬过滤后,这类板块在四张榜 Top20 里全部消失 —— 双过滤确实修掉了这个偏差)
  2. 资金流是单日快照:5 日窗口偏短,单日巨量异动会被放大;要更稳可叠 10 日
  3. 板块资金为负 ≠ 一定下跌:它代表主力在减仓,是降级依据,不是做空依据
  4. 千人千缠 + 重绘:笔/线段划分带主观性,中枢会随新数据回溯改写。实盘信号不稳定
  5. 报告输出需匹配 IDE 主题,light 主题禁止用深色背景

scripts/

| 文件 | 用途 | |---|---| | tdx_reader.py | 本地数据读取模块(日线 / 档案 / 名称 / 概念板块 / 板块指数),含全部踩坑注释,直接复用 | | chanlun_scan.py | 缠论结构扫描 → chanlun_hits.csv(含三类买点四要件收紧) | | fund_flow.py | 全市场主力资金流抓取(东财批量接口)→ fund_flow.json | | resonance4.py | 四类分别统计的共鸣引擎(末端资金确认,主榜 N≥3 / 观察区 N=2 分区) | | export_kdata.py | 导出板块 + 个股 K 线(列式紧凑存储)→ kdata.json | | export_tdx_blocks.py | 导出通达信自定义板块(.blk + blocknew.cfg),含三态运行检测 | | build_dashboard.py | 主交付:单文件看板(四类卡片 + Canvas K 线弹窗) | | build_report.py | 旧版 tab 式 HTML 报告(保留对照) | | smoke_test.py | 看板浏览器冒烟(需额外装 playwright),用法 python smoke_test.py [看板HTML] [截图目录],两者默认取 TDX_SCAN_OUT 或当前目录 | | sync_tdx_official.py | 官方全量日线包兜底同步(必须用 curl 下载,防倒退 + 只覆盖不删除) | | tdx_autostart.ps1 | 配合计划任务 15:55 拉起通达信(需 UTF-8 with BOM),好让 16:00 定时下载触发 |

所有脚本与环境/个人路径无关,两个输入输出变量都走环境变量:

| 变量 | 作用 | 默认 | |---|---|---| | TDX_HOME | 通达信安装目录(数据源) | C:\new_tdx | | TDX_SCAN_OUT | 输出目录(中间文件与看板都落这里) | 当前工作目录 |

流水线各脚本必须共用同一个输出目录——它们通过 resonance_data.json、 chanlun_hits.csv 等中间文件串联。想换目录就把两个脚本放在同一目录下运行, 或统一设 set TDX_SCAN_OUT=D:\scan 后再依次执行。

必须按 chanlun_scan → fund_flow → resonance4 → export_kdata → build_dashboard 顺序执行。

回流到通达信(可选收尾,用户是通达信用户时强烈建议做)

把四类命中直接写成通达信自定义板块,看盘时一键调用。格式已从本地现有文件逆向确认:

  • .blk 成分文件:纯文本 GBK,每行 7 字符 = 市场前缀(1=沪 0=深)+ 6 位代码,行尾 CRLF (例 1601566\r\n0000852\r\n)
  • T0002/blocknew/blocknew.cfg:定长 120 字节/条 [0:50] 名称 GBK 补 0x00 [50:120] 板块代码 ASCII(= .blk 文件名) 校验方法:文件长度必须能被 120 整除(现有 1560 = 13 条,与 NZ/DNQD/WPXG 完全对应)
  • 追加新板块 = 备份 cfg → 末尾 append 120 字节记录 → 回读验证「原始记录逐字节未变」

⚠️ 两个必须记住的坑:

  1. 通达信运行时不能写 cfg(退出时会用内存配置覆盖)。但不要用 try/except -> return False 做进程检测: 沙箱/受限 shell 里 tasklist 可能「返回码 0 但看不到宿主进程」,于是乐观判定"未运行"而误写。 必须做三态检测:running / not_running(命令跑通且输出里有其他进程)/ unknown, 且 unknown 时一律不写,要求显式 --force。判断"未运行"的充分条件应包括 「输出行数 > 5」这类旁证,不能只看 returncode。
  2. 只在 cfg 真的要变更时才备份,否则反复执行会堆一堆备份文件。
python export_tdx_blocks.py            # 只生成到 ./tdx_blocks/(安全)
python export_tdx_blocks.py --install --force   # 关闭通达信后写入(--force 表示已知悉)
python export_tdx_blocks.py --uninstall --force # 一键移除(cfg 记录 + .blk 文件)

外部接口踩坑(东财资金流)

  • ⚠️ push2.eastmoney.com 连续批量请求几十次后会 RemoteDisconnected 封禁,而且是全节点一起封 (82./1./48. 等所有 push2 前缀都一起失效)。必须用 push2delay.eastmoney.com(延时节点), 资金流是收盘统计值,延时节点完全够用
  • 批量用 api/qt/ulist.np/get,secids 形如 1.600519,0.000001(1=沪 0=深), 单批 50 只、间隔 0.25s、pz 必须等于 secids 个数,否则返回不全
  • 请求头带 Referer: https://quote.eastmoney.com/ 更稳
  • 失败重试时轮换节点,不要死磕同一个

历史归档(按季度分级)+ 看板日历回看

主产物 chanlun_dashboard.html 是固定文件名、每次覆盖。不归档的话上周榜单就永久 丢失,无法回答「当时的主线是什么」。build_dashboard.py 已内置归档 + 回看。

两级存储

| 文件 | 内容 | 体积 | |---|---|---| | history/lite_{YYYYMMDD}.json | 本期榜单(full:Top10 板块 + 每板块前 8 只个股) | 约 40 KB | | history/lite_{YYYYMMDD}.json | 跨季度后降级 mini:仅 Top8 板块名 + 核心指标 | 约 5 KB | | history/dashboard_{YYYYMMDD}.html | 完整看板快照(仅本季度保留) | 约 3 MB | | history/snap_{YYYYMMDD}.json | 周报用(候选池 + Top10 板块名,由 weekly_report.py 写) | 约 8 KB |

save_and_prune() 每次运行做三件事:

  1. 写本期 lite(full)
  2. 把非本季度的 lite 降级为 mini(删掉 stocks 字段)—— 实测 40KB → 5.4KB
  3. 删除非本季度的 dashboard_*.html

看板日历

顶部「历史回看」区块:按月展示、有快照的日期高亮可点(本季度蓝色、跨季度灰蓝、 当期带橙色内框)、‹ › 切月、回到最新 复位。

点击历史日期会同时替换 #hero(四类概览卡)和 #cards(四张榜单)—— 两处必须联动,否则会出现「顶部是最新、下方是历史」的自相矛盾。

踩坑

  • ⚠️ kdata.json 的 dates 是整数(20260911)不是字符串,直接 len()/切片会抛 object of type 'int' has no len()。统一走 ymd() 转字符串
  • ⚠️ 用数据日期命名快照,不用当天日期。通达信没下载盘后数据时,今天跑出来的 仍是上一交易日结果,用今天命名会造成「以为跨越了一周」的假象
  • ⚠️ main() 里 d 已经是 resonance_data,归档时不要复用变量名 d
  • main() 里变量 CURDATE / HIST / HISTLV / HISTSTAT 由 Python 注入为全局, 冒烟脚本可直接 window.CURDATE 取值
  • 日历走了独立的 IIFE + 第二个 <script> 块,靠 document 上的事件委托工作, 因此替换 innerHTML 后无需重新绑定(原 JS 也是委托在 document 上的,别改成直接绑定)

冒烟必须覆盖

日历切换要验证「概览 + 榜单一起变」:记录切换前 #hero .stat .v,点历史日期后 对比是否变化,再点 回到最新 验证能否复位。只检查日期格子存在是不够的。