gangtise-openapi-cli 0.49.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.
- package/README.md +25 -5
- package/dist/src/commands/indicator.js +8 -13
- package/dist/src/commands/insight.js +10 -3
- package/dist/src/commands/quote.js +19 -0
- package/dist/src/commands/vault.js +49 -4
- package/dist/src/core/endpoints.js +36 -0
- package/dist/src/core/errors.js +26 -12
- package/dist/src/core/etfComponents.js +74 -17
- package/dist/src/core/normalize.js +1 -1
- package/gangtise-openapi/SKILL.md +20 -14
- package/gangtise-openapi/references/commands/fund.md +2 -2
- package/gangtise-openapi/references/commands/indicator.md +39 -18
- package/gangtise-openapi/references/commands/insight.md +4 -1
- package/gangtise-openapi/references/commands/quote.md +20 -0
- package/gangtise-openapi/references/commands/reference-and-lookup.md +1 -1
- package/gangtise-openapi/references/commands/vault.md +54 -8
- package/gangtise-openapi/references/errors.md +2 -2
- package/gangtise-openapi/references/examples.md +1 -1
- package/gangtise-openapi/references/fields.md +1 -1
- package/gangtise-openapi/references/response-schema.md +8 -3
- package/package.json +1 -1
- package/dist/src/core/calendarType.js +0 -44
|
@@ -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
|
|
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
|
|
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 |
|
|
@@ -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_
|
|
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 枚举值非法:
|
|
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_
|
|
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}` | 各自的 ID:`list[].roadshowId` / `siteVisitId` / `strategyMeetingId` / `forumId`;`list[].title` / `list[].startTime` / `list[].endTime
|
|
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` |
|
package/package.json
CHANGED
|
@@ -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
|
-
}
|