gangtise-openapi-cli 0.48.0 → 0.50.0

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.
Files changed (32) hide show
  1. package/README.md +40 -13
  2. package/dist/src/cli.js +9 -3
  3. package/dist/src/commands/ai.js +2 -2
  4. package/dist/src/commands/fund.js +5 -3
  5. package/dist/src/commands/groups.js +2 -1
  6. package/dist/src/commands/indicator.js +9 -14
  7. package/dist/src/commands/insight.js +10 -3
  8. package/dist/src/commands/quote.js +19 -0
  9. package/dist/src/commands/shared.js +2 -1
  10. package/dist/src/commands/update.js +30 -0
  11. package/dist/src/commands/vault.js +49 -4
  12. package/dist/src/core/endpoints.js +40 -4
  13. package/dist/src/core/errors.js +26 -12
  14. package/dist/src/core/etfComponents.js +358 -0
  15. package/dist/src/core/normalize.js +1 -1
  16. package/dist/src/core/selfUpdate.js +94 -0
  17. package/dist/src/core/updateCheck.js +50 -14
  18. package/dist/src/version.js +5 -3
  19. package/gangtise-openapi/SKILL.md +25 -19
  20. package/gangtise-openapi/references/commands/ai.md +1 -1
  21. package/gangtise-openapi/references/commands/fund.md +6 -2
  22. package/gangtise-openapi/references/commands/indicator.md +41 -20
  23. package/gangtise-openapi/references/commands/insight.md +4 -1
  24. package/gangtise-openapi/references/commands/quote.md +20 -0
  25. package/gangtise-openapi/references/commands/reference-and-lookup.md +1 -1
  26. package/gangtise-openapi/references/commands/vault.md +54 -8
  27. package/gangtise-openapi/references/errors.md +3 -3
  28. package/gangtise-openapi/references/examples.md +1 -1
  29. package/gangtise-openapi/references/fields.md +1 -1
  30. package/gangtise-openapi/references/response-schema.md +10 -5
  31. package/package.json +1 -1
  32. package/dist/src/core/calendarType.js +0 -44
@@ -1,6 +1,6 @@
1
1
  # Vault 命令详细参数(私域数据)
2
2
 
3
- 列表类命令大多有 `--keyword` `--start-time` `--end-time` `--from` `--size`;`wechat-chatroom-list` 只有 `--from` / `--size`,股票池的查询命令都没有这些选项。`my-conference-list` 按条计费,省略 `--size` 时估算全量超过 1000 积分会报错退出 1,确认后加 `--yes` 或改传 `--size N`。
3
+ 列表类命令大多有 `--keyword` `--start-time` `--end-time` `--from` `--size`;`wechat-chatroom-list` 只有 `--from` / `--size`,股票池的查询命令都没有这些选项,笔记查询只有 `--from` / `--size`。`my-conference-list` 按条计费,省略 `--size` 时估算全量超过 1000 积分会报错退出 1,确认后加 `--yes` 或改传 `--size N`。
4
4
 
5
5
  ---
6
6
 
@@ -101,24 +101,27 @@ gangtise vault wechat-chatroom-list [--room-name <name>] [--from <n>] [--size <n
101
101
 
102
102
  ## 自选股股票池 `vault stock-pool-*`
103
103
 
104
- 查询两个、增删改五个,全部免费,只操作当前账号本人的数据。
104
+ 查询两个、增删改五个,全部免费。查询可看个人股票池或所属租户的股票池,增删改只操作当前账号本人的个人股票池。
105
105
 
106
106
  ### 查询
107
107
 
108
108
  ```bash
109
- gangtise vault stock-pool-list
110
- gangtise vault stock-pool-stocks [--pool-id <id>]
109
+ gangtise vault stock-pool-list [--space-type 1|2]
110
+ gangtise vault stock-pool-stocks [--space-type 1|2] [--pool-id <id>]
111
111
  ```
112
112
 
113
- - `stock-pool-list`:查询当前用户的全部股票池,返回 `poolId` / `poolName`
113
+ - `--space-type`:`1` 个人自选股股票池(不传即此项)、`2` 所属租户的股票池(终端租户页签下的池;页签名不是池名)。一次只查一种。无租户股票池查询权限时报 `999004`;有权限但没有可见的池返回空列表
114
+ - `stock-pool-list`:查询股票池列表,返回 `poolId` / `poolName` / `spaceType`
114
115
  - `stock-pool-stocks`:查询股票池中的证券明细
115
- - `--pool-id`:股票池 ID,可重复;不传默认 `all`(返回所有池中的非重复证券)
116
- - 传入 `--pool-id all` 等同于全量查询,最多返回 10000 只
116
+ - `--pool-id`:股票池 ID,可重复;不传默认 `all`(返回该 `--space-type` 下所有可见池中的非重复证券)
117
+ - 🔴 **`--pool-id` 要取自同一 `--space-type` 的 `stock-pool-list`**:另一种空间的 ID 不会被匹配——与其他有效 ID 一起传时被静默忽略,全部不匹配时报 `999004`「股票池不存在或无权访问」。租户池的明细要带 `--space-type 2`
118
+ - 按完整 `securityCode` 去重合并,不标注来自哪个池;要逐池归属就逐池查
119
+ - 单次最多返回 10000 只,超出截断且没有截断标记,返回恰好 10000 只时不能当作已取全(逐池查可降低截断)
117
120
  - 返回字段:`securityCode` / `securityName`
118
121
 
119
122
  ### 增删改
120
123
 
121
- 🔴 **这五个命令会改动账号数据**(与上面的云盘管理命令同属写操作);`--pool-id` 一律取自 `stock-pool-list`。
124
+ 🔴 **这五个命令会改动账号数据**(与上面的云盘管理命令同属写操作);`--pool-id` 一律取自不带 `--space-type` 的 `stock-pool-list`(个人股票池)。
122
125
 
123
126
  ```bash
124
127
  gangtise vault stock-pool-create --name <名称>
@@ -146,3 +149,46 @@ gangtise vault stock-pool-delete --pool-id <id> [--pool-id <id>...] --yes
146
149
  **幂等**:重复加已在池内的证券、移出本就不在池内的证券、删不存在的 `poolId`,都算成功并计入 `successList`,不会报错。唯一不幂等的是 `stock-pool-create`(见上面的重名规则)。
147
150
 
148
151
  **返回字段**:`create` / `rename` 返回 `poolId` / `poolName`;`add-stock` / `remove-stock` / `delete` 返回 `successList[]` + `failList[]{securityCode 或 poolId, failReason}`。
152
+
153
+ ## 个股投资笔记 `vault stock-note-*`
154
+
155
+ 查询、新增、改写、删除四个命令,全部免费,只操作当前账号本人写的笔记(别人的笔记查不到、改不了、删不到,也不会暴露是否存在)。笔记与证券是多对多:一条笔记关联 1~50 只证券,同一只证券可以有多条笔记,每条笔记以 `stockNoteId` 区分。笔记与自选关注相互独立——取消关注、删股票池都不会删笔记。
156
+
157
+ ```bash
158
+ gangtise vault stock-note-list [--security <code>... | --note-id <id>...] [--from <n>] [--size <n>]
159
+ gangtise vault stock-note-add --security <code> [--security <code>...] --content <正文>
160
+ gangtise vault stock-note-update --note-id <id> --content <新正文>
161
+ gangtise vault stock-note-delete --note-id <id> [--note-id <id>...] --yes
162
+ ```
163
+
164
+ ### 查询 `stock-note-list`
165
+
166
+ - 不传筛选 = 本人**全部**笔记(含已取消关注的证券下仍保留的笔记);`--security` 按证券筛,笔记关联的证券里**含其中任一只**即命中;`--note-id` 按笔记 ID 精确查。`--security` 与 `--note-id` 二选一,同传本地报错
167
+ - `--security` / `--note-id` 各最多 100 个;代码大小写、首尾空格不影响匹配,但港股要 5 位(`00700.HK`,`700.HK` 不会补零)、美股要用交易所后缀(`AAPL.O`,`AAPL.US` 不会转换)。**代码写错、不存在或类型不支持时整次报 `120001`**,不会当成「这只票没有笔记」返回空;有效代码没有笔记才是空结果
168
+ - 不存在或不属于本人的笔记 ID 被直接过滤,不计入 `total`、不报错
169
+ - 自动翻页,省略 `--size` 取全部;按 `createTime` 倒序(同秒按 ID 倒序),同时命中多只证券的笔记只出现一次。翻页期间有增删时偏移会变,不保证跨页一致
170
+ - 返回字段:`stockNoteId` / `securityList`(该笔记**完整**的关联证券,按代码升序,不只是你筛的那几只)/ `content`(纯文本原样,保留换行;Markdown 只是文字)/ `createTime` / `updateTime`(北京时间,精确到秒;正文没变的提交不刷新 `updateTime`)
171
+ - 默认输出 json(正文常含换行,表格不便阅读)
172
+
173
+ ### 新增 `stock-note-add`
174
+
175
+ - `--security`:1~50 只(按规范化去重后计数),可重复或逗号分隔。可关联 A 股、港股、美股(`.O` / `.N` 等)、ETF、交易所指数、概念指数(`.GT`)、中信行业指数(`.CI`)、全球指数(如 `SPX.SPI`),可混传;**申万行业指数(`.SWI`)不支持**。任一只无效则整条不建,报 `120001` 并点名
176
+ - `--content`:纯文本,不能为空或全是空白,最长 10000 个字符(按 Unicode 码点计,首尾空白和换行都算);原样保存,不裁剪
177
+ - 返回 `stockNoteId` 与实际关联的 `securityList`(规范化代码、升序)
178
+ - 🔴 **关联的证券建好后不能改**:要换关联范围就新增一条、核对无误后再删旧的(两步不是原子操作,中间会短暂同时存在两条)
179
+ - 🔴 **超时或 5xx 不会自动重发,也不要直接重跑**:服务端没有防重机制,同样的请求再提交一次就是第二条笔记。超时后先 `stock-note-list --security <code>` 看是否已经写入
180
+
181
+ ### 改写 `stock-note-update`
182
+
183
+ - `--note-id` 取自 `stock-note-list`;`--content` **整体替换**旧正文(不是追加),规则同新增
184
+ - 只改正文,关联证券不变;一条笔记关联多只证券时,改动在每只证券下都可见
185
+ - 成功返回 `{}`;`updateTime` 从查询里看。不存在或不属于本人返回 `130002`「笔记不存在或无权操作」
186
+ - 没有版本控制,并发改写以最后提交的为准
187
+
188
+ ### 删除 `stock-note-delete`
189
+
190
+ - **必须显式加 `--yes`**(不加直接报错退出,不发请求);删除的是整条笔记连同它与全部证券的关联,**不可恢复**。`raw call vault.stock-note.delete` 同样要 `--yes`
191
+ - `--note-id` 可重复或逗号分隔,一次最多 100 个;**只接受明确的 ID,没有 `all`**——批量删先 `stock-note-list` 拿 ID 核对
192
+ - 幂等:已删掉的、不存在的、不属于本人的 ID 都进 `successList`(不会删到别人的笔记),重复执行仍算成功
193
+ - 部分失败同股票池写命令:服务端仍返回成功信封、把失败项放进 `failList`,CLI 在 stderr 列出、标 `partial`、**退出码 3**;拿到结果后只需对 `failList` 里的 ID 重试
194
+ - 返回字段:`successList[]` + `failList[]{stockNoteId, failReason}`
@@ -16,13 +16,13 @@
16
16
  | `999999` | 系统错误。`indicator`(EDE)的「无数据」不用此码(无数据是保留行列的占位单元格 `null`,见 `commands/indicator.md`;代码或参数名写错报 `100003`、缺必填参数报 `100001`,都会点名),所以这个码基本只剩真故障 | 普通端点自动重试 ×2;🔴 贵档与 `indicator` 端点不重试 | 确认参数无误仍报此码即服务端故障 |
17
17
  | `410110` | **异步生成中**(HTTP 400,旧码)。新码 `140001`,CLI 两码都认 | 轮询视为 pending | 继续等 |
18
18
  | `410111` | **异步生成失败**(HTTP 400,旧码)。新码 `140002`,CLI 两码都认 | 终态 | **不重试**,换参数 |
19
- | `130002` | 资源不存在——下载类的常见码:`reportId` 不存在 / 非数字多归此码。另有更具体的 `130003`(资源未生成 / 无附件);`--file-type` 写错由 CLI 在本地报错、不发请求 | — | 确认 ID 有效且本账号可见;换 `--file-type` 或换一篇验证 |
19
+ | `130002` | 资源不存在——下载类的常见码:`reportId` 不存在 / 非数字多归此码。另有更具体的 `130003`(资源未生成 / 无附件);`--file-type` 写错由 CLI 在本地报错、不发请求。`vault stock-note-update` 传了不存在或不属于本人的笔记 ID 也报此码(`stock-note-delete` 不报,这类 ID 计入 `successList`) | — | 确认 ID 有效且本账号可见(笔记 ID 用 `vault stock-note-list` 核对);换 `--file-type` 或换一篇验证 |
20
20
  | `130001` | 数据未找到,或**该指标无权限**(`indicator` 内层失败会带具体 msg 如"指标无权限") | — | 检查查询条件与指标权限 |
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
24
  | `110003` | **超出账号数据权限的时间范围**。范围按账号等级而定、**不是平台常量**;窗口按**账号**配、不按接口配 | — | 把日期移进权限范围内;整个区间都早于下界时缩短窗口无用(`--fiscal-year 2015` 无论怎么缩都报错);**换接口绕不过去**,要更长历史联系客户经理开通。⚠️ 区间**跨过**下界时,`indicator`、`bond` 与 `fund` 报这个码,`quote` 日 K 与 `valuation-analysis` 则从下界起返回、不报错——核对首行日期 |
25
- | `100006` | 查询/下载数量超限;**`indicator cross-section` / `time-series` 单次单元格数超 30000**、**`fund` 命令单次超过 10000 行**也是此码(msg 写明限额) | — | 缩短日期范围或调小 `--size`/`--limit`;全市场场景应已自动分片;EDE 按「截面=证券数×指标数、时序=证券数或指标数×日期数」拆批;`fund` 不分页、没有 `--size`,减少 `--security` 只数或缩短日期区间分批查 |
25
+ | `100006` | 查询/下载数量超限;**`indicator cross-section` / `time-series` 单次单元格数超 30000**、**`fund` 命令单次超过 10000 行**、**`quote sector-fund-flow` 计算量超限**也是此码(msg 写明限额) | — | 缩短日期范围或调小 `--size`/`--limit`;全市场场景应已自动分片;EDE 按「截面=证券数×指标数、时序=证券数或指标数×日期数」拆批;`fund` 不分页、没有 `--size`,减少 `--security` 只数或缩短日期区间分批查;`quote sector-fund-flow` 缩短日期区间、减少 `--sector` 或 `--field`,**调小 `--limit` 无效**(它只截返回行数,不减计算量) |
26
26
  | `240001` | 财报期未披露或超出查询期(`earnings-review` 提交阶段就报,**不扣积分**) | — | 换更早的 `--period`(`2025q3` → `2025interim`) |
27
27
  | `250001` | 不支持的数据源 | — | 检查 `resourceType + sourceId` 组合 |
28
28
  | `999011` | 开发账号凭证无效(不区分 AK 错还是 SK 错) | 登录即失败,**不重试** | 检查 `GANGTISE_ACCESS_KEY`/`GANGTISE_SECRET_KEY` 是否写反或未 export |
@@ -76,7 +76,7 @@
76
76
  `partial: true`,保留已取到的部分;stderr 有 warning,`--format json` 才看得见标记,table/csv/jsonl 只有数据行、看不出问题。**拿到 3 就必须告知用户缺了哪段,不能当成功静默继续。**
77
77
 
78
78
  - 触发场景:翻页 / K 线分片有页失败、服务端返回行数与 `total` 矛盾(提前短页)、`total` 撞服务端上限(`totalCapped`:`total` 是封顶值而非真实条数,或正好等于偏移窗口、窗口外的行取不到也数不到)、`quote` 系带 `--field` 时请求了但服务端没回的列(`missingFields`)。附带定位字段:页失败 `failedPages`;分片失败 `failedShards`、分片撞行数上限 `truncatedShards`(均带日期区间可缩窗补拉)、只有部分分片返回的列 `droppedColumns`(合并结果的列集取自第一个有数据的分片,多出来的列放不下,需要就缩小日期区间单独拉);缺列 `missingFields`(字段名写错或已下线,按 `references/fields.md` 核对);多证券分批请求里撞行数上限的证券 `truncatedSecurities`;翻页时同一行在相邻两页各出现一次 `duplicateRows`(列表按时间这类可重复的字段排序,同一时刻的一组数据跨在页边界上时,重复了几行也就漏了几行;重复行已去掉,缩短时间范围重拉可补齐);同一 ID 在后面的页以不同内容再次出现 `changedRows`(翻页期间列表有变动、也可能漏了行;两版都保留,按 ID 去重只留一版,重拉即可;重拉后仍在同一处出现,说明该列表同一 ID 对应多条记录,按 ID 去重前先核对)。EDE 的 `omittedIndicators` / `omittedSecurities`:服务端对写错的 code 直接报 `100003`(退出 1),所以这两个标记很少出现
79
- - **EDE 代码写错是退出码 1 + `100003`,msg 里直接点名是哪个 code**(美股后缀用 `.O`/`.N`,不是 `.US`),不用从退出码 3 反推。真的没覆盖是占位单元格(统一 `null`)+ 退出码 0
79
+ - **EDE 代码写错是退出码 1 + `100003`,msg 里直接点名是哪个 code**(美股后缀用 `.O`/`.N`/`.A`,不是 `.US`),不用从退出码 3 反推。真的没覆盖是占位单元格(统一 `null`)+ 退出码 0
80
80
  - **`screener` 的缺列按表达式的布尔结构判**:把缺列的变量当作「无法求值」,看整个表达式**是否还有一条能成立的分支**——`A && B` 要两边都可求值,`A || B` 只要一边。**一条分支都不剩 → 退出码 1、不输出任何结果**(返回的行以「通过了该条件」的名义呈现,而条件根本无法证明被执行过);**还有分支可求值,或缺的只是未参与表达式的辅助变量 → `partial` + 退出码 3**。所以 `F1 || F2` 缺 F1 是降级,缺两个则是致命;`F1 && (F2 || F3)` 缺 F1 仍是致命。⚠️ **前提是服务端返回了命中行**——零命中时走「nothing matched」+ **退出码 0**。**空集别急着当成「条件成立但无标的符合」**:指标码写错会直接报 `100003`(退出 1),剩下**三种成因产生逐字相同的输出**——① 真的没有标的满足条件;② **日期没落在报告期末**(报告期类指标此时整批 `null`);③ **该指标不覆盖这批证券**(拿 A 股专属指标查港美股,如 `mgn_*` 融资融券系列)。②③ 都要靠单查一行确认,不能只改日期
81
81
 
82
82
  ## 退出码 4:数据写全了,但文件被另一次导出替换
@@ -269,7 +269,7 @@
269
269
  --date <最新交易日> --key-by code --format json
270
270
  4. 按 security 合并两张宽表(列头即 indicatorCode,各取所需日期的值);不要把不同日期语义的指标塞进同一个 --date。
271
271
  5. 计费:search 免费;两次取数各按请求单元格数量计费,每次不足 100 单元格按 100 计。
272
- 6. 无数据(无覆盖 / 非交易日 / 未来日期)一律保留行列并给占位单元格,退出码 0——占位值统一是 null(见 commands/indicator.md);⚠️ 报告期类指标(is_*)的时序只有报告期末那几行是真值,别对整列手工求均值。代码写错或参数名写错则直接报 100003 并点名是哪个(指标码拼错、证券后缀错如美股写成 .US、参数名写错、同 code 重复配置),按报错改即可。
272
+ 6. 无数据(无覆盖 / 非交易日 / 未来日期)一律保留行列并给占位单元格,退出码 0——占位值统一是 null(见 commands/indicator.md);⚠️ 报告期类指标(is_*)的时序与交易日类指标混查、或显式传 ND / TD / WD 时,只有报告期末那几行是真值,别对整列手工求均值。代码写错或参数名写错则直接报 100003 并点名是哪个(指标码拼错、证券后缀错如美股写成 .US、参数名写错、同 code 重复配置),按报错改即可。
273
273
  ```
274
274
 
275
275
  ## 例 15b:帕米尔专家纪要(独立库,筛选项与踩坑都和 summary 不同)
@@ -9,7 +9,7 @@
9
9
  | 接口 | 响应里发生什么 | CLI 怎么处理 |
10
10
  | :-- | :-- | :-- |
11
11
  | `quote realtime` / `day-kline` / `minute-kline` / `fund-flow` | **不报错,字段名和值一起消失**,`fieldList` 里也没有它,列数与值数始终对得上 | 比对请求与返回的列名,缺列标 `partial` + `missingFields`、在 stderr 点名、**退出码 3** |
12
- | `fundamental main-business` / `valuation-analysis` | 整条报 `100005`,msg 点名写错的名字(`fieldList 枚举值非法: <名字>`) | 原样报错,退出 1 |
12
+ | `fundamental main-business` / `valuation-analysis`、`quote sector-fund-flow` | 整条报 `100005`,msg 点名写错的名字(`fieldList 枚举值非法: <名字>` / `fieldList 包含不支持的字段 [<名字>]`) | 原样报错,退出 1 |
13
13
  | `bond` / `fund` | 整条报 `100003` 并点名 | 原样报错,退出 1 |
14
14
 
15
15
  **行情这一族才是要留神的那个**:结果只是少一列、看着正常。绕过 CLI 直连时,`fieldList` 与行**始终等长**,只比长度什么都查不出来——要拿**请求的字段名**去比**返回的 `fieldList`**,少了哪个就是那个名字没被认出来。
@@ -4,7 +4,7 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
4
4
 
5
5
  > 例外:`indicator`(EDE)四个接口(`search` / `cross-section` / `time-series` / `screener`)成功时**双层信封**(`data` 里再裹一层 `{code, status, data}`)。内层字段名为 `securityCodeList` / `securityNameList` / `indicatorList[{code,name,dataType}]`(screener 另带 `field`),`values` 是 2D 矩阵:**截面与 screener 为 `[证券][指标]`**、时序为 `[序列][日期]`。
6
6
  >
7
- > **缺数据 vs 代码写错**:无数据 / 无覆盖一律保留行列并给占位单元格——**占位值统一为 `null`**;⚠️ 报告期类指标(`is_*`)的时序只有报告期末那几行是真值,其余全是 `null`,详见 `commands/indicator.md`。**代码写错则直接报 `100003` 并点名**(指标 code 拼错 →「指标 xxx 不存在」;证券 code 后缀错 →「xxx 不是有效证券或者板块ID」,美股是 `.O`/`.N` 不是 `.US`),**无论同批有没有正确的 code 都会报**。CLI 的 `indicator` 子命令已二次解包、拍平成宽表;服务端没有返回请求里的某个指标或证券时标 `partial` + `omittedIndicators` / `omittedSecurities`、退出码 3(写错的 code 会直接报 `100003`,所以这种情况很少出现)。**`screener` 例外**:把缺列的变量当作无法求值,按表达式的**布尔结构**判断是否还有分支能成立(`A && B` 要两边、`A || B` 只要一边)。一条都不剩 → **退出码 1 且不输出**(那些行以「通过了该条件」的名义呈现,而条件根本无法证明被执行过);仍有分支可求值、或缺的只是输出用的辅助变量 → `partial` + 退出码 3。直接 `raw call indicator.*` 只会剥外层,内层需自行处理。
7
+ > **缺数据 vs 代码写错**:无数据 / 无覆盖一律保留行列并给占位单元格——**占位值统一为 `null`**;⚠️ 报告期类指标(`is_*`)的时序与交易日类指标混查、或显式传 `ND` / `TD` / `WD` 时,只有报告期末那几行是真值,其余全是 `null`,详见 `commands/indicator.md`。**代码写错则直接报 `100003` 并点名**(指标 code 拼错 →「指标 xxx 不存在」;证券 code 后缀错 →「xxx 不是有效证券或者板块ID」,美股是 `.O`/`.N`/`.A` 不是 `.US`),**无论同批有没有正确的 code 都会报**。CLI 的 `indicator` 子命令已二次解包、拍平成宽表;服务端没有返回请求里的某个指标或证券时标 `partial` + `omittedIndicators` / `omittedSecurities`、退出码 3(写错的 code 会直接报 `100003`,所以这种情况很少出现)。**`screener` 例外**:把缺列的变量当作无法求值,按表达式的**布尔结构**判断是否还有分支能成立(`A && B` 要两边、`A || B` 只要一边)。一条都不剩 → **退出码 1 且不输出**(那些行以「通过了该条件」的名义呈现,而条件根本无法证明被执行过);仍有分支可求值、或缺的只是输出用的辅助变量 → `partial` + 退出码 3。直接 `raw call indicator.*` 只会剥外层,内层需自行处理。
8
8
 
9
9
  ## 通用模式(5 类)
10
10
 
@@ -27,7 +27,7 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
27
27
  | insight summary download | 文件路径(stdout) | — |
28
28
  | insight pamirs-summary list | `{list, total}` | `list[].summaryId` / `list[].title` / `list[].brief` / `list[].summaryTime`(纪要生成时间)/ `list[].publishTime`(发布时间)/ `list[].securityList[]{securityCode, securityName}` / `list[].researchAreaList[]{researchAreaId, researchAreaName}` / `list[].conceptList[]{conceptId, conceptName}` / `list[].categoryList` / `list[].marketList`。⚠️ `conceptList` / `categoryList` / `marketList` 三个标签字段**稀疏,且是否有值随记录和查法而变**:不带筛选时经常整条为空,用 `--category` 或 `--market` 任一过滤时回填率明显更高(这两个字段绑定,一起有或一起没有)。所以**别拉全量再本地分组**(会漏记录),也**别据此断言某条记录没有该属性**(为空只说明这次没回填)。详见 `commands/insight.md` |
29
29
  | insight pamirs-summary download | 文件路径(stdout) | — |
30
- | insight roadshow / site-visit / strategy / forum list | `{list, total}` | `list[].id` / `list[].title` / `list[].publishTime` / `list[].institution.institutionName` |
30
+ | insight roadshow / site-visit / strategy / forum list | `{list, total}` | 各自的 ID:`list[].roadshowId` / `siteVisitId` / `strategyMeetingId` / `forumId`;`list[].title` / `list[].startTime` / `list[].endTime`(举办时间)/ `list[].publishTime`(首次收录时间,可为 `null`)/ `list[].location`;路演、调研、策略会另有 `list[].institutionList[]{institutionId, institutionName}`(论坛没有) |
31
31
  | insight research list | `{list, total}` | `list[].reportId` / `list[].title` / `list[].brief`(全文摘要) / `list[].publishTime` / `list[].publisher.brokerName` / `list[].securityList[].rating` |
32
32
  | insight research download | 文件路径(stdout) | — |
33
33
  | insight foreign-report list | `{list, total}` | `list[].reportId` / `list[].title` / `list[].titleTranslate` / `list[].brief` / `list[].briefTranslate`(中译全文摘要) / `list[].publishTime` / `list[].publisher.brokerName` |
@@ -65,6 +65,7 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
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` / …) |
68
+ | quote sector-fund-flow | 同 `fund-flow` | `sectorId` / `tradeDate` + 请求的字段(`sectorMainNetInflow` / `sectorLargeInflow` / `sectorTotalOutflow` / …,单位元)。`total` 是本次返回行数,不是满足条件的总数 |
68
69
  | fundamental income-statement / balance-sheet / cash-flow(含 quarterly / -hk / -us) | `{total, list: [{...}]}` | `fiscalYear` / `period` / `endDate` / `companyName` / `companyType` / `currency` / `unit` + 各 `--field` 字段;港股/美股另含 `timeCovered`(不规则跨度)。⚠️ A 股 `*-quarterly` 单季表的 `companyType` 返回未映射的数字码(如 `102119999`),`currency` 正常;累计口径的三张表两列均正确。科目数字不受影响 |
69
70
  | fundamental main-business | `{fieldList, list}` 列式 → 规范化后 `{list: [{...}]}` | 前 3 列恒定:`periodName` / `periodEndDate` / `categoryName`(分项名,随 `--breakdown` 变)+ `opRevenue` / `opRevenueYoy` / `opRevenueRatio` / `opCost` / `opCostYoy` / `opCostRatio` / `grossProfit` / `grossProfitYoy` / `grossProfitRatio` / `grossMargin` / `grossMarginYoy` / `grossMarginRatio`(共 15 个)。`--field` 只能从后 12 个里选 |
70
71
  | fundamental valuation-analysis(仅 A 股) | `{fieldList, list}` 列式 → 规范化后 `{list: [{...}]}` | `tradeDate` / `value` / `percentileRank` / `average` / `median` / `upper1Std` / `lower1Std`(共 7 个);**无 `securityCode`**——`--field` 写不存在的名字(含 `securityCode`)报 `100005`;只传 `tradeDate` 返回 0 行、不报错 |
@@ -97,11 +98,15 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
97
98
  | vault my-conference-download | 文件路径(stdout) | — |
98
99
  | vault wechat-message-list | `{list, total}` | `list[].msgId` / `list[].content`(正文)/ `list[].url` / `list[].msgTime` / `list[].wechatGroupName` / `list[].speakerName` / `list[].category` / `list[].tagList[].tagCode` / `list[].securityList[].securityCode` / `list[].quoteMsg.quoteContent`(引用消息,无引用为 `null`)。**字段名是 `content` / `url`**,不是 `msgContent` / `contentUrl` |
99
100
  | vault wechat-chatroom-list | `{list, total}` | `list[].chatroomName` / `list[].chatroomId` |
100
- | vault stock-pool-list | 数组 | `[].poolId`(后续命令的 `--pool-id`)/ `[].poolName` |
101
+ | vault stock-pool-list | 数组 | `[].poolId`(后续命令的 `--pool-id`)/ `[].poolName` / `[].spaceType`(`1` 个人、`2` 租户;查明细时 `--space-type` 要与之一致) |
101
102
  | vault stock-pool-stocks | 数组 | `[].securityCode` / `[].securityName`。空池返回 `[]` |
102
103
  | vault stock-pool-create / stock-pool-rename | `{poolId, poolName}` | 新建 / 改名后的池 ID 与名称 |
103
104
  | vault stock-pool-add-stock / stock-pool-remove-stock | `{successList, failList}` | `successList[]` 是规范化后的证券代码(含已在池内 / 本就不在池内的幂等成功项);`failList[]{securityCode, failReason}`。**外层 `code` 恒为 `000000`,逐条失败只在 `failList` 里**——CLI 检测到非空时标 `partial`、退出码 3 |
104
105
  | vault stock-pool-delete | `{successList, failList}` | 同上,但键是 `failList[].poolId`;删不存在的池算幂等成功,计入 `successList` |
106
+ | vault stock-note-list | `{list, total}` | `list[].stockNoteId`(后续命令的 `--note-id`)/ `list[].securityList`(该笔记完整的关联证券,升序)/ `list[].content` / `list[].createTime` / `list[].updateTime`。`total` 是按笔记去重后的真实总数 |
107
+ | vault stock-note-add | `{stockNoteId, securityList}` | 新笔记 ID 与实际关联的规范化证券代码(升序) |
108
+ | vault stock-note-update | `{}` | 成功即空对象;改后的时间从 `stock-note-list` 看 |
109
+ | vault stock-note-delete | `{successList, failList}` | `successList[]` 是笔记 ID(含已不存在的幂等成功项);`failList[]{stockNoteId, failReason}`,非空时 CLI 标 `partial`、退出码 3 |
105
110
  | alternative edb-search | `{list: [...]}` 指标列表 | `indicatorId` / `indicatorName` / `dataSource` / `frequency` / `unit` |
106
111
  | alternative edb-data | 列表,每行 `{date, <indicatorId>: value, ...}` 宽表 | `date` + 每个 `--indicator-id` 一列(该日指标值) |
107
112
  | alternative concept-info | `{conceptId, conceptName, ...}`(单对象,**非列表**) | `conceptName` / `definition` / `investmentLogic` / `industrySpace` / `competitiveLandscape`;**`keyEvents[].date` / `keyEvents[].content` 仅 `--full`**;文本字段未配置为 `null` |
@@ -113,11 +118,11 @@ CLI 自动处理 envelope:`{code, msg, data}` 信封会按 `code === "000000"`
113
118
  | fund fee-rate | 同上 | `[].fundCode` / `feeType` / `feeCondition`(分档条件,无分档为 `null`)/ `feeRate`(**字符串**,如 `"1.5%"`) |
114
119
  | fund manager-info | 同上 | `[].managerName` / `currentCompany` / `careerStartDate` / `workingYears` / `managedFundScale`(亿元)/ `managedFundCount` / `historyFundCount` / `introduction`;同名经理多行 |
115
120
  | fund manager-history | 同上 | `[].fundCode` / `managerName` / `startDate` / `endDate`(`null` = 现任)/ `managedDays` / `managedReturn`(%) |
116
- | fund asset-size / holder-structure / top10-holders | 同上,每报告期一行(`top10-holders` 每期按名次多行) | `[].fundCode` / `reportDate` + 规模(`sharesEnd` / `netAsset`,万份 / 万元)、持有人结构(`holderCount` 是**带千分位的字符串** / `pctInstitution` / `pctIndividual`)、前十大(`serialNumber` / `holderName` / `holdingShares` / `holdingPct`) |
121
+ | fund asset-size / holder-structure / top10-holders | 同上,每报告期一行(`top10-holders` 每期按名次多行) | `[].fundCode` / `reportDate` + 规模(`sharesEnd` / `netAsset`,万份 / 万元)、持有人结构(`holderCount` 整数 / `pctInstitution` / `pctIndividual`)、前十大(`serialNumber` / `holderName` / `holdingShares` / `holdingPct`) |
117
122
  | fund asset-allocation | 同上,每报告期按资产类型多行 | `[].fundCode` / `reportDate` / `assetLevel` / `assetType` / `assetTypeName` / `holdingValue`(元)/ `pctToTotalAsset` |
118
123
  | fund stock-portfolio / bond-portfolio / fund-portfolio | 同上,每报告期按持仓多行 | `[].fundCode` / `reportDate` / `stockName` · `bondName` · `holdingFundName`(**只有简称、无代码**)/ `holdingQuantity` / `holdingValue`(万元)/ `pctToNav`;股票另有 `pctToFloatShares` / `pctToNavChg` |
119
124
  | fund industry-allocation / bond-type-allocation / fund-type-allocation | 同上,每报告期按分类多行 | `[].fundCode` / `reportDate` / `industryCode` + `industryName` · `bondTypeCode` + `bondTypeName` · `investTypeCodeLevel1` + `investTypeNameLevel1` / `pctToNav`;行业与基金类型另有 `positionType`(`top` / `all`) |
120
- | fund etf-pcf-header / etf-pcf-components / etf-share-change | 同上 | 申赎参数(`tradeDate` / `purchaseRedemptionStatus` / `minUnitShares` / `cashBalance` / `componentCount`);成分清单(`serialNumber` / `componentCode` / `componentName` / `pctWeight` / `purchaseQuantity` / `cashSubstituteFlag`);份额变动(`tradeDate` / `shares` / `sharesChange` / `scale`,万份 / 万元) |
125
+ | fund etf-pcf-header / etf-pcf-components / etf-share-change | 同上 | 申赎参数(`tradeDate` / `purchaseRedemptionStatus` / `minUnitShares` / `cashBalance` / `componentCount`);成分清单(`serialNumber` / `componentCode`(CLI 按名称匹配补上,匹配不到为 `null`,见 `commands/fund.md`)/ `componentName` / `pctWeight` / `purchaseQuantity` / `cashSubstituteFlag`);份额变动(`tradeDate` / `shares` / `sharesChange` / `scale`,万份 / 万元) |
121
126
  | indicator search | `[{indicatorCode, indicatorName, ...}]`(列表) | `indicatorCode` / `indicatorName` / `description` / `scopeList[].market` / `scopeList[].securityType` / `scopeList[].usageRestriction`(接口限制,`null`=无限制) / `parameterList[].paramKey`(**参数名以此为准**) / `.enumList[].value` / `score` |
122
127
  | indicator cross-section | CLI 拍平为宽表 `{list, total}` | `list[].security` / `list[].name` + 每个指标名一列;**单日多指标 × 多证券**,每行一只证券。**没有 `date` 列**(查询日期挂在每个指标的参数上,各列可为不同日期);原始响应的 `values` 为 `[证券][指标]` |
123
128
  | indicator time-series | CLI 拍平为宽表 `{list, total}` | `list[].date` + 序列列:单证券时列=各指标、多证券时列=各证券;每行一个日期。原始响应 `values` 仍为 `[序列][日期]` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gangtise-openapi-cli",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "CLI for Gangtise OpenAPI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,44 +0,0 @@
1
- import { unwrapIndicatorData } from "./indicatorMatrix.js";
2
- import { runWithConcurrency } from "./transport.js";
3
- const TRADE_DATE_KEY = "tradeDate";
4
- const PERIOD_DATE_KEYS = ["reportDate", "fiscalYear"];
5
- const PROBE_CONCURRENCY = 5;
6
- export async function resolveCalendarType(client, indicators) {
7
- const codes = [...new Set(indicators.filter((code) => typeof code === "string" && code.length > 0))];
8
- if (codes.length === 0)
9
- return undefined;
10
- const verdicts = await runWithConcurrency(codes, PROBE_CONCURRENCY, (code) => isTradingDayTyped(client, code));
11
- return verdicts.every((verdict) => verdict === true) ? "TD" : undefined;
12
- }
13
- async function isTradingDayTyped(client, code) {
14
- let entry;
15
- try {
16
- entry = await findIndicator(client, code);
17
- }
18
- catch {
19
- return false;
20
- }
21
- if (!entry)
22
- return false;
23
- const keys = paramKeys(entry.parameterList);
24
- if (keys.length === 0)
25
- return false;
26
- if (keys.some((key) => PERIOD_DATE_KEYS.includes(key)))
27
- return false;
28
- return keys.includes(TRADE_DATE_KEY);
29
- }
30
- async function findIndicator(client, code) {
31
- const raw = await client.call("indicator.search", { keyword: code, limit: 100 });
32
- const data = unwrapIndicatorData(raw);
33
- const list = Array.isArray(data) ? data : data?.list;
34
- if (!Array.isArray(list))
35
- return undefined;
36
- return list.find((item) => !!item && typeof item === "object" && item.indicatorCode === code);
37
- }
38
- function paramKeys(parameterList) {
39
- if (!Array.isArray(parameterList))
40
- return [];
41
- return parameterList
42
- .map((param) => (param && typeof param === "object" ? param.paramKey : undefined))
43
- .filter((key) => typeof key === "string");
44
- }