上期所查库(SHFE 行情与仓单查询)
从上海期货交易所官网公开报表接口获取库存仓单和价格走势数据,整理为表格与图表。
一、快速用法(普通用户先看这里)
> 一句话上手:告诉它"查哪个品种、查多久",例如"近一个月沪铜仓单和价格"。
常用命令示例
单品种、近 N 天(默认 20)仓单 + 价格 —— 默认只出表格,不画图(要图才加 --chart / --chart-html):
python3 scripts/fetch_shfe.py --product cu --days 20
半年走势(约 126 个交易日)并出图:
python3 scripts/fetch_shfe.py --product cu --days 126 --chart <输出路径>.png
指定起止日期(两年窗口也支持):
python3 scripts/fetch_shfe.py --product au --start 20241023 --end 20261023 --chart-html <输出路径>.html
同时查多个品种(对比仓单与价格的变化方向和幅度):
python3 scripts/fetch_shfe.py --product cu,al,zn --days 20
python3 scripts/fetch_shfe.py --product 沪铜 螺纹钢 黄金 --days 20
多品种支持逗号、空格、顿号、斜杠分隔,且可混用中英文(--product 沪铜 cu 黄金);输出各品种明细表 + 末尾「多品种对比汇总」。
参数速查
| 参数 | 说明 |
|---|---|
--product |
品种代码或中文名,可传多个:cu、cu al、cu,al、沪铜、沪铜 沪铝。见 references/products.md |
--days N |
最近 N 个交易日(默认 20) |
--start / --end |
起止日期 YYYYMMDD,可与 --days 并用 |
--chart 路径.png |
输出静态走势图(双 Y 轴:左仓单折线、右价格日K线,红涨绿跌)。仅单品种 |
--chart-html 路径.html |
输出交互图(可缩放/悬浮;价格同为日K线,悬浮显示开高低收)。仅单品种 |
--price-line |
价格改用结算价折线(默认已是日K线蜡烛图,此参数用于切回旧样式) |
--detail |
附最近交易日分仓库明细 |
--ohlc |
表格额外显示开盘价/最高价/最低价列(默认仅显示收盘价与结算价) |
--price-only |
只查价格(跳过仓单) |
--contract 2612 |
强制指定合约月份;默认每日按持仓量最大自动选主力。指定时绕过缓存(缓存只存主力口径),每次联网 |
--continuous |
主连口径(同 --main):逐日拼接主力收盘价、不显示合约列、不按换月分段,区间涨跌直接首末相减。想"就和行情软件主连一样"时用它 |
--no-cache |
跳过本地缓存,强制联网取数(成功后仍写回缓存) |
--clear-cache |
清空本地缓存后退出 |
--repair-cache |
清理旧版本遗留的脏缓存(空仓单 / 错合约价格)后退出 |
> 代码提示:--product 现在也认中文名(沪铜/沪金/螺纹/原油/20号胶 等)。但代码仍须精确——输错成另一个已收录品种(如 ru 打成 rb)会返回那个品种的真实数据且名称单位都对,极易误读;完全无法识别时(如 zzz)整条命令直接报错退出,不会只查一半。
> 图表功能为可选依赖:--chart 需 matplotlib、--chart-html 需 plotly(均非标准库)。未安装时脚本跳过图表并提示安装,不影响数据与表格。安装:pip install matplotlib plotly。
查询结果长什么样
以下全部是真实运行输出(v1.8.0,2026-10-28 实测,可逐字对照)。
① 标准输出:--product cu --days 5
### 铜(CU)· 来源:上海期货交易所官网
> 库存口径:2025-11-17 起官网统一仓单日报(all.html),更早为旧版汇总(dailystock.dat);单位按品种自然口径(吨)。价格全程覆盖。
> 价格口径:每日取当日**持仓量最大的合约(主力)**,合约月份单列;换月日的跨日涨跌含跨月价差,**不可直接相减**。成交量/持仓量为主力合约口径。
| 日期 | 仓单量(吨) | 仓单变动 | 合约 | 收盘价(元/吨) | 结算价(元/吨) | 成交量 | 持仓量 |
|------|--------|----------|------|--------|--------|--------|--------|
| 20261022 | 23,787 | | 2612 | 107,590 | 108,080 | 88,191 | 181,612 |
| 20261023 | 19,058 | -4,729 | 2612 | 107,900 | 107,310 | 111,694 | 183,779 |
| 20261026 | 16,219 | -2,839 | 2612 | 108,110 | 108,180 | 88,952 | 190,675 |
| 20261027 | 14,593 | -1,626 | 2612 | 108,390 | 108,640 | 72,456 | 191,199 |
| 20261028 | 12,620 | -1,973 | 2612 | 109,200 | 109,200 | 79,060 | 195,587 |
**区间统计**
- 仓单:期初 23,787 → 期末 12,620(累计 -11,167 吨),区间最大 23,787 / 最小 12,620 吨
- 主力 2612:20261022 → 20261028,收盘 107,590 → 109,200(+1.50%)
- 结算价:期初 108,080 → 期末 109,200(+1,120),区间最大 109,200 / 最小 107,310
- 数据截至:20261028(每日约 15:15 后更新,日盘 15:00 收盘)
怎么读:前三列是仓单(量/日变动),第四列是合约月份,中间两列是价格,最后两列是成交与持仓;末尾自动附区间统计。
⚠️ 合约列是必读项,不是装饰。v1.8.0 之前脚本取的是「该品种第一个匹配行」,也就是最近交割月而不是主力;移仓前两者恰好相同,移仓后就会悄悄换成即将摘牌的合约,并在换月当天凭空造出一根假阴线。现在改为按持仓量选主力,并把月份显式打出来,换月那天标 ⚠换月。
⚠️ 当日仓单尚未发布(15:15 前)时,仓单列是 -、变动写「无数据」,价格与量仓照常——这是正常表现,不是出错,也不会用前一天的数字冒充。
② 单位随品种自动切换:--product au --days 3
### 黄金(AU)· 来源:上海期货交易所官网
| 日期 | 仓单量(千克) | 仓单变动 | 合约 | 收盘价(元/克) | 结算价(元/克) | 成交量 | 持仓量 |
|------|--------|----------|------|--------|--------|--------|--------|
| 20261026 | 116,904 | | 2612 | 863 | 862 | 249,122 | 194,392 |
| 20261027 | 116,904 | 0 | 2612 | 861 | 864 | 200,923 | 191,290 |
| 20261028 | 116,904 | 0 | 2612 | 867 | 870 | 197,994 | 188,406 |
黄金是千克 / 元每克,铜是吨 / 元每吨——表头会跟着品种变,不需要你换算。仓单变动 = 0 是真实结果(当日出入库持平),不是缺失。
②b 区间内换月时,涨跌必须分段给:--product al --start 20260928 --end 20261028
**区间统计**
- 仓单:期初 138,188 → 期末 61,888(累计 -76,300 吨),区间最大 138,188 / 最小 61,888 吨
- ⚠️ 区间内主力合约切换 **2 段**,跨段涨跌不可比,按段拆分:
- **2611**:20260928 → 20261015,收盘 23,950 → 23,040(-3.80%)
- **2612**:20261016 → 20261028,收盘 22,920 → 23,540(+2.71%)
- 段间价差不可当作下跌:相邻段首日与末日的差值是跨月价差,不是行情。
- 结算价:区间最大 24,080 / 最小 22,895(跨合约,期初/期末差值无意义,故不给)
> 若把两段首尾直接相减,会得到「一个月 −1.71%」——那是假跌。真实情况是 2611 段跌 3.80%、2612 段涨 2.71%,中间没有可比的连续价格。
③ 分仓明细:--product cu --days 3 --end 20261023 --detail
**20261023 分仓库明细(前 15)**
| 仓库 | 仓单量(吨) |
|------|--------|
| 常州融达 | 5,866 |
| 中储大场 | 3,515 |
| 上港云仓安达 | 2,017 |
| 广东炬申 | 1,720 |
| …(共 13 家仓库,按仓单量降序) |
在汇总表之后追加最近一个有仓单数据日的各交割仓库分布,用于看仓单集中在哪几个库。
④ 代码写错时(--product zzz)
未知品种代码:'zzz'。SHFE/INE 支持的常用代码:ag, al, ao, au, bc, br, bu, cu, fu, hc, lu, ni, nr, pb, rb, ru, sc, sn, sp, ss, wr, zn
请核对代码后重试(代码须精确,输错会查到别的品种或报错)。
(退出码 2)
不会静默返回别的数据,也不会返回"看起来正常但其实是别品种"的结果。
⑤ 图表长什么样
--chart 路径.png:双 Y 轴静态图。左轴蓝色折线 = 仓单量,右轴 = 价格日K线(蜡烛图)——红蜡烛 = 当日收盘 ≥ 开盘(涨),绿蜡烛 = 收盘 < 开盘(跌),影线为当日最高/最低,标题形如「铜(CU) 库存与价格走势 · SHFE(红涨绿跌)」--chart-html 路径.html:同样的双轴交互图,可缩放拖拽;价格同为日K线,鼠标悬停显示当日开/高/低/收;仓单量悬停仍给前一日变化
> 图表和表格是同一份数据的两种呈现:看趋势用图,查具体数值用表。
⑥ 多品种对比(--product cu,al --days 3)
在各自明细表之后,末尾自动附一张横向对比:
**多品种对比汇总**
| 品种 | 期末仓单 | 仓单区间变动 | 期末结算价 | 结算价涨跌幅 |
|------|---------|-------------|-----------|-------------|
| 铜(CU) | 19,058 吨 | -4,729 | 108,130 元/吨 | -0.64% |
| 铝(AL) | 68,242 吨 | -2,719 | 23,495 元/吨 | +0.06% |
> 各品种单位不同(吨/千克/桶),横向对比看的是方向与幅度,不可直接比绝对值。
⑦ 要同时看开盘/最高/最低价:加 --ohlc(默认表格只给收盘价与结算价)。
python3 scripts/fetch_shfe.py --product cu --days 5 --ohlc
输出在合约列之后多出「开盘 / 最高 / 最低」三列,区间统计也补一行"价格区间:最高 / 最低":
| 日期 | 仓单量(吨) | 仓单变动 | 合约 | 开盘(元/吨) | 最高(元/吨) | 最低(元/吨) | 收盘价(元/吨) | 结算价(元/吨) | 成交量 | 持仓量 |
|---|---|---|---|---|---|---|---|---|---|---|
| 20261023 | 19,058 | 2612 | 106,880 | 108,260 | 106,670 | 107,900 | 107,310 | 111,694 | 183,779 | |
| 20261026 | 16,219 | -2,839 | 2612 | 108,290 | 108,700 | 107,760 | 108,110 | 108,180 | 88,952 | 190,675 |
> 需要图形化 K 线仍用 --chart / --chart-html:蜡烛图本就由开高低收绘制,交互图悬停也会显示当日开/高/低/收。
⑧ 只要一条连续价格、不要合约分段:加 --continuous(主连口径)。
python3 scripts/fetch_shfe.py --product al --start 20260928 --end 20261028 --continuous
输出去掉「合约」列,价格就是逐日主力收盘价首尾相接(不复权,等同行情软件主连);区间统计直接给首末涨跌幅,并单列换月跳空:
**区间统计**
- 仓单:期初 138,188 → 期末 61,888(累计 -76,300 吨),区间最大 138,188 / 最小 61,888 吨
- 主连:20260928 23,950 → 20261028 23,540(-410 / -1.71%)
- 换月 1 次,跳空已并入上述涨跌(这几笔不是当日行情):
- 20261016 起改用 **2612**,跳空 -120(-0.52%)
> 两种口径怎么选:要严谨(判断某根 K 线、某个点位属于哪个合约)用默认的按合约分段;要和行情软件主连对得上、或只要"一个月涨跌多少"这种单一数字,用 --continuous。主连的涨跌幅里含换月跳空,跳空量单独列出来了,扣不扣由你。
二、怎么说都能触发
不需要记参数——按日常说法讲需求即可,下面这些说法都能得到对应结果:
| 你会怎么说 | 实际会怎么做 |
|---|---|
| "查一下沪铜今天的仓单" | 沪铜最近可用交易日,表格 |
| "半年来铜的仓单和价格走势" | cu --days 126,表格 + 区间统计(要图再加图表参数) |
| "查下铜和铝的仓单" | cu,al,两份明细表 + 多品种对比汇总 |
| "帮我看看螺纹钢,不用画图" | 默认就不画图,直接给表格,无需任何额外参数 |
| "黄金和白银一起查,要图" | 多品种 + 图表不能并存:先给对比表并提示"要图请单品种" |
| "上期所黄金结算价" | au --price-only(只取价格,跳过仓单) |
| "螺纹钢最近库存多少" | 螺纹钢仓单为主角 |
| "沪铝主连一个月走势" | al --continuous:一条连续价格,不分合约段、不显示合约列 |
| "铜的仓单都堆在哪些仓库" | 加 --detail,附分仓库明细 |
| "查个不存在的品种 xyz" | 报错并列出可用代码,不返回假数据 |
三种常见"说不出口"的情况,明确一下:
- 不想画图 → 什么都不用说。图表是可选开关(
--chart/--chart-html),不加就不画 - 想一次查多个 → 直接并列说品种名即可("铜和铝"、"沪铜、螺纹钢、黄金"),支持中英文混说
- 只关心价格、不关心仓单 → 说"只要价格/只要结算价",走
--price-only
> 品种说法支持:代码 cu、中文名 铜、交易所俗称 沪铜 均可,例如 --product 沪铜 黄金 原油。
何时启用本 Skill:用户提到「上期所 / 沪铜 / 沪铝 / 黄金 / 螺纹钢 / 原油 / 仓单 / 库存 / 结算价 / 走势」等词并希望查数据时即启用,典型说法见上表。
三、问题解答(常见特殊情况)
> 查询中遇到"不对劲"或特殊情形,先来这里查。下面按场景归类,每条都给结论和应对。
> 完整技术口径见第四节之后,本節只解决"出事了 / 看不懂"的实操问题。
A. 查数据 / 看结果
Q:当天某行显示"无数据"是怎么回事?
A:常见三种——① 周末或法定节假日,官网本就不发布;② 数据约 15:15 后才更新(日盘 15:00 收盘),15:15 前查当日会空;③ 网络或官网接口异常。脚本对 404 一律视为"该日无数据"并自动跳过,不会崩、也不会用旧数字冒充。若整段区间都空白,会提示"区间内无可用数据"。只想看价格可加 --price-only 绕开仓单断档期。
Q:怎么读懂"无数据"那一行?
A:典型如 20261026 | - | 无数据 | - | - | 44,109 | 109,711:仓单尚未发布(15:15 前)、但成交/持仓/价格可能已更新——这是正常表现,不是出错。另外仓单变动显示 0 是真实"当日出入库持平",也不是缺失。
Q:不同品种单位不一样,会不会看错?
A:铜/铝/螺纹/橡胶/原油等按各自自然单位(吨/桶),黄金=千克(价 元/克)、白银=千克(价 元/千克)。表头会随品种自动变,切勿自行换算;多品种横向对比只看方向与幅度、不比绝对值。
B. 输错 / 查错
Q:品种代码输错了会怎样?
A:脚本按精确代码匹配、不猜测,两种结果:① 打成另一个已收录真实品种(如 ru 误成 rb),会返回那个品种的真实数据、名称单位都"对",极易误读——所以代码必须精确;② 打成未收录代码(如 zzz),直接报错并列出可用代码,不返回假数据。不确定时先查 references/products.md。
Q:能用中文名或"沪铜"这种俗称吗?
A:可以,cu = 铜 = 沪铜 都认,支持中英文混写(--product 沪铜 黄金 原油)。但代码仍须精确:写成另一个已收录代码仍会"精准查到别的品种",请留意。
C. 功能 / 依赖
Q:图表没有生成?
A:图表是可选开关,且需额外库——--chart(PNG) 要 matplotlib,--chart-html(交互) 要 plotly。没装时脚本跳过图表并提示安装,不影响表格数据;若图里中文显示成方框,是字体缺失,脚本已自动降级为英文标签,属正常。安装:pip install matplotlib plotly。
Q:想一次查多个品种,还能出图吗?
A:多品种可以(--product cu,al,zn 或中文并列),输出各明细表 + 末尾「多品种对比汇总」。但图表仅支持单品种,传了图表参数会提示"要图请单品种"并跳过,不会报错。
Q:只想查价格、不查仓单?
A:说"只要价格/结算价"或加 --price-only 即可跳过仓单。另外默认就不画图——不加任何图表参数只出表格。
Q:原油 / 国际铜 / 20号胶 / 低硫燃料油(INE 品种)支持吗?
A:支持,代码 sc / bc / nr / lu,已配好中文名与单位,与 SHFE 品种区分使用。
D. 边界
Q:数据为什么在 2025-11-17 前后没断档?
A:旧版接口与新门户数据在该日 90,426 千克处无缝衔接,全程连续,脚本不标注断档。
Q:能预测行情 / 查外盘(LME/COMEX)/ 查分钟级吗?
A:都不能。本 Skill 仅覆盖 SHFE/INE 日线级公开报表,不做预测、不构成投资建议;外盘与分钟级行情不在范围。
Q:断网 / 网络很差 / 上期所官网打不开,还能查吗?
A:分两种情况——① 曾经成功查过的区间:本地缓存(~/.cache/shfe_query/)里有历史数据,会自动兜底,输出会标注"N 天来自本地缓存",数据已定型不变、可信;② 从未查过且当前完全断网:缓存也无数据,会如实提示"暂无数据"。所以日常多查几次、把常用区间缓存下来,断网时就更从容。想强制重新联网用 --no-cache。
Q:为什么提示"联网取数失败 / 疑似官网改版"?
A:脚本对 404(周末/节假日/未发布)与网络/接口异常做了区分。当大量日期出现联网异常(而非个别 404),会打印 ⚠️ 提示"可能是网络波动,也可能是上期所官网调整",并尽量用缓存兜底未覆盖的日期才报无数据。若你确认网络正常、却长期失败,多半是官网改版——此时请反馈,脚本需适配新接口;在此之前历史区间仍可凭缓存查看。
四、接口与取数策略(技术参考,普通用户可跳过)
> 本节为接口实现细节,遇到问题再回看即可。
基地址 https://www.shfe.com.cn/data/tradedata/future/
| 数据 | 路径 | 说明 |
|---|---|---|
| 价格日线(开高低收/结算/成交/持仓) | dailydata/kx{YYYYMMDD}.dat |
品种汇总行 PRODUCTID 为 {code}_f;全程覆盖 |
| 仓单日报(新·统一汇总 HTML,全品种) | stockdata/dailystock_{YYYYMMDD}/ZH/all.html |
新时段(≥2025-11-17)的权威入口,含 al/ag/au 等全部品种;HTML 表格按品种分组,黄金在「黄金」表(单位:千克),合计行首格为「上期所指定交割金库」;其余品种合计行首格为「总计」。这是脚本默认采用的新时段数据源 |
| 仓单日报存档(旧·汇总 JSON,全品种) | dailydata/{YYYYMMDD}dailystock.dat |
旧时段(≤2025-11-17)数据源;o_cursor 含全品种,取 ROWSTATUS=="2" 且 WHABBRNAME 名为「总计」的汇总行(注意:该接口 ROWSTATUS=="2" 含"保税/完税/总计"三行,保税常为 0,须取名为「总计」的全口径行,勿取首个;黄金在该接口仅 1 行 ROWSTATUS=="0"「上期所交割库」即汇总值) |
| 仓单货位明细(per-product,新) | dailydata/stock_{code}_{YYYYMMDD}.dat |
⚠️ 脚本未采用:黄金该接口全程 404,覆盖不全;统一由上面的 all.html 替代 |
| 仓单可用日期列表 | dailydata/stock_data_list.dat |
⚠️ 脚本未采用:已改为"工作日枚举 + 回退取数",不依赖该列表 |
取数策略(v1.3.2 起,统一回退链路,适用于全部品种含黄金):
- 对每个交易日,先试新门户统一报表
all.html(覆盖 ≥2025-11-17,含全部品种);若 404(更早日期)再回退旧版汇总dailystock.dat(覆盖 ≤2025-11-17)。两源均为全品种,无需按品种切换不同端点。 - 单位按品种自然口径:铜/铝/锌/铅/镍/锡/螺纹钢/热卷/线材/不锈钢/氧化铝/橡胶/纸浆/沥青/燃料油/丁二烯橡胶/20号胶/低硫燃料油 = 吨;白银/黄金 = 千克;原油 = 桶。价格单位同理(黄金 元/克、白银 元/千克、其余 元/吨、原油 元/桶)。
- 关键坑(源自官方页面研究):官方报表页 JS 拼的是分品种
/ZH/{code}.html,但实测该路径全程 404,只有all.html可用;且stock_au_{date}.dat等 per-product 端点黄金 404、覆盖不全。脚本因此统一走all.html,不依赖任何 per-product 端点。 - 边界验证:2025-11-17(旧
dailystock.dat)= 2025-11-18(新all.html)两段在 90,426 千克处无缝衔接,全程连续,不要标注"断档"。 - 价格(kx)通道全程无断档,
--price-only可只查价格。
重要口径:
- 仓单汇总 = 该品种当日
all.html合计行("总计"/"上期所指定交割金库")的重量,或旧dailystock.dat中「总计」行的WRTWGHTS;数值已为品种自然单位(吨/千克/桶),切勿再换算。 - 价格:取
kx{date}.dat中PRODUCTID == "{code}_f"的主力合约行,字段OPENPRICE / HIGHESTPRICE / LOWESTPRICE / CLOSEPRICE / SETTLEMENTPRICE / VOLUME / OPENINTEREST。开/高/低三价默认不进表格,加--ohlc开关即可在表格中显示(见第一节示例⑦)。
> ⚠️ 这里有三个必须知道的坑(v1.8.0 修正):
> 1.kx*.dat中同一品种所有合约的PRODUCTID都等于{code}_f,合约月份在DELIVERYMONTH;最后一行是DELIVERYMONTH=="小计"的品种汇总行。"取第一个匹配行"拿到的是最近交割月,不是主力——2026-10-28 实测 25/25 个品种的首行都不是主力。
> 2. 该错误表现为「先正确、后错误」:移仓前首行恰好就是主力,移仓后悄悄切到即将摘牌的合约,并在换月当天用跨月价差造出一根假阴线。只能靠逐日核对合约月份发现,看涨跌幅看不出来。
> 3. 现在的选法是每日按OPENINTEREST最大自动选主力,并把合约月份单列输出;需要固定某一合约时用--contract 2612。跨合约的首末相减一律不计算(区间统计会按段拆分)。 - 每日数据约 15:15 后发布(日盘 15:00 收盘);节假日无数据。请求 404(返回 HTML)视为该日无数据,脚本自动跳过(表格标"无数据",图表留空)。
- 数据仅反映交易所公开报表,脚本结果须标注"来源:上海期货交易所官网"。
五、执行要点
- 取数默认并行请求(线程池 16);新时段取
all.html,旧时段回退dailystock.dat,404 即视为无数据,无需先拉日期列表。 - 时间范围长时逐日请求,脚本内置上限 600 个交易日,超出报错提示缩小范围。
- 输出为 Markdown 表格:默认列 = 日期、仓单量(带单位)、仓单变动、合约、收盘价、结算价、成交量、持仓量;加
--ohlc时在合约列后插入开盘/最高/最低三列。末尾附区间统计(期初/期末/最大/最小/累计变动;--ohlc时补"价格区间最高/最低");区间内主力换月时按段拆分涨跌,不给跨段合计。 - 多品种(≥2 个)时:各品种明细表依次输出,末尾追加「多品种对比汇总」(期末仓单、区间变动、期末结算价、涨跌幅)。图表仅支持单品种,多品种会提示并跳过。汇总表的涨跌幅在区间含换月时显示「—」,不做跨合约计算。
- 交互图 hover:价格日K线显示当日开/高/低/收,仓单量显示当日值与前一日变化。价格默认蜡烛图(红涨绿跌),需要旧版结算价折线时加
--price-line。 - 图表中文字体不可用时自动降级为英文标签,不中断。
六、依赖与提示
- 图表功能为可选依赖:
--chart(静态 PNG)需matplotlib,--chart-html(交互图)需plotly;二者均不在 Python 标准库中。未安装时脚本会跳过图表并打印安装提示,不影响数据与表格输出。安装:pip install matplotlib plotly,或直接运行脚本附带的python3 scripts/install_deps.py一键补齐。 - 品种代码须精确:输错会查到别的品种的真实数据(脚本按精确代码匹配,不猜测)。未知代码(不在 references/products.md 列表)会直接报错并列出可用代码。若需查询 INE 品种,注意代码区分(如 20号胶
nr、低硫燃料油lu、原油sc、国际铜bc)。
本地缓存(抗网络波动 / 官网调整)
为缓解「无本地缓存、网络差受影响」与「官网改版可能查不到」两点,v1.6.0 起内置本地缓存:
- 自动缓存:每次成功查到的历史日期数据会写入本地(
~/.cache/shfe_query/shfe_cache.json,按 品种+日期 索引)。历史日期数据一经发布即定型不变,可长期安全复用。 - 自动兜底:查询时优先读缓存;若某天联网失败(断网 / 官网抽风 / 接口调整),已缓存的该日历史数据仍会从缓存读出,不会凭空丢失——输出会在「区间统计」处标注"本表有 N 天来自本地缓存"。
- 强制刷新:
--no-cache跳过读缓存、强制联网(成功后仍会写回缓存)。 - 清空缓存:
--clear-cache清掉全部本地缓存后退出。 - 修复旧缓存:
--repair-cache清理 v1.7.0 及更早版本留下的脏条目后退出(见下方"缓存的两个历史缺陷")。 - 缓存的两个历史缺陷(v1.8.0 修复):
1. 空仓单被永久固化。旧实现「只要价格有值就整条缓存」,于是盘中或 15:15 前跑一次,就把「仓单当日尚未发布」写成了「仓单无数据」,此后无论怎么重跑都查不到——代码注释写的是"刻意不缓存无数据",实现恰好相反。实测 al 品种 244 天缓存里有 119 天仓单为空。现在只写「确实取到的字段」,缓存命中但缺仓单时会继续联网补齐。
2. 价格缓存存的是错合约。旧 price 没有合约标记,存的是近月合约报价。--repair-cache据此识别并删除这些条目,强制重取。
> 升级后若发现某些历史日期显示-,属正常:那是旧脏缓存被清掉、等待重新联网补齐。加--no-cache或直接重跑一次即可。 - 边界说明:缓存只缓解「曾经成功查过」的场景;若某区间从未成功联网过且当前网络全断,缓存也无数据可兜,这种情况会如实提示"暂无数据"。要根治「官网改版整体失效」,需等待脚本适配新接口(届时会提示疑似改版)。
七、输出规范
回复用户时:
- 先给结论(如"沪铜仓单半年累计减少 X 吨,价格从 A 涨到 B"),再给表格摘要(长区间只展示关键节点,不全贴)。
- 图表用 present_files 呈现。
- 末尾注明:数据来源与更新时间、"仅日线级数据,分钟级行情不在覆盖范围"、"不构成投资建议"。
八、边界
- 不预测行情、不给投资建议。
- 官网未发布的数据如实说明(标"无数据"),不用旧数据冒充。
- 接口若整体失效(改版),停止执行并告知用户重新核实接口,不猜测新路径。
- 分钟级行情、外盘(LME/COMEX)数据不在本 Skill 范围。
九、自助排查与试错手册(FAQ 没命中时)
> 第三节的 FAQ 覆盖常见情形;若你遇到没列出的特殊问题,按下面"先判现象 → 5 步试错"来定位,比盲试高效得多。脚本不会凭空成功或失败,每个异常都有可解释的原因。
先看现象 / 退出码
| 现象 | 含义 | 下一步 |
|---|---|---|
| 退出码 2 + "未知品种:xxx" | 代码/中文名未被识别 | 查 references/products.md 确认代码;注意 ru/rb 这类易混代码 |
| 退出码 0 但整段表空 / "区间内无可用数据" | 该区间官网确实未发布(周末/法定长假),或从未成功联网过(连缓存也没有) | 换更近的日期;若怀疑从未联网,见步骤 4 直连官网核对 |
| 退出码 0 但某行"无数据" | 单日未发布(常见 15:15 前查当日仓单)或网络/接口偶发 | 属正常表现,不必处理;见问题解答 A |
| 退出码 0 + 末尾 ⚠️"较多日期联网失败" | 网络波动或官网改版 | 见步骤 1–5;长期失败多半是改版,需反馈 |
5 步试错法
- 隔离维度:加
--price-only。若价格正常只有仓单缺 → 问题在仓单通道(注意仓单 15:15 前常为空,非故障);若价格也缺 → 价格通道或网络问题。 - 排缓存干扰:加
--no-cache强制联网。若结果变了,说明是旧缓存误导;若仍失败,问题在联网或官网。 - 查本地缓存:打开
~/.cache/shfe_query/shfe_cache.json,看该品种+日期是否已有记录、数值是否合理(历史数据定型不变,可长期复用)。 - 直连官网核对:打开报表页
https://www.shfe.com.cn/reports/tradedata/dailyandweeklydata/,选日期与品种,确认是该日真没数据(脚本问题)还是官网本身空(非脚本问题)。 - 仍无解就反馈:把以下信息一并给出能最快定位——① 品种与起止日期;② 完整命令;③ 退出码;④ 是否出现 ⚠️;⑤ 缓存是否含该日数据;⑥ 官网该日报表是否有数据。
> 这套方法的目的:把"试错"变成"有依据的排查",避免反复盲跑命令。
十、版本变更记录
v1.10.1(2026-10-28)— 修「--contract 被缓存静默吞掉」
缺陷:缓存键是 (品种, 日期),不含合约维度,而缓存里存的是主力口径价格。于是 --contract 2611、--contract 2612、--contract 2701 三次查询只要命中缓存,就返回完全相同的主力价格——参数看起来生效了,实际被静默忽略。这与 v1.8.0 的「空仓单被缓存」「空价格被缓存」是同一族:缓存粒度没有覆盖查询口径。
实测(2026-10-28,铜):修复前三条命令输出逐字相同(均 2612 / 108,390);修复后分别为 2611 / 109,110、2612 / 108,390、2701 / 108,040。
修复
- 指定
--contract时整体绕过缓存(既不读也不写)。不写是为了避免用非主力价格反向污染主力缓存。 - 顺带说明:这也意味着
--contract查询每次都联网,长区间会慢一些——这是正确性换来的,值得。
v1.10.0 — 修「长区间首次查询静默丢价格」
长区间(如一年 240+ 个交易日)首次查询时,官网会对高频并发做限流,部分 kx*.dat 请求失败;旧实现把这种失败当成「当日无价格」,于是:
表格里价格显示 -、缓存里落一条「只有仓单没有价格」的记录,看起来像官网上没有数据(实际第二天重跑就有了)。
修复
- 并发从 16 降到 8(限流的主要诱因)。
- 抓完后对「该有价格却没有价格」的日期串行重试一轮(间隔 0.4s,不走缓存),仍失败才计为缺失。
fetch_json遇到非 200 一律返回ERR(不再返回None)——None的语义只保留给「404 / 文件确实不存在」。- 末尾提示区分真失败与非交易日:以前春节、元旦这些休市日也被写成「X 天联网取数失败」,现在改为「已跳过 N 个非交易日」,不再制造虚假告警。
> 实测:一年(20251028–20261028,243 个交易日)--no-cache 全量重抓,价格与仓单零缺失。
升级后要注意:旧缓存里可能存在「只有仓单、没有价格」的条目,新版本命中这类条目会自动联网补价格,无需手动清缓存;想强制重抓可加 --no-cache。
v1.9.0 — 新增主连口径 --continuous
- 加
--continuous(同义--main):逐日拼接主力收盘价、不显示合约列、不按换月分段,区间涨跌直接首末相减,并单列每次换月的跳空金额/百分比。用于"只要一条连续价格"或与行情软件主连对齐的场景。 - 默认行为不变(按合约分段、跨段不给合计),保证严谨口径不受影响;
--continuous只影响表格列与区间统计的呈现,底层取数同源(仍是每日持仓量最大者)。
v1.8.0(2026-10-28)— 修两个会直接产出错误数据的缺陷
| # | 缺陷 | 表现 | 影响面 |
|---|---|---|---|
| 1 | 价格取「首个匹配行」 | kx*.dat 中同品种所有合约的 PRODUCTID 都是 {code}_f,首行是最近交割月而非主力。移仓前两者恰好相同 → 先正确、后错误;移仓后静默切换合约,换月当天用跨月价差造出一根假阴线 |
2026-10-28 实测 25/25 个品种全部取错。例:铜 10/26 报 109,550(2611 近月),真实主力 2612 是 108,110,差 1,440 元 |
| 2 | 空仓单被缓存永久固化 | 旧逻辑「只要价格有值就整条缓存」,于是 15:15 前(或盘中)跑一次,就把「仓单当日尚未发布」写成「仓单无数据」,此后怎么重跑都查不到。代码注释写的是"不缓存无数据",实现恰好相反 | 本机 al 缓存 244 天中 119 天仓单为空 |
| 3 | 缓存命中数在过滤前统计 | 输出"本表有 7 天来自缓存",但表里只有 5 行 | 仅观感,已修 |
| 4 | 多品种汇总的涨跌幅跨合约相减 | 区间含换月时给出无意义的涨跌幅 | 汇总表,现显示「—」 |
修复方式
- 价格改为按
OPENINTEREST最大逐日选主力,并把合约月份作为独立一列输出;换月日标⚠换月;新增--contract 2612可锁定单一合约。 - 区间统计按主力分段给涨跌,跨段一律不算;结算价在跨合约时不给期初期末差值。
- 缓存改为只写确实取到的字段,命中缓存但缺仓单时继续联网补齐;新增
--repair-cache清理旧脏条目(空仓单 + 无合约标记的错价格)。
v1.10.4(2026-11-02)— 全品种仓单解析大修(多类历史解析缺陷修复)
经全品种回归测试(scripts/test_all_products.py,新门户 all.html + 旧版 dailystock.dat 两套链路、24 个品种全覆盖)发现并修复了多类会导致仓单数值错误或返回无数据的解析缺陷:
| # | 缺陷 | 影响 | 修复 |
|---|---|---|---|
| 1 | 表头精确相等匹配失败:官网把氧化铝拆成 氧化铝(仓库)/氧化铝(厂库) 两张表,旧逻辑 h == "氧化铝" 永远匹配不到 → 全程返回"无数据" |
氧化铝及所有"仓库/厂库"拆分品种(螺纹钢、不锈钢、石油沥青、纸浆、丁二烯橡胶、热轧卷板、低硫燃料油、20号胶) | 新增 _header_match:支持 基础名/基础名(仓库)/基础名(厂库),单独处理 铜(BC) |
| 2 | 原油表头别名:新门户写 中质含硫原油,META 写 原油 匹配不上 |
原油长期无数据 | _header_match 加 原油↔中质含硫原油 分支 |
| 3 | 旧版 legacy_total 只认"总计"行 |
黄金(仅 1 行明细行)、国际铜(仅合计行)旧版链路返回 None | 三级回退:总计 → 合计 → 明细求和 |
| 4 | 厂库表无"总计"时漏算厂库部分 | 螺纹钢、20号胶等厂库库存被整体漏掉,全口径偏小 | 重写 parse_html_total 为"直接对明细行求和",兼容有总计/仅合计/无汇总三种结构 |
| 5 | 地区小计行 4 列 [地区,'合计',重量,增减] 被误判,与明细重复累加 |
铝出现 58,516 → 117,032 的 2× 虚高 |
改用名称列判定 + 子串匹配(覆盖 完税商品总计 等) |
| 6 | 新上市品种未收录 | 铸造铝合金、胶版印刷纸查不到 | 补入 META(ad / op) |
附带:新增 scripts/test_all_products.py 可复跑的全品种回归测试(断言表头匹配、总计行存在、新旧链路一致、--detail 明细求和=全口径);--detail 分仓库明细仍偶有厂库名显示为地区名(标签问题,不影响重量合计)。