gangtise-openapi-cli 0.41.0 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -41
- package/dist/src/cli.js +31 -10
- package/dist/src/version.js +1 -1
- package/gangtise-openapi/SKILL.md +5 -5
- package/gangtise-openapi/references/commands/fundamental.md +3 -1
- package/gangtise-openapi/references/commands/indicator.md +2 -2
- package/gangtise-openapi/references/commands/quote.md +7 -10
- package/gangtise-openapi/references/errors.md +2 -2
- package/gangtise-openapi/references/fields.md +9 -7
- package/gangtise-openapi/references/response-schema.md +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
README 仅列最近 5 个版本摘要:
|
|
8
8
|
|
|
9
|
+
- **v0.41.1 — 2026-09-24**:① **`quote index-day-kline --security all` 改为直接报错**:该接口对 `all` 返回空结果、不报错,与「当天无数据」无法区分;报错信息写明原因与替代写法。指数日 K 请用 `quote day-kline` 逐个传代码;该接口的返回字段与 `day-kline` 相同、不含指数名称,名称用 `reference securities-search --keyword <指数代码> --category index` 返回的 `gtsName`。② **`fundamental valuation-analysis` 长区间不再被静默截断**:序列逐自然日一行(含周末),默认只取最近 2000 行,区间更长时开头会缺失——现在撞满即标 `partial`、退出码 3,并提示把 `--limit` 设到不小于区间天数(权限窗口内的十年约 3700 行);首行恰好就是 `--start-date` 时说明没丢,不标。起点早于账号回溯下界时该接口从下界起返回、不报错,CLI 在 stderr 提示首行晚于 `--start-date`。③ **估值分析 `--field` 不再可能错列**:CLI 发请求前对字段去重并去掉 `tradeDate`(它总在第一列返回)。旧版在 `--field` 同时含 `tradeDate`(或重复字段)与不存在的字段名时,会输出整体右移一列的数据且退出 0,**用过这类组合的估值结果请重跑**。④ 依赖 `undici` 升至 7.29.1(安全更新)。⑤ 文档:`fundamental valuation-analysis` 的 `--field` 至少要含一个数值列(`value` 等),只传 `tradeDate` 或只传不存在的名字时接口返回 0 行、不报错;`references/errors.md` 的退出码摘要补上 `4`。⑥ **Skill 安装命令改为可重复执行**:目标目录已存在时直接 `cp -r`,新版会被复制进嵌套的 `gangtise-openapi/gangtise-openapi/`、已安装的副本仍是旧版;这样更新过的,按「AI Agent Skill」段的新命令重新执行一次即可。
|
|
10
|
+
|
|
9
11
|
- **v0.41.0 — 2026-09-24**:① **观点列表改走 v2,省积分**:`insight opinion list` / `foreign-opinion list` 默认只返回摘要(`brief`,外资另有 `briefTranslate`,取正文前 200 字),**1 积分/条**;要正文用新增的 `insight opinion detail --chief-opinion-id` / `foreign-opinion detail --foreign-opinion-id`(**30 积分/条**,按返回条数计;ID 可一次传多个,CLI 按 20 个一批自动拆分;没返回正文的 ID 列在 `missingIds`、中途某批失败时未取的 ID 列在 `unfetchedIds`,两种情况都以退出码 3 结束)。想像以前一样列表直接带正文,加 `--with-content`(30 积分/条,返回旧版结构:内资正文在 `contentList.content`;与 `detail` 一样超时不自动重发)。② **题材画像与成分股改走 v2**:`alternative concept-info` / `concept-securities` **50 积分/次**;v2 不含催化事件 `keyEvents`、重点个股标识 `isKey`、纳入理由 `inclusionReason`,需要这几列加 `--full`(500 积分/次)。③ **云盘管理 9 个命令,全部免费**:`vault drive-folder-list`(浏览目录)/ `drive-upload` / `drive-create-folder` / `drive-rename` / `drive-move-file` / `drive-move-folder` / `drive-copy`(把文件复制到另一空间:我的云盘 ↔ 租户云盘)/ `drive-delete-file` / `drive-delete-folder`。两个删除**需 `--yes`**,删文件夹会连同其中全部子文件夹与文件一起删除且不可恢复;云盘允许同名,上传 / 新建 / 复制每跑一次就多一份,这三个与两个删除**超时不自动重发**。④ **新增 `bond` 族 12 个命令,0.4 积分起**:`basic-info`(静态档案)/ `issuer-info` / `daily-quote` / `valuation`(上清所估值)/ `cash-flow` / `announcement` / `issuance-detail` / `rating-overview` / `rating-change`(债项评级变动)/ `issuer-rating-change` / `issuance-plan` / `exercise-notice`。多数按次计费;`rating-overview` 按条、`rating-change` 按有数据的债券只数、`issuer-rating-change` 按发行人计。`--security` **只收标准债券代码**(`019742.SH` / `220205.IB`),简称与拼音整批拒绝(`120001`),先用 `reference securities-search` 换代码;`--field` 写错字段名同样整批拒绝(`100003`),不会静默丢列。**整族超时不自动重发**——计费接口重发会重复扣费,偶发 5xx / 超时请自行重跑。🔴 **`bond announcement` 需手动翻页**:本族唯一分页的接口且**不返回 `total`**,`--page-no` 从 1 起逐页递增直到某页为空。⑤ **`insight highlight list` 会议线索**(会议核心要点信息流;🔴 **5 积分/条,务必带 `--size`**,省略会拉全量;`content` 是 HTML 片段,`--research-area` 不认申万码;按偏移量最多能翻到第 10000 条,超出部分请缩短时间范围分段取)。⑥ **`tool web-search` 联网搜索**(1 积分/次,零结果不扣):`--site` 定向站点、`--min-tier` 收信源等级、`--freshness` 收时效、`--include-content` 取正文(此时 `--size` ≤5)。⚠️ **排序是「信源等级 → 发布日期 → 相关性」,首条不等于最相关**;`publishTime` 判不出时为 `null`,`--freshness` 会把这批一并滤掉,做时点判断别用 `indexTime`。⑦ **常量接口新增 9 个分类**:`fundType` / `fundBondType` / `bondType` / `interestRateType` / `interestFrequency` / `absUnderlyingAssetType` / `ratingType` / `exchange` / `nationalEconomicIndustry`。⑧ **取数前注意几处取值**:`bond issuer-info` 的评级列混合境内外口径,做信用比较一并取 `ratingAgency`;评级值可能带 `sf` / `pi` 后缀;`bond daily-quote` / `valuation` / `issuance-plan` 的 `--start-date` 早于可回溯下界时整批返回 `110003`,把起点移进窗口再查。逐条见 `gangtise-openapi/references/commands/bond.md`。
|
|
10
12
|
|
|
11
13
|
- **v0.40.1 — 2026-09-19**:**文档修正,无代码变更**。① **字段名写错时两族接口的表现完全不同,现在写明了**:`quote realtime` / `day-kline` / `minute-kline` / `fund-flow` 是**字段名和值一起消失**(列数与值数始终对得上,CLI 标 `partial` + `missingFields`、退出码 3);`fundamental main-business` / `valuation-analysis` 是**字段名照请求回显、行里少一个值**(`fieldList` 比行长,按下标对位会让缺口之后每个值都贴到错误的表头上,CLI 因此直接报错退出 1)。对照表在 `gangtise-openapi/references/fields.md` 顶部。② 订正 `references/commands/fundamental.md` 里一处把这两族说反的类比。**两族都不报错**,所以 `--field` 的字段名要以 `fields.md` 为准,不确定就别传(不传即返回全部字段)。
|
|
@@ -13,9 +15,10 @@ README 仅列最近 5 个版本摘要:
|
|
|
13
15
|
- **v0.40.0 — 2026-09-19**:① **自选股股票池可增删改**:新增 `vault stock-pool-create` / `stock-pool-rename` / `stock-pool-add-stock` / `stock-pool-remove-stock` / `stock-pool-delete` 五个命令,全部免费、只操作本账号数据。**这是本 CLI 仅有的写操作**——`stock-pool-delete` 会连带移除池内全部关注关系且不可恢复,必须显式加 `--yes`(不加则直接报错、不发请求;个股的投资笔记不受影响)。加 / 删自选与删池是**逐条处理**的:单条失败(如证券代码不存在)服务端仍返回成功信封、明细放在 `failList`,CLI 检测到后在 stderr 点名、标 `partial`、**退出码 3**,批量脚本按退出码判断即可。池名上限 10 个字符、不可与已有池重名(分别返回 `230007` / `230006`),每账号最多 30 个池(`230003`);证券代码大小写敏感且要带市场后缀,`600519.sh` / `700.HK` 这类会进 `failList` 而不是报错。**`raw call` 打这些端点时规则完全相同**——`raw call vault.stock-pool.delete` 一样要 `--yes`,逐条失败一样退出 3。② **`indicator time-series` 的日期轴改为自动判定**:不传 `--calendar-type` 时,CLI 先用免费的 `indicator search` 读各指标的 `parameterList`(去重后每个指标码各查一次,并发)——**全部是交易日类指标才按交易日取**(无非交易日空行,单元格也更省),只要有一个报告期类指标就按自然日取。这条很要紧:报告期末常落在非交易日(如 2024-03-31、2024-06-30 均为周日),按交易日取会让报告期类指标**整行全 `null`、退出码 0、不报错**。显式传了 `--calendar-type` 就完全按给的发。③ **`indicator` 截面 / 时序新增单次 30000 单元格上限**(截面 = 证券数 × 指标数,时序 = 序列数 × 日期数),超出报 `100006` 且不返回部分结果——按这个乘积拆批。⚠️ `--security` 传板块 ID 时服务端会展开成全部成分股,实际证券数远大于写进命令的条数。④ 🔴 **Token 缓存绑定签发它的凭证**:同一台机器上换掉 `GANGTISE_ACCESS_KEY` 后,只要上一个账号的 token 还没过期,此前会继续拿它发请求——取到的是**上一个账号**的数据;本版新增的股票池写命令若撞上,改的也会是上一个账号的池。现在凭证一换即重新登录。**`auth login` 报告的是接下来真正会用的身份**(新增 `source` 字段):设了 `GANGTISE_TOKEN` 就报那个注入的 token 并说明未登录,只有走 AK/SK 时才真的登录;返回的 `cache` 描述这次登录本身,缓存落盘失败也不会混进上一个账号。⑤ **`ai knowledge-batch --query` 整段送出**:此前按逗号拆,`--query "比较两家公司毛利率,并解释差异"` 会被拆成两个半句各查一次;多个问题仍用重复 `--query`。⑥ **异步任务的提交与查询都遵守 `--output` / `--format`**:`ai earnings-review` / `viewpoint-debate` 不加 `--wait` 的提交结果,以及 `*-check` 返回「还在生成中」时的 pending 结果,此前都直接打到 stdout——传了 `--output` 却拿到退出 0 加一个不存在的文件,而 pending 恰恰是轮询脚本最需要读的那个。⑦ 错误码:新增 `230003` / `230006` / `230007` 的处置提示;`100006` 的提示补上 EDE 单元格口径。⑧ 文档:分钟线加总对日线的口径更正——**个别历史交易日的分钟数据与日线不一致**,成交量 / 成交额统计一律以日线为准,**别把「Σ分钟 = 日线」写成校验条件**(详见 `gangtise-openapi/references/fields.md`)。
|
|
14
16
|
|
|
15
17
|
- **v0.39.0 — 2026-09-12**:① **导出文件可核验**:`csv` / `jsonl` 的 `<文件>.meta.json` 增加数据文件的字节数 `bytes` 与内容哈希 `sha256`,转交或归档前 `shasum -a 256 <文件>` 比一下,就能确认这份元信息描述的是旁边这份数据。② **并发导出到同一 `--output` 不再互相掺混**:两个进程同时写同一路径时各写各的暂存文件,最终文件是其中某一次的完整产物(此前可能把两次的行混进一个文件、还报成功);收尾时回读比对哈希,文件已被另一次导出替换就在 stderr 说明并**退出码 4**——数据是完整的,只是那个位置上的文件不是本次产物(与退出码 3 同时发生时退出 3)。并发导出请给各自不同的 `--output`。③ **K 线分片合并的缺列护栏**:合并结果的列集取自第一个有数据的分片,只有靠后分片才返回的列放不下——现在标 `partial` + `droppedColumns`、退出码 3 并在 stderr 点名(此前静默丢弃),需要这些列就缩小日期区间单独拉。④ **`fundamental valuation-analysis` 的 `--skip-null` 与 `--field` 同用**:`--skip-null` 判的是 `value` 与 `percentileRank` 两列,`--field` 没点名它们时此前会把每一行都判成空、返回 `{total: 0, list: []}`(读起来像「这只票没有估值历史」);现在自动补取这两列做过滤,输出仍只给 `--field` 点名的列。⑤ `ai stock-summary` 单次上限由 5000 放宽到接口上限 **6000**,全 A 股可一次提交完。⑥ 文档:EDE 指标覆盖按当前实测更新——`finc_pe_ttm` / `finc_pb_mrq` 港股均有数。分钟线加总对日线的口径见 `gangtise-openapi/references/fields.md`。
|
|
16
|
-
|
|
18
|
+
|
|
17
19
|
### 历史里程碑
|
|
18
20
|
|
|
21
|
+
- **v0.38.0**:`quote realtime` / `day-kline` / `minute-kline` 支持沪深 ETF 与 20 个全球指数,代码直接传即可。
|
|
19
22
|
- **v0.37.0**:下载的「智能文件命名」改为默认只读缓存,缓存未命中不再自动回查 list 接口(省下按条计费的开销),要中文文件名加 `--resolve-title`。
|
|
20
23
|
- **v0.36.0**:日期写法放宽为三种「年在前」格式(「年在后」仍拒收,接口会按美式解析导致差半年),`indicator screener` 支持无日期指标。
|
|
21
24
|
- **v0.29.0**:新增财报日历与 PDF 解析工具,群消息补 `quoteMsg`,并加强大整数 ID 与高积分调用防护。
|
|
@@ -74,17 +77,17 @@ export GANGTISE_TIMEOUT_MS=30000 # 请求超时(默认 30s)
|
|
|
74
77
|
export GANGTISE_TOKEN_CACHE_PATH=... # 覆盖 token 缓存路径(默认 ~/.config/gangtise/token.json)
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
如果没有 `GANGTISE_TOKEN`,CLI 会自动调用 token 接口并缓存到本地(`~/.config/gangtise/token.json`,权限 0600
|
|
80
|
+
如果没有 `GANGTISE_TOKEN`,CLI 会自动调用 token 接口并缓存到本地(`~/.config/gangtise/token.json`,权限 0600)。服务端判定 Token 失效时会自动重新登录并重试一次;凭证本身错(AK/SK 不匹配)不重试,直接报错让你查环境变量。
|
|
78
81
|
|
|
79
82
|
|
|
80
83
|
## AI Agent Skill
|
|
81
84
|
|
|
82
85
|
本项目包含 Skill 定义(`gangtise-openapi/SKILL.md`),可让 AI agent 自动调用 `gangtise` CLI 完成投研数据查询。支持以下 AI 编程助手:
|
|
83
86
|
|
|
84
|
-
- [Claude Code](https://claude.
|
|
87
|
+
- [Claude Code](https://claude.com/product/claude-code) — `~/.claude/skills/`
|
|
85
88
|
- [Codex](https://github.com/openai/codex) — `~/.codex/skills/`
|
|
86
89
|
- [OpenClaw](https://github.com/openclaw/openclaw) — `~/.openclaw/workspace/skills/`
|
|
87
|
-
- [Hermes](https://github.com/
|
|
90
|
+
- [Hermes](https://github.com/NousResearch/hermes-agent) — `~/.hermes/skills/`
|
|
88
91
|
|
|
89
92
|
Skill 目录结构:
|
|
90
93
|
|
|
@@ -93,50 +96,40 @@ gangtise-openapi/
|
|
|
93
96
|
├── SKILL.md # 主 skill 文件(必备规则、速查表、按需引用 references)
|
|
94
97
|
└── references/
|
|
95
98
|
├── commands/ # 按命令组拆分的详细参数文档(agent 按需 Read)
|
|
96
|
-
│ ├── ai.md # AI
|
|
97
|
-
│ ├── alternative.md #
|
|
98
|
-
│ ├──
|
|
99
|
+
│ ├── ai.md # AI 能力(知识库 / 个股线索与看点 / 一页通等生成类 / 业绩点评与观点 PK 异步任务 / 热点 / 管理层讨论)
|
|
100
|
+
│ ├── alternative.md # 行业指标数据库 EDB(search / data)+ 题材指数画像与成分股
|
|
101
|
+
│ ├── bond.md # 债券(基本资料 / 发行人 / 行情与估值 / 兑付 / 公告 / 发行 / 评级 / 行权)
|
|
102
|
+
│ ├── fundamental.md # 财务数据(A股/港股/美股三大报表 / 主营 / 估值 / 盈利预测 / 股东)
|
|
99
103
|
│ ├── indicator.md # 证券级数据指标 EDE(search / 截面 / 时序 / 条件选股)
|
|
100
|
-
│ ├── insight.md #
|
|
101
|
-
│ ├── quote.md #
|
|
102
|
-
│ ├── reference-and-lookup.md #
|
|
103
|
-
│ ├── tool.md # PDF 解析(file-parse)
|
|
104
|
-
│ └── vault.md #
|
|
105
|
-
├── errors.md # 错误码全表、不报错的坑、退出码 3 的判读、Troubleshooting
|
|
104
|
+
│ ├── insight.md # 投研内容(研报 / 观点 / 纪要 / 公告 / 外资 / 财报日历 / 会议线索 / 公众号 / QA / 研报图表)
|
|
105
|
+
│ ├── quote.md # 行情(A股/港股/美股/ETF/各类指数的日 K、分钟 K、实时行情、资金流向)
|
|
106
|
+
│ ├── reference-and-lookup.md # 证券 / 首席 / 机构 / 公众号 / 题材 / 板块 ID 搜索与常量速查
|
|
107
|
+
│ ├── tool.md # PDF 解析(file-parse)/ 联网搜索(web-search)
|
|
108
|
+
│ └── vault.md # 云盘(含目录与文件管理)/ 录音 / 会议 / 群消息 / 股票池
|
|
109
|
+
├── errors.md # 错误码全表、不报错的坑、退出码 3 / 4 的判读、Troubleshooting
|
|
106
110
|
├── examples.md # 典型场景的端到端示例
|
|
107
|
-
├── fields.md #
|
|
111
|
+
├── fields.md # 行情 / 财务 / 估值等字段中英文对照速查表
|
|
108
112
|
├── lookup-ids.md # 常用 ID 速查表(行业/券商/机构/公告分类等)
|
|
109
113
|
└── response-schema.md # 各接口响应字段说明
|
|
110
114
|
```
|
|
111
115
|
|
|
112
|
-
安装(skill 目录随 npm 包分发,`npm install -g`
|
|
116
|
+
安装(skill 目录随 npm 包分发,`npm install -g` 之后即可从全局安装位置复制)。首次安装与版本更新都执行这一段,只保留你在用的助手那一行:
|
|
113
117
|
|
|
114
118
|
```bash
|
|
115
119
|
SKILL_SRC="$(npm root -g)/gangtise-openapi-cli/gangtise-openapi"
|
|
116
120
|
|
|
117
|
-
#
|
|
118
|
-
cp -
|
|
119
|
-
|
|
120
|
-
# Codex
|
|
121
|
-
cp -r "$SKILL_SRC" ~/.codex/skills/gangtise-openapi
|
|
121
|
+
# 先删旧副本再复制:目标目录已存在时直接 cp -r 会复制进嵌套的 gangtise-openapi/gangtise-openapi/,旧版原样留着
|
|
122
|
+
install_skill() { rm -rf "$1/gangtise-openapi" && mkdir -p "$1" && cp -R "$SKILL_SRC" "$1/gangtise-openapi"; }
|
|
122
123
|
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# Hermes
|
|
127
|
-
cp -r "$SKILL_SRC" ~/.hermes/skills/gangtise-openapi
|
|
124
|
+
install_skill ~/.claude/skills # Claude Code
|
|
125
|
+
install_skill ~/.codex/skills # Codex
|
|
126
|
+
install_skill ~/.openclaw/workspace/skills # OpenClaw
|
|
127
|
+
install_skill ~/.hermes/skills # Hermes
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
> 从仓库 clone 开发时,把 `$SKILL_SRC`
|
|
130
|
+
> 从仓库 clone 开发时,把 `$SKILL_SRC` 换成仓库内 `gangtise-openapi` 目录的绝对路径即可。
|
|
131
131
|
|
|
132
|
-
>
|
|
133
|
-
>
|
|
134
|
-
> ```bash
|
|
135
|
-
> # 示例:更新 Claude Code 的 skill
|
|
136
|
-
> cp -r gangtise-openapi ~/.claude/skills/gangtise-openapi
|
|
137
|
-
> ```
|
|
138
|
-
>
|
|
139
|
-
> 可通过查看 SKILL.md 头部的 `version` 字段确认当前版本。
|
|
132
|
+
> **版本更新**:`SKILL.md` 头部的 `version` 字段与 CLI 版本一致。`npm update -g gangtise-openapi-cli` 之后重新执行上面这段,再看已安装副本的 `version` 是否与 `gangtise --version` 相同。对已安装副本做过的本地修改会被覆盖。
|
|
140
133
|
|
|
141
134
|
安装后,可以用自然语言触发,例如:
|
|
142
135
|
- "帮我查今天所有的研报"
|
|
@@ -179,7 +172,7 @@ cp -r "$SKILL_SRC" ~/.hermes/skills/gangtise-openapi
|
|
|
179
172
|
| | `sector-constituents` | 板块成分股查询 |
|
|
180
173
|
| **Quote** | `day-kline` | 历史日K线——A股/港股/美股个股 + 沪深 ETF + 交易所/概念/行业指数 + 20 个全球指数,可混查 |
|
|
181
174
|
| | `day-kline-hk` / `day-kline-us` | ⚠️ 已下线,能力并入 `day-kline`(接口仍可调,但不校验证券代码) |
|
|
182
|
-
| | `index-day-kline` | ⚠️ 已下线,能力并入 `day-kline
|
|
175
|
+
| | `index-day-kline` | ⚠️ 已下线,能力并入 `day-kline`(返回字段相同);只收明确的指数代码,`--security all` 会被 CLI 拒绝 |
|
|
183
176
|
| | `minute-kline` | 分钟K线——沪深A股 / ETF + 各类指数含全球指数(`--security` 可重复,逐只并发合并) |
|
|
184
177
|
| | `realtime` | 实时行情快照——A股/港股/美股个股 + 沪深 ETF + 各类指数含全球指数 |
|
|
185
178
|
| | `fund-flow` | A股个股日资金流向(沪深京;小/中/大/特大单 + 主力净流入) |
|
|
@@ -239,7 +232,8 @@ cp -r "$SKILL_SRC" ~/.hermes/skills/gangtise-openapi
|
|
|
239
232
|
| | `concept-securities` | 题材指数成分股(按分组;50 积分/次,`--full` 另含重点个股标识与纳入理由 500 积分/次) |
|
|
240
233
|
| **Tool** | `file-parse` / `file-parse-check` | PDF 解析为 Markdown + 图片(异步,返回 ZIP) |
|
|
241
234
|
| | `web-search` | 联网搜索(公开互联网定向检索,信源分级 T0–T3,可选返回正文;1 积分/次) |
|
|
242
|
-
| **Raw** | `
|
|
235
|
+
| **Raw** | `list` | 列出全部 endpoint key(供 `raw call` 使用) |
|
|
236
|
+
| | `call` | 原始接口调用(可访问任意 JSON / download endpoint;upload 型的 `tool.file-parse.submit` / `vault.drive.upload` 需走 `tool file-parse` / `vault drive-upload`,raw 带不了文件) |
|
|
243
237
|
|
|
244
238
|
## 命令概览
|
|
245
239
|
|
|
@@ -341,10 +335,10 @@ vault.drive.upload
|
|
|
341
335
|
vault.my-conference.download
|
|
342
336
|
vault.stock-pool.create
|
|
343
337
|
-->
|
|
344
|
-
- **Token
|
|
338
|
+
- **Token 自愈**:服务端判定 Token 失效时自动强制刷新 Token 并重试一次。
|
|
345
339
|
- **Token 缓存绑定账号**:缓存记录它是为哪组凭证 + 哪个 `GANGTISE_BASE_URL` 签发的(只存不可逆的指纹,不存 key 本身)。换了 `GANGTISE_ACCESS_KEY` 再跑,即使旧 token 还没过期也会重新登录,**不会拿上一个账号的身份去发请求**——这点在股票池那五个写命令上尤其要紧。
|
|
346
340
|
- **`auth login` 报告的是「接下来真正会用的身份」**,返回体里的 `source` 说明它从哪来:`GANGTISE_TOKEN` 有值时是 `env-token`(那个 token 对所有命令优先,所以不联服务端、也不签发新的,并附一句提示);没有它、有 AK/SK 时是 `login`(每次都真的登录,不复用缓存);两者都没有才报错。返回的 `cache` 描述的就是这次登录的结果,缓存落盘失败也不会混进上一个账号的信息。
|
|
347
|
-
- **K线/资金流向自动分片**:`quote day-kline --security aShares|hkStocks|usStocks`、`quote fund-flow --security aShares` 等全市场查询自动按日期切分(A股 K线/资金流向 1 天/片、美股 1 天/片、港股 2 天/片;已下线的 `day-kline-hk`/`day-kline-us
|
|
341
|
+
- **K线/资金流向自动分片**:`quote day-kline --security aShares|hkStocks|usStocks`、`quote fund-flow --security aShares` 等全市场查询自动按日期切分(A股 K线/资金流向 1 天/片、美股 1 天/片、港股 2 天/片;已下线的 `day-kline-hk`/`day-kline-us` 用 `all`,分别 2/1 天/片),并发执行后合并结果;按日分片自动跳过周六日。分片时如果用户未传 `--limit`,自动注入 `limit: 10000`(API 上限)避免默认 6000 截断。**显式多证券**的日 K 在「证券数 × 交易日数」超过 `--limit` 时自动逐只请求并按传入顺序合并(撞上限的证券标 `partial` + `truncatedSecurities`);`minute-kline` 的 `--security` 可重复,逐只并发请求后合并。
|
|
348
342
|
- **Token 内存缓存**:Token 在进程内存中缓存,避免每次请求读盘。
|
|
349
343
|
- **`--verbose`**:打印每个请求的方法、路径、状态码、耗时和响应大小到 stderr,方便定位慢查询。
|
|
350
344
|
|
|
@@ -559,8 +553,8 @@ gangtise quote day-kline --security 512800.SH --security SPX.SPI --security N225
|
|
|
559
553
|
gangtise quote day-kline --security hkStocks --start-date 2026-04-01 --end-date 2026-04-10 --format json
|
|
560
554
|
# 美股全市场(自动按 1 天/片分片)
|
|
561
555
|
gangtise quote day-kline --security usStocks --start-date 2026-04-01 --end-date 2026-04-02 --field securityCode --field close --format json
|
|
562
|
-
# 沪深京指数日K
|
|
563
|
-
gangtise quote
|
|
556
|
+
# 沪深京指数日K线(逐个传指数代码;要指数名称用 reference securities-search --category index)
|
|
557
|
+
gangtise quote day-kline --security 000001.SH --security 399001.SZ --start-date 2024-05-01 --end-date 2024-05-20 --field securityCode --field tradeDate --field close --field volume
|
|
564
558
|
# A股分钟K线
|
|
565
559
|
gangtise quote minute-kline --security 600519.SH --start-time "2026-04-15 09:30:00" --end-time "2026-04-15 15:00:00" --field open --field close --field volume
|
|
566
560
|
# 实时行情:三大市场混合查询
|
|
@@ -964,7 +958,7 @@ datetime 参数(`--start-time` / `--end-time`)同理,只归一日期部分
|
|
|
964
958
|
|
|
965
959
|
> **错误码分两代并存**:服务端错误码分三层(`999xxx` 服务统一层 / `1xxxxx` 业务通用层 / `2xxxxx` 接口专有层),信封带 `errorType` 和 `traceId`。按「错误处理层」而不是按业务模块划分:参数校验层与路由层发新码(信封 `code` 是 JSON 数字且带 `errorType`),方法路由层、token 过滤器、以及异步生成状态仍发旧码(字符串、无 `errorType`)——这判断的是单条错误路径,不是整个接口;CLI 对两代都能识别。报错行会带 `[trace <id>]`,**报障时请带上它**。
|
|
966
960
|
>
|
|
967
|
-
>
|
|
961
|
+
> 表里没列的码(`999004`、`999007`–`999009`、`999015`、`100002`、`210001`、`220001`、`230001`、`240002`、`240003` 等)多被上面的兜底码接管,CLI 同样内置了对应提示,逐条说明见 skill 的 `references/errors.md`。⚠️ 两个需要留意的行为:**枚举值拼错和分页越界在部分端点上会报 `100005`/`100006`、在另一些端点上被静默忽略**(后者按未传该筛选条件处理,结果看着正常但范围不对——CLI 对 `--search-type`/`--rank-type`/`--file-type` 等已知枚举本地拦截,未覆盖的自由字符串参数要自己核对);**`viewpoint-debate` 的敏感内容不会被提前拦截**,会扣满 50 积分再以 `410111` 失败。
|
|
968
962
|
|
|
969
963
|
---
|
|
970
964
|
|
|
@@ -977,6 +971,8 @@ npm 发版通过 GitHub Actions Trusted Publishing 完成,不需要 `NPM_TOKEN
|
|
|
977
971
|
```bash
|
|
978
972
|
npm version patch --no-git-tag-version
|
|
979
973
|
npm run prepare
|
|
974
|
+
npm test && TZ=UTC npx vitest run # CI 是 UTC,两个时区都要过
|
|
975
|
+
npm audit --omit=dev --registry=https://registry.npmjs.org # 显式走官方源:默认源是 npmmirror 时审计请求会 404,报不出告警
|
|
980
976
|
VERSION=$(node -p "require('./package.json').version")
|
|
981
977
|
git commit -am "chore: release v$VERSION"
|
|
982
978
|
git tag -a "v$VERSION" -m "v$VERSION" # 必须 annotated
|
package/dist/src/cli.js
CHANGED
|
@@ -44,7 +44,7 @@ async function withClient(a, b) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
const DEFAULT_QUOTE_LIMIT = 6000;
|
|
47
|
-
function flagIfLimitTruncated(data, cap, label, rangeFlags = "--start-date/--end-date") {
|
|
47
|
+
function flagIfLimitTruncated(data, cap, label, rangeFlags = "--start-date/--end-date", advice) {
|
|
48
48
|
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
49
49
|
return;
|
|
50
50
|
const rec = data;
|
|
@@ -52,7 +52,7 @@ function flagIfLimitTruncated(data, cap, label, rangeFlags = "--start-date/--end
|
|
|
52
52
|
return;
|
|
53
53
|
if (Array.isArray(rec.list) && rec.list.length >= cap) {
|
|
54
54
|
rec.partial = true;
|
|
55
|
-
process.stderr.write(`[gangtise] warning: ${label} returned ${rec.list.length} rows = the ${cap}-row limit; results are likely truncated (this endpoint has no pagination). Narrow ${rangeFlags} or raise --limit (max 10000), fetching in date batches
|
|
55
|
+
process.stderr.write(`[gangtise] warning: ${label} returned ${rec.list.length} rows = the ${cap}-row limit; results are likely truncated (this endpoint has no pagination). ${advice ?? `Narrow ${rangeFlags} or raise --limit (max 10000), fetching in date batches.`}\n`);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
async function runDownload(client, endpointKey, query, options) {
|
|
@@ -463,7 +463,7 @@ const REALTIME_MARKETS = ["aShares", "hkStocks", "usStocks"];
|
|
|
463
463
|
const FUND_FLOW_MARKETS = ["aShares"];
|
|
464
464
|
const MARKET_KEYWORDS = new Set(["all", "ashares", "hkstocks", "usstocks"]);
|
|
465
465
|
const matchesMarketKeyword = (value, keyword) => value.toLowerCase() === keyword.toLowerCase();
|
|
466
|
-
const checkMarketKeywords = (securities, accepted, command) => {
|
|
466
|
+
const checkMarketKeywords = (securities, accepted, command, noKeywordReason) => {
|
|
467
467
|
const used = securities.filter((s) => MARKET_KEYWORDS.has(s.toLowerCase()));
|
|
468
468
|
if (used.length === 0)
|
|
469
469
|
return;
|
|
@@ -471,14 +471,14 @@ const checkMarketKeywords = (securities, accepted, command) => {
|
|
|
471
471
|
if (unsupported.length > 0) {
|
|
472
472
|
throw new ValidationError(accepted.length > 0
|
|
473
473
|
? `${command}: '${unsupported[0]}' is not a whole-market keyword for this command — use ${accepted.join(" / ")}`
|
|
474
|
-
: `${command}: this command takes explicit security codes only — '${unsupported[0]}' and other whole-market keywords are not supported`);
|
|
474
|
+
: `${command}: this command takes explicit security codes only — '${unsupported[0]}' and other whole-market keywords are not supported${noKeywordReason ? `: ${noKeywordReason}` : ""}`);
|
|
475
475
|
}
|
|
476
476
|
if (securities.length > 1) {
|
|
477
477
|
throw new ValidationError(`${command}: a market keyword must be passed alone, got '${securities.join(", ")}' — the API rejects it mixed with security codes or another keyword`);
|
|
478
478
|
}
|
|
479
479
|
};
|
|
480
480
|
const canonicalizeMarketKeywords = (securities, accepted) => securities.map((s) => accepted.find((a) => matchesMarketKeyword(s, a)) ?? s);
|
|
481
|
-
const addKlineCommand = (name, endpointKey, securityHelp, markets) => quote.command(name)
|
|
481
|
+
const addKlineCommand = (name, endpointKey, securityHelp, markets, noKeywordReason) => quote.command(name)
|
|
482
482
|
.option("--security <code>", securityHelp, collectList, [])
|
|
483
483
|
.option("--start-date <date>", "Start date (default: 1 year before end-date)", dateArg("--start-date"))
|
|
484
484
|
.option("--end-date <date>", "End date (default: latest)", dateArg("--end-date"))
|
|
@@ -487,7 +487,7 @@ const addKlineCommand = (name, endpointKey, securityHelp, markets) => quote.comm
|
|
|
487
487
|
.option("--format <format>", "Output format", "table")
|
|
488
488
|
.option("--output <path>")
|
|
489
489
|
.action((options) => {
|
|
490
|
-
checkMarketKeywords(options.security, Object.keys(markets), `quote ${name}
|
|
490
|
+
checkMarketKeywords(options.security, Object.keys(markets), `quote ${name}`, noKeywordReason);
|
|
491
491
|
options.security = canonicalizeMarketKeywords(options.security, Object.keys(markets));
|
|
492
492
|
return withClient(options, async (client) => {
|
|
493
493
|
const format = parseOutputFormat(options.format);
|
|
@@ -516,7 +516,7 @@ const addKlineCommand = (name, endpointKey, securityHelp, markets) => quote.comm
|
|
|
516
516
|
addKlineCommand("day-kline", "quote.day-kline", "Security code — A-share .SH/.SZ/.BJ, ETF .SH/.SZ (e.g. 512800.SH), HK .HK, US .O/.N/.A, exchange index .SH/.SZ/.BJ, concept index .GT, industry index .CI/.SWI, global index (e.g. SPX.SPI / N225.NKI / HSI.HI); or one market keyword: aShares / hkStocks / usStocks (auto-sharded by date, must be passed alone; keywords cover stocks only — ETFs and indices must be listed by code)", KLINE_MARKETS);
|
|
517
517
|
addKlineCommand("day-kline-hk", "quote.day-kline-hk", "[deprecated: use 'day-kline'] Security code (HK stock: .HK, or 'all' for full market)", LEGACY_ALL_MARKET(2));
|
|
518
518
|
addKlineCommand("day-kline-us", "quote.day-kline-us", "[deprecated: use 'day-kline'] Security code (US stock: e.g. AAPL.O, or 'all' for full market)", LEGACY_ALL_MARKET(1));
|
|
519
|
-
addKlineCommand("index-day-kline", "quote.index-day-kline", "[deprecated: use 'day-kline'] Index code (.SH/.SZ/.BJ,
|
|
519
|
+
addKlineCommand("index-day-kline", "quote.index-day-kline", "[deprecated: use 'day-kline'] Index code (.SH/.SZ/.BJ), repeat for several; no whole-market keyword — 'all' is refused because the endpoint answers it with an empty result", {}, "the endpoint answers 'all' with an empty result. List the index codes one by one — quote day-kline takes the same codes");
|
|
520
520
|
quote.command("minute-kline").option("--security <code>", "Security code — A-share .SH/.SZ (SH/SZ only), ETF .SH/.SZ (e.g. 512800.SH), exchange index .SH/.SZ, concept index .GT, industry index .CI/.SWI, global index (e.g. SPX.SPI / N225.NKI / HSI.HI); repeat for several — one request each, run concurrently and merged; no whole-market keyword", collectList, []).option("--start-time <datetime>", "Start time (yyyy-MM-dd HH:mm:ss)", datetimeArg("--start-time")).option("--end-time <datetime>", "End time (yyyy-MM-dd HH:mm:ss)", datetimeArg("--end-time")).option("--limit <number>", "Max rows per request (default: 6000, max: 10000)").option("--field <field>", "Field", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => withClient(options, async (client) => {
|
|
521
521
|
const format = parseOutputFormat(options.format);
|
|
522
522
|
const limit = parseOptionalNumberOption(options.limit, "--limit", { integer: true, min: 1, max: 10000 }) ?? DEFAULT_QUOTE_LIMIT;
|
|
@@ -585,12 +585,33 @@ addFinancialReport("income-statement-us", "fundamental.income-statement-us", "Pe
|
|
|
585
585
|
addFinancialReport("balance-sheet-us", "fundamental.balance-sheet-us", "Period: q1/h1/q3/nsd/annual/latest");
|
|
586
586
|
addFinancialReport("cash-flow-us", "fundamental.cash-flow-us", "Period: q1/h1/q3/nsd/annual/latest");
|
|
587
587
|
fundamental.command("main-business").requiredOption("--security-code <code>").option("--start-date <date>", "Start date (yyyy-MM-dd)", dateArg("--start-date")).option("--end-date <date>", "End date (yyyy-MM-dd)", dateArg("--end-date")).addOption(new Option("--breakdown <type>", "Breakdown: product/industry/region").choices(["product", "industry", "region"]).default("product")).option("--period <type>", "Period: interim/annual", collectList, []).option("--field <field>", "Field", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("fundamental.main-business", { securityCode: options.securityCode, startDate: options.startDate, endDate: options.endDate, breakdown: options.breakdown, periodList: maybeArray(options.period), fieldList: maybeArray(options.field) })));
|
|
588
|
-
|
|
588
|
+
const VALUATION_DEFAULT_LIMIT = 2000;
|
|
589
|
+
function valuationFieldsToSend(fields) {
|
|
590
|
+
const unique = [...new Set(fields)];
|
|
591
|
+
const values = unique.filter((field) => field !== "tradeDate");
|
|
592
|
+
return values.length > 0 ? values : unique;
|
|
593
|
+
}
|
|
594
|
+
function firstRowTradeDate(data) {
|
|
595
|
+
const normalized = normalizeRows(data);
|
|
596
|
+
const list = Array.isArray(normalized) ? normalized : normalized?.list;
|
|
597
|
+
const first = Array.isArray(list) ? list[0] : undefined;
|
|
598
|
+
const value = first && typeof first === "object" ? first.tradeDate : undefined;
|
|
599
|
+
return typeof value === "string" ? value : undefined;
|
|
600
|
+
}
|
|
601
|
+
fundamental.command("valuation-analysis").requiredOption("--security-code <code>").addOption(new Option("--indicator <name>", "Indicator").choices(["peTtm", "pbMrq", "peg", "psTtm", "pcfTtm", "em"]).makeOptionMandatory()).option("--start-date <date>", "Start date (yyyy-MM-dd)", dateArg("--start-date")).option("--end-date <date>", "End date (yyyy-MM-dd)", dateArg("--end-date")).option("--limit <number>", "Max rows (default: 2000). One row per calendar day, weekends included, and the most recent are kept — a range longer than the limit loses its START (flagged partial, exit 3)").option("--field <field>", "Field", collectList, []).option("--skip-null", "Drop rows where value or percentileRank is null").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => withClient(async (client) => {
|
|
589
602
|
const format = parseOutputFormat(options.format);
|
|
590
603
|
const requested = maybeArray(options.field);
|
|
591
604
|
const filterFields = options.skipNull && requested ? ["value", "percentileRank"].filter((field) => !requested.includes(field)) : [];
|
|
592
|
-
const fieldList = requested &&
|
|
593
|
-
|
|
605
|
+
const fieldList = requested && valuationFieldsToSend([...requested, ...filterFields]);
|
|
606
|
+
const limit = parseOptionalNumberOption(options.limit, "--limit", { integer: true, min: 1 }) ?? VALUATION_DEFAULT_LIMIT;
|
|
607
|
+
let data = await client.call("fundamental.valuation-analysis", { securityCode: options.securityCode, indicator: options.indicator, startDate: options.startDate, endDate: options.endDate, limit, fieldList });
|
|
608
|
+
const firstDate = firstRowTradeDate(data);
|
|
609
|
+
if (!(options.startDate && firstDate === options.startDate)) {
|
|
610
|
+
flagIfLimitTruncated(data, limit, "fundamental valuation-analysis", "--start-date", `The API keeps the most recent rows, so it is the START of the range that is missing. The series has one row per calendar day (weekends included): raise --limit to at least the number of days in the range (e.g. --limit 4000 for ten years within your account's history window), or move --start-date later.`);
|
|
611
|
+
}
|
|
612
|
+
if (options.startDate && firstDate && firstDate > options.startDate && !data.partial) {
|
|
613
|
+
process.stderr.write(`[gangtise] note: the series starts at ${firstDate}, later than --start-date ${options.startDate}. Either the security listed later, or the range reaches past your account's history window — this endpoint returns what lies inside the window without an error.\n`);
|
|
614
|
+
}
|
|
594
615
|
if (options.skipNull) {
|
|
595
616
|
const normalized = normalizeRows(data);
|
|
596
617
|
if (normalized && typeof normalized === "object" && !Array.isArray(normalized)) {
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.41.
|
|
1
|
+
export const CLI_VERSION = "0.41.1";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gangtise-openapi
|
|
3
|
-
version: "0.41.
|
|
3
|
+
version: "0.41.1"
|
|
4
4
|
description: |-
|
|
5
5
|
通过 gangtise CLI 直接调用 Gangtise OpenAPI,拉取投研原始数据、批量导出、下载文件、调用 AI 能力。
|
|
6
6
|
|
|
@@ -29,7 +29,7 @@ description: |-
|
|
|
29
29
|
5. **多值参数**:优先重复传(最稳、最明确):`--security 600519.SH --security 000858.SZ`。CLI 也支持半/全角逗号分隔(语音输入容错),但重复传不易被 shell 吞。
|
|
30
30
|
6. **K 线"最近 N 条"**:必须用 `--start-date`/`--end-date` 拉日期范围,从结果按 `tradeDate` 取尾部最近 N 条。**不要只用 `--limit N`**(截取的是窗口开头)。
|
|
31
31
|
6.1. **日 K 仅历史**:`day-kline` **不返回盘中实时数据**。当日数据入库时间:A 股 ~15:30 / 港股 ~16:30 / 美股 ~07:00(北京时间)。需要盘中快照请走 `quote realtime`。
|
|
32
|
-
6.1.1. 🔴 **`quote day-kline` 一个命令覆盖 A 股 / 港股 / 美股 / 沪深 ETF / 各类指数(含 20 个全球指数)**,可混着传代码。**全市场关键字是 `aShares` / `hkStocks` / `usStocks`,必须单独传**(不能与代码或另一个关键字混填;`--security all` 会被 CLI 拒并提示改用哪个)。**关键字只覆盖个股**:ETF 与各类指数(`.SH`/`.SZ`/`.BJ` 交易所指数、`.GT` 概念、`.CI`/`.SWI` 行业、`SPX.SPI` 等全球指数,清单见 `references/commands/quote.md`)须逐个传代码。全球指数 realtime 的 `volume` / `amount` / `amplitude` 与分钟 K 的 `volume` / `amount` 为 `null`,日 K 只有 `amount` 为 `null`;`tradeTime` 是交易所当地时间。`day-kline-hk` / `day-kline-us` / `index-day-kline`
|
|
32
|
+
6.1.1. 🔴 **`quote day-kline` 一个命令覆盖 A 股 / 港股 / 美股 / 沪深 ETF / 各类指数(含 20 个全球指数)**,可混着传代码。**全市场关键字是 `aShares` / `hkStocks` / `usStocks`,必须单独传**(不能与代码或另一个关键字混填;`--security all` 会被 CLI 拒并提示改用哪个)。**关键字只覆盖个股**:ETF 与各类指数(`.SH`/`.SZ`/`.BJ` 交易所指数、`.GT` 概念、`.CI`/`.SWI` 行业、`SPX.SPI` 等全球指数,清单见 `references/commands/quote.md`)须逐个传代码。全球指数 realtime 的 `volume` / `amount` / `amplitude` 与分钟 K 的 `volume` / `amount` 为 `null`,日 K 只有 `amount` 为 `null`;`tradeTime` 是交易所当地时间。`day-kline-hk` / `day-kline-us` / `index-day-kline` 已下线(接口仍可调;前两个仍用 `all`,`index-day-kline` 的 `all` 取不到数、CLI 会拒绝),**新代码别用**——它们不校验证券代码,传错返空而不报错。
|
|
33
33
|
6.2. **多标的日 K**:显式传多个 `--security` 时,「证券数 × 交易日数」不超过 `--limit`(默认 6000 / 上限 10000)走单请求;超过则 CLI 自动逐只请求并按传入顺序合并,每只各自受 `--limit` 约束,撞上的标 `partial` + `truncatedSecurities`、退出码 3。单只超 10000 行仍要缩日期区间分批。
|
|
34
34
|
7. **CLI 已内置自动化,不要手动复刻**:
|
|
35
35
|
- 翻页 → 首页拿 total 后剩余页并发拉取;🔴 **全量拉取结束会多探一行验证 `total` 是不是服务端封顶**(`total` 若只是服务端封顶值,按它翻完会停在上限、看起来却像全量)——探到就标 `partial` + `totalCapped` + 退出 3,**这时导出的是截断结果,要缩小时间范围分片拉**
|
|
@@ -156,7 +156,7 @@ vault.stock-pool.create
|
|
|
156
156
|
| 管理层讨论 / A·港·美股公告 / `alternative edb-*` 行业指标 | 前溯 **3 年** |
|
|
157
157
|
| `insight pamirs-summary` 帕米尔纪要 | **不限**(但需单独购买专家纪要库) |
|
|
158
158
|
|
|
159
|
-
⚠️ **这是官方口径,不是硬边界**:服务端按**账号**配这个时间窗口、不按接口配——**换接口绕不过去**(`indicator` 三接口与 `quote day-kline`
|
|
159
|
+
⚠️ **这是官方口径,不是硬边界**:服务端按**账号**配这个时间窗口、不按接口配——**换接口绕不过去**(`indicator` 三接口与 `quote day-kline` 同界)。整段都在界外返回 `110003`,**不是空结果**——缩窗口对这种查询无效,要把日期移进范围或联系客户经理。🔴 **区间跨过下界时各接口不同**:`quote` 日 K 与 `fundamental valuation-analysis` 从下界起返回、**不报错**(结果看着完整,要核对首行日期;估值分析 CLI 会在 stderr 提示);`indicator` 三接口与 `bond` 整批报 `110003`,把起点移进窗口再查。
|
|
160
160
|
|
|
161
161
|
### 下载规则(`--file-type` / `--content-type`)
|
|
162
162
|
|
|
@@ -209,7 +209,7 @@ vault.stock-pool.create
|
|
|
209
209
|
| 管理层讨论(财报) | `ai management-discuss-announcement` |
|
|
210
210
|
| 管理层讨论(业绩会) | `ai management-discuss-earnings-call` |
|
|
211
211
|
| 日 K(历史,A 股 / 港股 / 美股 / 沪深 ETF / 各类指数含 20 个全球指数,可混查) | `quote day-kline` |
|
|
212
|
-
|
|
|
212
|
+
| 沪深京指数日 K / 要指数名称 | `quote day-kline` 逐个传指数代码(**没有一次拿全部指数的写法**,旧命令 `index-day-kline --security all` 已取不到数、CLI 会拒绝);指数名称用 `reference securities-search --keyword <指数代码> --category index`(返回 `gtsName`) |
|
|
213
213
|
| ~~港股 / 美股日 K~~ | ⚠️ 已下线,用 `quote day-kline`(`day-kline-hk` / `day-kline-us` 仍可调但不校验代码) |
|
|
214
214
|
| 分钟 K(沪深 A 股 / ETF + 各类指数含全球指数) | `quote minute-kline`(`--security` 可重复,逐只并发合并) |
|
|
215
215
|
| 实时行情(A / 港 / 美 / 沪深 ETF / 各类指数含全球指数) | `quote realtime` |
|
|
@@ -218,7 +218,7 @@ vault.stock-pool.create
|
|
|
218
218
|
| 单证券 港股完整利润表 / 资产负债 / 现金流 | `fundamental income-statement-hk / balance-sheet-hk / cash-flow-hk` |
|
|
219
219
|
| 单证券 美股完整利润表 / 资产负债 / 现金流 | `fundamental income-statement-us / balance-sheet-us / cash-flow-us` |
|
|
220
220
|
| 单证券主营业务 / 收入结构 | `fundamental main-business` |
|
|
221
|
-
| A股单证券估值序列 / PE / PB / 历史分位 | `fundamental valuation-analysis` |
|
|
221
|
+
| A股单证券估值序列 / PE / PB / 历史分位 | `fundamental valuation-analysis`(逐自然日一行,默认只取最近 2000 行;长区间要把 `--limit` 设到不小于区间天数,撞满会退出 3;起点早于账号回溯下界时从下界起返回、不报错) |
|
|
222
222
|
| A股盈利预测 / 一致预期 | `fundamental earning-forecast` |
|
|
223
223
|
| 前十大股东 | `fundamental top-holders` |
|
|
224
224
|
| 债券基本资料 / 票面利率 / 到期日 / 债券条款 | `bond basic-info`(`--field` 写错**整批拒绝** `100003`,不会静默丢列) |
|
|
@@ -92,8 +92,10 @@ gangtise fundamental valuation-analysis --security-code <code> --indicator <name
|
|
|
92
92
|
- **市场与路由**:本命令仅支持 A 股(港股 / 美股会报 `120001`「非有效A股」)。A股单证券估值序列与估值历史分位始终优先本命令;多证券批量取一组已实现估值点值,且 `indicator search` 三项校验都通过时,才优先 EDE `cross-section` / `time-series`。港 / 美股估值历史分位当前 CLI 不支持,不能用普通 EDE 点值冒充
|
|
93
93
|
- `--indicator`(**必选**):`peTtm` 滚动PE | `pbMrq` PB | `peg` PEG | `psTtm` 滚动PS | `pcfTtm` 滚动PCF | `em` 企业倍数
|
|
94
94
|
- `--limit` 默认 2000,省略 `--start-date` 时自动查近一年
|
|
95
|
+
- 🔴 **序列按自然日逐日一行(含周末)**,行数超过 `--limit` 时接口**保留最近的行、丢掉区间开头**。CLI 撞满即标 `partial`、退出码 3 并在 stderr 说明(首行恰好就是 `--start-date` 时说明没丢,不标)。取长区间把 `--limit` 设到不小于区间天数(账号权限窗口内的十年约 3700 行,可设 `--limit 4000`)
|
|
96
|
+
- 🔴 **`--start-date` 早于账号回溯下界时,接口从下界起返回、不报错**(整段都在界外才报 `110003`)。首行晚于 `--start-date` 时 CLI 在 stderr 提示——可能是该证券上市较晚,也可能是撞了权限窗口,看首行日期判断
|
|
95
97
|
- `--skip-null`:丢弃 `value`/`percentileRank` 为 null 的行(最新交易日可能未入库)
|
|
96
|
-
- **返回字段只有 7 个**:`tradeDate` `value` `percentileRank` `average` `median` `upper1Std` `lower1Std
|
|
98
|
+
- **返回字段只有 7 个**:`tradeDate` `value` `percentileRank` `average` `median` `upper1Std` `lower1Std`,**没有 `securityCode`**。🔴 `--field` 里**至少要有一个数值列**(`tradeDate` 以外的 6 个):只传 `tradeDate`、只传不存在的名字(含 `securityCode`)时接口返回 0 行、**不报错**,读起来像「这只票没有估值数据」;数值列与不存在的名字混传时,回显的字段比每行的值多,CLI 直接报错退出 1。`tradeDate` 总在第一列返回,不用写进 `--field`(写了 CLI 会在发请求前去掉;同一字段写两次也只发一次)。**建议不传 `--field`**,证券代码本来就是你自己传进去的
|
|
97
99
|
|
|
98
100
|
## A股盈利预测 `fundamental earning-forecast`
|
|
99
101
|
|
|
@@ -278,7 +278,7 @@ gangtise indicator cross-section --indicator qte_close --security 600519.SH \
|
|
|
278
278
|
| `100003`@400 | 入参/表达式错误:`time-series` 传了「多指标 × 多证券」、`expression` 引用未声明变量、`indicatorParamList` 的 code 不在 `indicatorCodeList` 里 | 按 msg 改;多 × 多改用 `cross-section`。CLI 已在本地拦截「表达式引用未绑定变量」,不会白发一次请求 |
|
|
279
279
|
| `140002`@500 | **终态参数错**:指标必填参数缺失、枚举越界(如「参数 adjustType 的值 99 不在有效范围内 [1,2,3,4]」)、表达式语法错误 | **不重试**(CLI 已把 140002 列为终态码)。读 `search --format json` 的 `parameterList` 改参数名/取值 |
|
|
280
280
|
| `999999` | 系统故障。「无数据」不用此码,所以它基本只剩真故障。⚠️ 别把它和空表混为一谈:无数据是占位单元格(统一 `null`),空表表示整轴 code 未识别或参数名写错 | CLI 对 indicator 端点**不重试此码**;确认参数无误仍报错,稍后再试或联系平台支持 |
|
|
281
|
-
| `110003` | **超出账号数据权限的时间范围**。窗口按**账号**配、不按接口配——`cross-section` / `time-series` / `screener`
|
|
281
|
+
| `110003` | **超出账号数据权限的时间范围**。窗口按**账号**配、不按接口配——`cross-section` / `time-series` / `screener` 同界,区间**跨过**下界也整批报这个码;`quote day-kline` 在同一条界上,但跨界时从下界起返回、不报错 | 把起点移进范围;整段区间都早于下界时缩短窗口无用,**换接口绕不过去**,要更长历史联系客户经理开通 |
|
|
282
282
|
| `130001`(旧 `410004`) | 数据未找到,或**该指标无权限**(内层信封失败会带具体 msg,如"指标无权限";此码被服务端复用) | 检查查询条件与指标权限;换证券/日期仍失败多为无权限,联系管理员开通 |
|
|
283
283
|
|
|
284
284
|
### 必填参数(`140002` 的根因)
|
|
@@ -418,5 +418,5 @@ gangtise indicator cross-section --indicator scr_indu_citic --indicator scr_indu
|
|
|
418
418
|
- **发现流程**:`indicator search --format json` → 核对 `indicatorName` + `description`、`scopeList`(含 `usageRestriction`)、`parameterList`(**参数名以此为准**)→ 三项都通过才用 `cross-section` / `time-series` / `screener`
|
|
419
419
|
- **积分**:`search` 免费;`cross-section` / `time-series` / `screener` 按请求单元格数量计费,标价为每 100 单元格 A 股 0.05 / 港股 0.1 / 美股 0.2 积分,每次查询不足 100 单元格按 100 计
|
|
420
420
|
- **空结果排查顺序**:真无数据会返回占位单元格(统一 `null`)而不是空表,所以**空表基本等于「没有任何 code 被认出来」或参数名写错**。按序排查:① 证券代码与后缀对不对(美股 `.O`/`.N`,不是 `.US`)② 指标 code 拼写对不对 ③ 参数名对不对(`indicator search` 的 `parameterList`)④ 日期语义对不对(`tradeDate` vs `reportDate`——报告期类指标日期用错会整批返 `null`,看着像「没数据」)
|
|
421
|
-
- **数据权限**:正式账号行情 / 财务 / 指标类可回溯的年限按服务等级而定,试用账号更短。这个时间窗口按**账号**配、不按接口配——三个 EDE
|
|
421
|
+
- **数据权限**:正式账号行情 / 财务 / 指标类可回溯的年限按服务等级而定,试用账号更短。这个时间窗口按**账号**配、不按接口配——三个 EDE 接口同界,区间跨过下界也整批返 `110003`(`quote day-kline` 在同一条界上,但跨界时从下界起返回、不报错),**换接口绕不过去**;整段区间都早于下界时缩短窗口无用,要更长历史联系客户经理开通
|
|
422
422
|
- 所有格式(table/json/jsonl/csv/markdown)均可用;导出宽表给 Excel 直接用 `--format csv --output xxx.csv`
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**关键规则**:查"最近"K线必须显式 `--start-date`/`--end-date` 拉范围,再从 `tradeDate` 取尾部最近 N 条;不要只用 `--limit N`(会截取查询窗口开头)。
|
|
8
8
|
|
|
9
|
-
🔴 **全市场关键字**:`quote day-kline` 不认 `--security all`,只认三个市场关键字之一——`aShares` / `hkStocks` / `usStocks`,且**必须单独传**(不能与证券代码或另一个关键字混填)。传 `all` 或混填 CLI
|
|
9
|
+
🔴 **全市场关键字**:`quote day-kline` 不认 `--security all`,只认三个市场关键字之一——`aShares` / `hkStocks` / `usStocks`,且**必须单独传**(不能与证券代码或另一个关键字混填)。传 `all` 或混填 CLI 会直接报错并提示正确写法。已下线的旧命令里,`day-kline-hk` / `day-kline-us` 仍认 `all`;`index-day-kline` 对 `all` 返回空结果,CLI 在发请求前直接拒绝——**全部沪深京指数没有一次拿全的写法**,要逐个传指数代码。
|
|
10
10
|
|
|
11
11
|
**自动分片**:全市场关键字跨日期范围时 CLI 自动按日切片并并发执行,合并结果返回,无需手动分批。分片粒度按各市场单个交易日的行数规模定,保证单请求不撞 10000 行的 API 上限:
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| `day-kline` | `hkStocks` | 2 天/片 |
|
|
17
17
|
| `day-kline` | `usStocks` | 1 天/片 |
|
|
18
18
|
| `fund-flow` | `aShares` | 1 天/片 |
|
|
19
|
-
| `day-kline-hk` / `day-kline-us
|
|
19
|
+
| `day-kline-hk` / `day-kline-us`(旧) | `all` | 2 / 1 天/片 |
|
|
20
20
|
|
|
21
21
|
分片路径会自动把 `limit` 抬到 10000(API 上限),避免默认 6000 行截断;按日分片自动跳过周六日。
|
|
22
22
|
|
|
@@ -73,7 +73,7 @@ gangtise quote day-kline [--security <code>] [--start-date <YYYY-MM-DD>] [--end-
|
|
|
73
73
|
|
|
74
74
|
- 旧命令**不校验证券代码**——传错代码返回空结果(`{"total":0,"list":[]}`)而不是报错,与「该票该区间真无数据」无法区分;`day-kline` 会报 `120001`
|
|
75
75
|
- 参数与字段和 `day-kline` 完全一致(`--security` / `--start-date` / `--end-date` / `--limit` / `--field`),迁移只是换命令名
|
|
76
|
-
-
|
|
76
|
+
- `day-kline-hk` / `day-kline-us` 的全市场关键字仍是 `all`(不是 `aShares` 那套);`index-day-kline` 没有全市场关键字,只收明确的指数代码
|
|
77
77
|
|
|
78
78
|
## 实时行情 `quote realtime`
|
|
79
79
|
|
|
@@ -118,14 +118,11 @@ gangtise quote fund-flow [--security <code>] [--start-date <YYYY-MM-DD>] [--end-
|
|
|
118
118
|
gangtise quote index-day-kline [--security <code>] [--start-date <YYYY-MM-DD>] [--end-date <YYYY-MM-DD>] [--limit <n>] [--field <name>]
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
- 沪深京指数:如 `000001.SH` 上证综指、`399001.SZ`
|
|
121
|
+
- 沪深京指数:如 `000001.SH` 上证综指、`399001.SZ` 深成指,可重复传;**没有全市场关键字**——`--security all` 在这个接口上返回空结果,CLI 直接拒绝
|
|
122
122
|
- `--limit` 默认 6000,上限 10000
|
|
123
|
-
-
|
|
124
|
-
- `
|
|
125
|
-
-
|
|
126
|
-
1. **一次拿全部沪深京指数**(`--security all`)——`day-kline` 的指数必须逐个传代码
|
|
127
|
-
2. **要指数名称**——`index-day-kline` 返回 `securityName`(如「上证指数」),`day-kline` **没有这个字段**,查指数只拿得到代码
|
|
128
|
-
- 反过来 `day-kline` 独有 `adjustFactor`(复权因子),但指数本来就没有复权,该字段查指数时恒为 `null`
|
|
123
|
+
- 返回字段与 `day-kline` 相同:`securityCode` `tradeDate` `open` `high` `low` `close` `preClose` `change` `pctChange` `volume` `amount` `adjustFactor`(指数恒为 `null`)。**不含指数名称**
|
|
124
|
+
- 没有 `day-kline` 做不到的能力,新代码直接用 `day-kline` 传同样的代码
|
|
125
|
+
- **要指数名称**:`gangtise reference securities-search --keyword 000001.SH --category index`,返回的 `gtsName` 即名称(如「上证指数」)
|
|
129
126
|
|
|
130
127
|
## 分钟 K 线 `quote minute-kline`
|
|
131
128
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 错误码、退出码与常见困境自救
|
|
2
2
|
|
|
3
3
|
> 主文件 `SKILL.md` 只保留最高频的几个码;这里是全表。
|
|
4
|
-
> **退出码语义**:`0` 完整成功(含合法空结果)/ `3` 有数据但不完整(`partial: true`)/ `1` 硬失败。
|
|
4
|
+
> **退出码语义**:`0` 完整成功(含合法空结果)/ `3` 有数据但不完整(`partial: true`)/ `4` 导出的数据完整,但 `--output` 已被另一次导出替换(见下文「退出码 4」)/ `1` 硬失败。
|
|
5
5
|
> 报错行末尾的 `[trace <id>]` 是 Gangtise 支持定位问题的唯一凭据,**报障时带上它**。
|
|
6
6
|
|
|
7
7
|
## 错误码怎么读
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| `100001` | 缺必填参数——**msg 带字段名**(「缺少必填参数: reportId」) | — | 按 msg 指的字段补上 |
|
|
22
22
|
| `110001` / `110002` | 日期格式错(msg 带字段名)/ 起晚于止。⚠️ **服务端对多种格式做宽松解析**(`2026/07/01`、`20260701`、`07/01/2026` 都能被接受),所以「没报 110001」不等于「格式被按你的意思理解了」 | — | 按参数名:`--*-date` 用 `YYYY-MM-DD`、`--*-time` 用 `YYYY-MM-DD HH:mm:ss`;`ai knowledge-batch` 的 --start-time/--end-time 收时间戳或 datetime,CLI 统一转 13 位毫秒 |
|
|
23
23
|
| `120001` | 证券代码无效——msg 带原因(「非有效A股」)。Fundamental 系与 `quote day-kline`/`realtime`/`minute-kline`/`fund-flow` 都会报;**旧版 `day-kline-hk`/`day-kline-us`/`index-day-kline` 静默返回空** | — | `reference securities-search` 确认代码与后缀(`600519.SH` / `00700.HK`) |
|
|
24
|
-
| `110003` | **超出账号数据权限的时间范围**。范围按账号等级而定、**不是平台常量**;窗口按**账号**配、不按接口配 | — | 把日期移进权限范围内;整个区间都早于下界时缩短窗口无用(`--fiscal-year 2015`
|
|
24
|
+
| `110003` | **超出账号数据权限的时间范围**。范围按账号等级而定、**不是平台常量**;窗口按**账号**配、不按接口配 | — | 把日期移进权限范围内;整个区间都早于下界时缩短窗口无用(`--fiscal-year 2015` 无论怎么缩都报错);**换接口绕不过去**,要更长历史联系客户经理开通。⚠️ 区间**跨过**下界时,`indicator` 与 `bond` 报这个码,`quote` 日 K 与 `valuation-analysis` 则从下界起返回、不报错——核对首行日期 |
|
|
25
25
|
| `100006` | 查询/下载数量超限(旧码 `430007`);`fund-flow` 全市场不传日期即此码;**`indicator cross-section` / `time-series` 单次单元格数超 30000** 也是此码(msg 写明限额) | — | 缩短日期范围或调小 `--size`/`--limit`;全市场场景应已自动分片;EDE 按「截面=证券数×指标数、时序=证券数或指标数×日期数」拆批 |
|
|
26
26
|
| `240001` | 财报期未披露或超出查询期(`earnings-review` 提交阶段就报,**不扣积分**) | — | 换更早的 `--period`(`2025q3` → `2025interim`) |
|
|
27
27
|
| `250001` | 不支持的数据源(旧码 `433007`) | — | 检查 `resourceType + sourceId` 组合 |
|
|
@@ -76,12 +76,14 @@
|
|
|
76
76
|
|
|
77
77
|
| 字段 | 含义 | 字段 | 含义 |
|
|
78
78
|
|------|------|------|------|
|
|
79
|
-
| `securityCode` | 指数代码 | `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
79
|
+
| `securityCode` | 指数代码 | `tradeDate` | 交易日期 |
|
|
80
|
+
| `open` | 开盘价 | `high` | 最高价 |
|
|
81
|
+
| `low` | 最低价 | `close` | 收盘价 |
|
|
82
|
+
| `preClose` | 昨收价 | `change` | 涨跌额 |
|
|
83
|
+
| `pctChange` | 涨跌幅(%) | `volume` | 成交量(股) |
|
|
84
|
+
| `amount` | 成交总额(元) | `adjustFactor` | 复权因子(指数恒为 `null`) |
|
|
85
|
+
|
|
86
|
+
与 `day-kline` 查指数的返回完全相同,**不含指数名称**;要名称用 `reference securities-search --keyword <指数代码> --category index` 的 `gtsName`。
|
|
85
87
|
|
|
86
88
|
---
|
|
87
89
|
|
|
@@ -202,7 +204,7 @@
|
|
|
202
204
|
|
|
203
205
|
### 估值分析 (`fundamental valuation-analysis`)
|
|
204
206
|
|
|
205
|
-
> ⚠️ `--field` 写错字段名时**只丢值、字段名照回显**,CLI 会因长度不匹配报错退出 1
|
|
207
|
+
> ⚠️ `--field` 写错字段名时**只丢值、字段名照回显**,CLI 会因长度不匹配报错退出 1——见本文顶部「字段名写错时,两族接口的表现完全不同」。🔴 例外:`--field` 里一个数值列都没有时(只传 `tradeDate`、或只有不存在的名字),接口返回 0 行、不报错。 `tradeDate` 总在第一列返回,不用写进 `--field`(写了 CLI 会自动去掉)。
|
|
206
208
|
|
|
207
209
|
| 字段 | 含义 | 字段 | 含义 |
|
|
208
210
|
|------|------|------|------|
|
|
@@ -61,7 +61,7 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
|
|
|
61
61
|
| reference concept-search | `{returnedCount, list}` | `list[].conceptId` / `list[].conceptName` / `list[].matchScore` |
|
|
62
62
|
| reference sector-search | `{returnedCount, list}` | `list[].sectorId` / `list[].sectorName` / `list[].hierarchy`(层级路径) / `list[].matchScore` |
|
|
63
63
|
| reference sector-constituents | `{total, list}` | `list[].gtsCode` / `list[].gtsName`;total=0 说明 sectorId 不对(先 sector-search 确认) |
|
|
64
|
-
| quote day-kline(及已下线的 day-kline-hk / day-kline-us / index-day-kline) | `{fieldList, list}` 或规范化后 `{list: [{...}]}` | `tradeDate` / `securityCode` / `open` / `close` / `pctChange` / `volume` / `amount`(全球指数为 `null`)/ `adjustFactor`(指数为 `null`,ETF
|
|
64
|
+
| quote day-kline(及已下线的 day-kline-hk / day-kline-us / index-day-kline) | `{fieldList, list}` 或规范化后 `{list: [{...}]}` | `tradeDate` / `securityCode` / `open` / `close` / `pctChange` / `volume` / `amount`(全球指数为 `null`)/ `adjustFactor`(指数为 `null`,ETF 有值);四者字段相同,均不含指数名称 |
|
|
65
65
|
| quote minute-kline | `{list: [{...}]}` | `tradeTime` / `open` / `close` / `volume` |
|
|
66
66
|
| quote realtime | `{fieldList, list, total}` 或规范化后 `{list: [{...}]}` | `securityCode` / `exchange` / `tradeDate` / `tradeTime` / `tradeStatus` / `open` / `high` / `low` / `latestPrice` / `preClose` / `change` / `pctChange` / `volume` / `amount` / `amplitude`(共 15 个,**无 `close`、无市值、无 `turnoverRate` / `volumeRatio`**;美股 `amount`、全球指数 `volume` / `amount` / `amplitude` 为 `null`) |
|
|
67
67
|
| quote fund-flow | `{fieldList, list, total}` 列式 → 规范化后 `{list: [{...}], total}` 宽表 | `securityCode` / `tradeDate` + 请求的字段(`mainNetInflow` / `largeInflow` / `xlargeOutflow` / …) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gangtise-openapi-cli",
|
|
3
|
-
"version": "0.41.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "CLI for Gangtise OpenAPI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"commander": "^14.0.0",
|
|
40
|
-
"undici": "^7.
|
|
40
|
+
"undici": "^7.29.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^20.19.43",
|