gangtise-openapi-cli 0.32.0 → 0.34.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 +29 -25
- package/dist/src/cli.js +158 -62
- package/dist/src/core/client.js +51 -5
- package/dist/src/core/commandBodies.js +9 -7
- package/dist/src/core/errors.js +7 -4
- package/dist/src/core/output.js +8 -0
- package/dist/src/core/printer.js +7 -1
- package/dist/src/core/quoteSharding.js +11 -11
- package/dist/src/version.js +1 -1
- package/gangtise-openapi/SKILL.md +42 -39
- package/gangtise-openapi/references/commands/ai.md +2 -3
- package/gangtise-openapi/references/commands/fundamental.md +3 -2
- package/gangtise-openapi/references/commands/indicator.md +83 -40
- package/gangtise-openapi/references/commands/insight.md +28 -25
- package/gangtise-openapi/references/commands/quote.md +44 -25
- package/gangtise-openapi/references/commands/reference-and-lookup.md +22 -7
- package/gangtise-openapi/references/commands/vault.md +3 -2
- package/gangtise-openapi/references/examples.md +7 -7
- package/gangtise-openapi/references/fields.md +2 -2
- package/gangtise-openapi/references/lookup-ids.md +7 -5
- package/gangtise-openapi/references/response-schema.md +4 -4
- package/package.json +1 -1
package/dist/src/core/client.js
CHANGED
|
@@ -186,10 +186,26 @@ export class GangtiseClient {
|
|
|
186
186
|
});
|
|
187
187
|
if (!this.isPaginatedListResponse(firstPage)) {
|
|
188
188
|
// Shape drift (e.g. total arriving as a string) silently degrades fetch-all
|
|
189
|
-
// to a single page with no partial marker
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
189
|
+
// to a single page with no partial marker. This is NOT hypothetical: passing
|
|
190
|
+
// industryList to insight foreign-opinion / independent-opinion makes the
|
|
191
|
+
// server answer 200 + data:null, which then renders as one phantom row
|
|
192
|
+
// ({"value":null} in jsonl, a blank row in table/markdown). Verbose-only was
|
|
193
|
+
// the wrong bar — a caller who never sets GANGTISE_VERBOSE reads that row as
|
|
194
|
+
// real data, so warn on stderr unconditionally.
|
|
195
|
+
process.stderr.write(`[gangtise] warning: ${endpoint.key} is marked paginated but the first page has an unexpected shape (no numeric total + list); returning it as-is\n`);
|
|
196
|
+
// A warning only helps a human. Scripts read the exit code, and without one they
|
|
197
|
+
// cannot tell "this filter legitimately matched nothing" from "this filter is
|
|
198
|
+
// broken". Reuse exit 3 (result is not what was asked for) rather than inventing a
|
|
199
|
+
// fourth code — fetch-all silently degraded to one page either way.
|
|
200
|
+
//
|
|
201
|
+
// Applies to EVERY malformed shape, not just `null`: a string `total` truncates the
|
|
202
|
+
// result to page 1, which looks complete and is therefore worse than an obviously
|
|
203
|
+
// empty payload. All 24 paginated endpoints are genuine {total, list} lists (the
|
|
204
|
+
// odd-shaped ones like reference.constant-list are not marked paginated), so there
|
|
205
|
+
// is no legitimate response that lands here. Endpoints where `null` IS a valid
|
|
206
|
+
// answer (ai.one-pager for a security with no generated content) are unpaginated
|
|
207
|
+
// and never reach this branch.
|
|
208
|
+
process.exitCode = 3;
|
|
193
209
|
return firstPage;
|
|
194
210
|
}
|
|
195
211
|
const total = firstPage.total;
|
|
@@ -295,11 +311,42 @@ export class GangtiseClient {
|
|
|
295
311
|
if (truncatedByPageCap) {
|
|
296
312
|
process.stderr.write(`[gangtise] warning: hit the ${MAX_PAGES}-page safety cap; fetched ${collected.length} of ${total} rows. Narrow the query (e.g. a shorter date range) or pass --size to fetch a bounded subset.\n`);
|
|
297
313
|
}
|
|
314
|
+
const short = collected.length < target;
|
|
315
|
+
// `total` is not always the real row count. Three opinion endpoints report a fixed
|
|
316
|
+
// 10000 while `from=30000` still returns real rows with monotonically older publish
|
|
317
|
+
// times (the shape of Elasticsearch's default track_total_hits). A fetch-all then
|
|
318
|
+
// stops exactly at the cap with collected === total, so every completeness check
|
|
319
|
+
// below passes and the truncated export looks complete — the worst failure mode we
|
|
320
|
+
// have, because `opinion` bills 30 credits per row.
|
|
321
|
+
//
|
|
322
|
+
// Probe one row past the claimed end rather than hardcoding any number: the server
|
|
323
|
+
// can change the cap, and evidence survives that where a constant would not. Only on
|
|
324
|
+
// a genuine fetch-all that otherwise looked complete — when `total` is honest the
|
|
325
|
+
// probe comes back empty (and bills nothing, since these endpoints charge per row).
|
|
326
|
+
let totalCapped = false;
|
|
327
|
+
if (requestedSize === undefined && total > 0 && !short && !totalDrift && !truncatedByPageCap && failedPages.length === 0) {
|
|
328
|
+
try {
|
|
329
|
+
const probe = await this.requestJson(endpoint, { ...initialBody, from: total, size: 1 });
|
|
330
|
+
if (this.isPaginatedListResponse(probe) && probe.list.length > 0)
|
|
331
|
+
totalCapped = true;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// A failed probe must never fail an otherwise-complete export: we simply do not
|
|
335
|
+
// learn whether the total was capped, which is the pre-existing behaviour.
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (totalCapped) {
|
|
339
|
+
process.stderr.write(`[gangtise] warning: ${endpoint.key} reported total=${total} but rows exist past that offset — 'total' is a server-side cap, not the real count. This export is TRUNCATED at ${collected.length} rows. Narrow the query (date range / filters) and fetch in slices.\n`);
|
|
340
|
+
}
|
|
298
341
|
const out = {
|
|
299
342
|
...firstPage,
|
|
300
343
|
total,
|
|
301
344
|
list: requestedSize === undefined ? collected : collected.slice(0, requestedSize),
|
|
302
345
|
};
|
|
346
|
+
if (totalCapped) {
|
|
347
|
+
out.partial = true;
|
|
348
|
+
out.totalCapped = true;
|
|
349
|
+
}
|
|
303
350
|
// Unified completeness backstop. Whatever the cause — a failed/shape-broken page,
|
|
304
351
|
// a short later page (server page cap < maxPageSize), the MAX_PAGES cap, or `total`
|
|
305
352
|
// drifting mid-fetch — the result is partial. A short row count or a total drift each
|
|
@@ -309,7 +356,6 @@ export class GangtiseClient {
|
|
|
309
356
|
// must agree. printData maps partial → exit 3 so a script can't read a truncated export
|
|
310
357
|
// as complete. The cap and drift branches above already warned on stderr; failedPages
|
|
311
358
|
// warns below.
|
|
312
|
-
const short = collected.length < target;
|
|
313
359
|
if (short || totalDrift || failedPages.length > 0)
|
|
314
360
|
out.partial = true;
|
|
315
361
|
if (failedPages.length > 0) {
|
|
@@ -39,13 +39,15 @@ export function buildStockPoolStocksBody(options) {
|
|
|
39
39
|
* indicator's `tradeDate`. A caller-supplied `reportDate` suppresses that
|
|
40
40
|
* injection for its indicator, so the two date fields are never sent together.
|
|
41
41
|
*
|
|
42
|
-
* This
|
|
43
|
-
* answered a `tradeDate` with an EMPTY result
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
42
|
+
* This is load-bearing, and the server has moved twice on it. A report-period
|
|
43
|
+
* indicator answered a `tradeDate` with an EMPTY result (probed 2026-08-01 on
|
|
44
|
+
* `is_op_rev_mom`), then resolved it to the enclosing report period (2026-08-08,
|
|
45
|
+
* same 33.4903 either way), and since 2026-08-14 REJECTS it outright:
|
|
46
|
+
* `100003 指标 is_op_rev_mom 不支持参数 tradeDate; 缺少必填参数 reportDate`
|
|
47
|
+
* (re-probed 2026-08-15; `reportDate` still returns 33.4903). So a caller-supplied
|
|
48
|
+
* `reportDate` is now the only way to reach those indicators, and suppressing the
|
|
49
|
+
* `tradeDate` injection is what makes it work rather than merely being tidier.
|
|
50
|
+
* Injecting into an indicator that takes no date at all is harmless —
|
|
49
51
|
* `pty_op_scope` still answers normally with a stray `tradeDate` attached.
|
|
50
52
|
*
|
|
51
53
|
* `sDate` is deliberately NOT here. It is an interval START, not a substitute:
|
package/dist/src/core/errors.js
CHANGED
|
@@ -40,7 +40,7 @@ const ERROR_HINTS = {
|
|
|
40
40
|
"999001": "检查 GANGTISE_TOKEN 或 AK/SK 是否已 export。",
|
|
41
41
|
"999002": "有 AK/SK 时 CLI 会自动重新登录重试一次,否则请重新登录。",
|
|
42
42
|
"999003": "定制接口需联系客户经理开通。",
|
|
43
|
-
"999004": "
|
|
43
|
+
"999004": "整库/整接口未开通(如帕米尔专家纪要需单独购买),或该条记录本账号不可见——list 撞上多为前者,先确认该数据库是否已开通。",
|
|
44
44
|
"999005": "联系客户经理充值,或缩小查询范围降低消耗。",
|
|
45
45
|
"999006": "触发限流,稍后再试或联系客户经理提额;429 所有端点都退避重试,5xx 仅普通端点重试(贵档 no-replay 端点的 5xx 不重放,但其 429 仍重试)。",
|
|
46
46
|
// CLI 自身发不出这三类请求(method / Content-Type 都由 endpoint 定义固定),
|
|
@@ -66,7 +66,8 @@ const ERROR_HINTS = {
|
|
|
66
66
|
"100003": "msg 已指明字段名或取值范围时直接按 msg 改;msg 只说「参数值非法」时多为枚举参数拼写错误(如 --source / --question-category / --answer-important),对照命令 --help 列出的合法值检查。",
|
|
67
67
|
"100004": "检查 --size / --from 是否为非负数且未超单页上限。",
|
|
68
68
|
"100005": "对照命令 --help 列出的合法取值检查。",
|
|
69
|
-
|
|
69
|
+
// 2026-08-14 起 list 类端点把单页上限(50)也归到此码,不再只是「一次要太多行」。
|
|
70
|
+
"100006": "缩短日期范围或调小 --size / --limit(list 类端点单页上限为 50,CLI 自动翻页,直发接口时要自己遵守)。",
|
|
70
71
|
// 按参数名判断,不要按命令组:AI 下 management-discuss 的 --report-date 是 date,
|
|
71
72
|
// 而同属 AI 的 knowledge-batch 收时间戳或 datetime——旧文案笼统写"AI 用 datetime"会把
|
|
72
73
|
// --report-date 的用户越导越错。
|
|
@@ -77,8 +78,10 @@ const ERROR_HINTS = {
|
|
|
77
78
|
"110003": "查询时间超出本账号的数据权限范围——把日期移进范围内(整个区间都早于下界时缩短窗口无用),或联系客户经理开通更长历史。",
|
|
78
79
|
"120001": "用 `gangtise reference securities-search` 确认代码与后缀(如 600519.SH / 00700.HK)。",
|
|
79
80
|
"130001": "未找到数据——先核对查询条件;EDE 指标端点此码也可能是未开通该指标权限,仍失败联系客户经理。",
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
// 2026-08-14 起下载类把「file-type 非法」拆到 130005、「资源未生成」拆到 130003,
|
|
82
|
+
// 但历史上两者都归过此码,所以提示保留对 --file-type 的指引。
|
|
83
|
+
"130002": "确认下载 ID 有效且本账号可见;下载类还需检查 --file-type 取值是否合法。",
|
|
84
|
+
"130003": "资源未生成或该条记录未附带文件——换一条有附件的记录(列表里 hasAttachment/hasFile 为 true 的),或稍后再试。",
|
|
82
85
|
// 下载类命令各有各的 ID 参数(--report-id / --announcement-id / --chunk-id /
|
|
83
86
|
// --summary-id / --conference-id / --record-id / --file-id / --article-id /
|
|
84
87
|
// --independent-opinion-id);--data-id 是异步 *-check 用的,不产生此码。
|
package/dist/src/core/output.js
CHANGED
|
@@ -51,6 +51,14 @@ function rowsFromList(list) {
|
|
|
51
51
|
return list.map((item, index) => ({ index, value: item }));
|
|
52
52
|
}
|
|
53
53
|
function toRows(value) {
|
|
54
|
+
// A null/undefined payload means "no data", not "one row whose only column is
|
|
55
|
+
// null". Without this guard it fell through to [{ value }] and jsonl emitted
|
|
56
|
+
// {"value":null} — a phantom record that makes `wc -l` report 1. Real case:
|
|
57
|
+
// insight foreign-opinion --industry answers 200 + data:null.
|
|
58
|
+
// Scalars (0, "", false) are left alone; only the two empty values short-circuit.
|
|
59
|
+
if (value === null || value === undefined) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
54
62
|
if (Array.isArray(value)) {
|
|
55
63
|
return rowsFromList(value);
|
|
56
64
|
}
|
package/dist/src/core/printer.js
CHANGED
|
@@ -53,5 +53,11 @@ export async function printData(data, format, output, cache) {
|
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
warnIfLargeInMemory(items, format);
|
|
56
|
-
|
|
56
|
+
const rendered = renderOutput(normalized, format);
|
|
57
|
+
// An empty render means "no rows at all". Appending the newline anyway would put
|
|
58
|
+
// one blank line on stdout — `wc -l` reports 1 and `while read` yields one empty
|
|
59
|
+
// record, which is exactly the phantom row the null-payload guard in toRows()
|
|
60
|
+
// removes. Table/markdown render "(empty)" so they are unaffected.
|
|
61
|
+
if (rendered)
|
|
62
|
+
process.stdout.write(`${rendered}\n`);
|
|
57
63
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isVerbose, PAGE_CONCURRENCY, runWithConcurrency } from "./transport.js";
|
|
2
2
|
const DAY_MS = 86_400_000;
|
|
3
|
-
/** API-side row cap (per docs). Used to lift the default 6000-row cap on
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
/** API-side row cap (per docs). Used to lift the default 6000-row cap on whole-market
|
|
4
|
+
* queries so a 2-day A-share shard (~11K rows) isn't silently truncated. Single-security
|
|
5
|
+
* queries are untouched. */
|
|
6
6
|
const ALL_MARKET_LIMIT = 10_000;
|
|
7
7
|
function parseDate(value) {
|
|
8
8
|
// Accept yyyy-MM-dd; reject anything else so we can fall back to a single request.
|
|
@@ -22,11 +22,6 @@ export function isFullMarket(body, fullMarketValue) {
|
|
|
22
22
|
return false;
|
|
23
23
|
return list[0] === fullMarketValue;
|
|
24
24
|
}
|
|
25
|
-
/** Kline uses `all` as its whole-market keyword; fund-flow uses `aShares`. Thin wrapper
|
|
26
|
-
* so kline call sites read naturally. */
|
|
27
|
-
export function isAllMarket(body) {
|
|
28
|
-
return isFullMarket(body, "all");
|
|
29
|
-
}
|
|
30
25
|
/** Sat/Sun in UTC — shard dates are formatted from UTC midnight (see parseDate). */
|
|
31
26
|
function isWeekendUtc(d) {
|
|
32
27
|
const day = d.getUTCDay();
|
|
@@ -40,9 +35,14 @@ function buildShards(start, end, shardDays) {
|
|
|
40
35
|
const shardEnd = Math.min(cursor + (shardDays - 1) * DAY_MS, endTime);
|
|
41
36
|
// Per-day sharding (shardDays===1): a lone weekend day always returns empty (A/HK/US
|
|
42
37
|
// markets closed) — skip it to save ~28% of requests and daily quota. This covers
|
|
43
|
-
// every 1-day-sharded full-market query: fund-flow AND day-kline /
|
|
44
|
-
//
|
|
45
|
-
//
|
|
38
|
+
// every 1-day-sharded full-market query: fund-flow AND day-kline (aShares/usStocks)
|
|
39
|
+
// / day-kline-us.
|
|
40
|
+
//
|
|
41
|
+
// Multi-day shards (day-kline hkStocks=2, index-day-kline=15) are NOT filtered. Note
|
|
42
|
+
// this is a deliberate simplification, not a claim that they always contain a weekday:
|
|
43
|
+
// a 2-day shard starting on a Saturday is Sat+Sun and returns nothing. That costs one
|
|
44
|
+
// wasted request at a range boundary and never drops a trading day, whereas filtering
|
|
45
|
+
// multi-day windows correctly would mean walking each window's days.
|
|
46
46
|
if (!(shardDays === 1 && isWeekendUtc(new Date(cursor)))) {
|
|
47
47
|
shards.push({
|
|
48
48
|
startDate: formatDate(new Date(cursor)),
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated — DO NOT EDIT
|
|
2
|
-
export const CLI_VERSION = "0.
|
|
2
|
+
export const CLI_VERSION = "0.34.0";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gangtise-openapi
|
|
3
|
-
version: "0.
|
|
3
|
+
version: "0.34.0"
|
|
4
4
|
description: |-
|
|
5
5
|
通过 gangtise CLI 直接调用 Gangtise OpenAPI,拉取投研原始数据、批量导出、下载文件、调用 AI 能力。
|
|
6
6
|
|
|
@@ -27,16 +27,18 @@ description: |-
|
|
|
27
27
|
4. **时间格式**:datetime `"YYYY-MM-DD HH:mm:ss"`(引号包裹),date `YYYY-MM-DD`。
|
|
28
28
|
5. **多值参数**:优先重复传(最稳、最明确):`--security 600519.SH --security 000858.SZ`。CLI 也支持半/全角逗号分隔(`args.ts` 为语音输入容错),但重复传不易被 shell 吞。
|
|
29
29
|
6. **K 线"最近 N 条"**:必须用 `--start-date`/`--end-date` 拉日期范围,从结果按 `tradeDate` 取尾部最近 N 条。**不要只用 `--limit N`**(截取的是窗口开头)。
|
|
30
|
-
6.1. **日 K 仅历史**:`day-kline`
|
|
31
|
-
6.
|
|
30
|
+
6.1. **日 K 仅历史**:`day-kline` **不返回盘中实时数据**。当日数据入库时间:A 股 ~15:30 / 港股 ~16:30 / 美股 ~07:00(北京时间)。需要盘中快照请走 `quote realtime`。
|
|
31
|
+
6.1.1. 🔴 **`quote day-kline` 一个命令覆盖 A 股 / 港股 / 美股 / 指数**(2026-08-14 合并),可混着传代码。**全市场关键字是 `aShares` / `hkStocks` / `usStocks`,`--security all` 已废弃**(CLI 会报错并提示改用哪个),且关键字**必须单独传**,不能与代码或另一个关键字混填。指数(`.SH`/`.SZ`/`.BJ` 交易所指数、`.GT` 概念、`.CI`/`.SWI` 行业)不支持全市场关键字,须逐个传代码。`day-kline-hk` / `day-kline-us` / `index-day-kline` 已下线(接口仍可调、仍用 `all`),**新代码别用**——它们不校验证券代码,传错返 `total:0` 而不报错。
|
|
32
|
+
6.2. **多标的日 K 不自动分片**:只有全市场关键字才按日切片提额;显式传多个 `--security` 时走单请求(默认 `--limit 6000` / 上限 10000)。**v0.23.0 起:返回行数撞上 `--limit` 时结果会标 `partial`、退出码 3、stderr 警告**(不再静默截断;`--limit` 超 10000 本地直接报错)。仍建议先估 标的数 × 交易日数,接近/超 6000 → 逐只分开拉、或显式 `--limit 10000` 并按日期区间分批。
|
|
32
33
|
7. **CLI 已内置自动化,不要手动复刻**:
|
|
33
|
-
- 翻页 → 首页拿 total
|
|
34
|
-
- K
|
|
34
|
+
- 翻页 → 首页拿 total 后剩余页并发拉取;🔴 **全量拉取结束会多探一行验证 `total` 是不是服务端封顶**(`opinion` / `foreign-opinion` / `independent-opinion` 的 `total` 恒为 10000 但实际远不止)——探到就标 `partial` + `totalCapped` + 退出 3,**这时导出的是截断结果,要缩小时间范围分片拉**
|
|
35
|
+
- K 线全市场关键字(`aShares` / `hkStocks` / `usStocks`;旧命令 `all`)跨日期 → 自动按日切片并合并,粒度按各市场单日行数定(A 1 天 / 港 2 天 / 美 1 天)
|
|
35
36
|
- 5xx / `429` / 网络错误 / `999999` → 自动指数退避重试(🔴 贵档端点例外:仅连接失败 / 429 / token 自愈重试,5xx/超时不重放防重复扣分,v0.26.0;`indicator` 端点对 `999999` 不重试,v0.27.0)
|
|
36
37
|
- Token 失效(`0000001008` / `999002`,含已废弃的 `8000014`/`8000015`)→ 自动重新登录并重试一次;凭证错 `999011` → **不重试**(AK/SK 不对不会自己好),查环境变量
|
|
37
38
|
8. **参数命名差异**:Insight/Quote/Vault 用 `--security`,Fundamental/AI 用 `--security-code`(例外:`ai stock-summary` 用 `--security`,`ai security-clue` 用 `--gts-code`)。
|
|
38
39
|
9. **调试**:`--verbose` 或 `GANGTISE_VERBOSE=1` 打印每个请求的耗时/字节数到 stderr。
|
|
39
40
|
10. **`--field` 字段名必须核对,不确定就别传**(返回全量最稳):`quote realtime` / `fundamental main-business` / `valuation-analysis` 遇到不存在的字段名时,上游只丢**值**、字段名照请求**回显**,按位置拍平会把值贴到错误的字段上(实测 realtime 传 `close`——它没有这个字段——换手率 28.5573 被贴成 `close`,茅台真实价 1297.41)。v0.28.3 起 CLI 检测到长度不匹配直接报错(退出码 1):**带 `--field` 的命令看到这个报错,先去 `references/fields.md` 核对字段名**(没有 `--field` 的命令如 `alternative edb-data` 报此错则是上游响应结构异常,报障时若报错末尾附了 `(trace …)` 就一并带上)。另:realtime **无 `close`**(用 `latestPrice`)、**无市值**(总市值走 `indicator cross-section --indicator qte_mkt_cptl`,2026-08-03 起 A/港/美股均有数)。
|
|
41
|
+
11. 🔴 **EDE 取不到数时给的是占位值,而 `0` 和 `null` 都可能**(`indicator` 截面 / 时序 / 选股):多数指标填 `null`,但个别——最常用的是 `is_dnrpnp` 扣非归母净利润——填 **`0`**,且这是**指标属性、与日期对不对无关**(日期落在报告期末时,覆盖不到的证券如美股同样返 `0`,行还在、无告警、退出码 0)。**`null` 通常被聚合跳过,`0` 会照常穿过比较与比率计算**:`screener` 的 `F1 > 0` 可能筛出空集(读起来像「没有盈利的股票」),时序整列求均值可能差几十倍。所以:报告期类指标的日期一律落**报告期末**;拿 `is_dnrpnp` 这类做筛选或聚合前先单查确认不是占位;**别对时序整列直接求均值 / 求和**。详见 `references/commands/indicator.md`。
|
|
40
42
|
|
|
41
43
|
## 工作流(3 步)
|
|
42
44
|
|
|
@@ -80,7 +82,7 @@ description: |-
|
|
|
80
82
|
|
|
81
83
|
### 数据范围(能查多久)
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
正式账号口径(下表为官方公布值;实际可查范围随账号服务等级而定,以自己账号实测为准):
|
|
84
86
|
|
|
85
87
|
| 命令组 | 可回溯 |
|
|
86
88
|
|--------|--------|
|
|
@@ -122,7 +124,7 @@ description: |-
|
|
|
122
124
|
| 纪要 / 会议纪要(外部) | `insight summary list` |
|
|
123
125
|
| 帕米尔纪要 / 帕米尔专家纪要 / Pamirs | `insight pamirs-summary list`(专家纪要库,需单独购买;筛选项比 `summary` 少,无 `--source`/`--institution`/`--participant-role`) |
|
|
124
126
|
| 路演 / 调研 / 策略会 / 论坛 | `insight roadshow / site-visit / strategy / forum list` |
|
|
125
|
-
| 财报日历 / 业绩预告 / 业绩快报 / 财报披露排期 | `insight performance-calendar list`(**用 `--start-date`/`--end-date`,不是 `--start-time
|
|
127
|
+
| 财报日历 / 业绩预告 / 业绩快报 / 财报披露排期 | `insight performance-calendar list`(**用 `--start-date`/`--end-date`,不是 `--start-time`**;全表体量很大且按条计费,CLI 强制要求日期范围 / `--security` / `--size` 三者至少其一;只给 `--security` 时另有 1000 行隐式上限,撞上限且 total 还有剩余=筛选可能没生效(标 `partial`、退出码 3),改用日期范围重查。下载原文 `performance-calendar download --performance-report-id`,仅 `hasAttachment: true` 可下) |
|
|
126
128
|
| A 股公告 / 公告 | `insight announcement list` |
|
|
127
129
|
| 港股公告 / HK 公告 | `insight announcement-hk list` |
|
|
128
130
|
| 美股公告 / US 公告 | `insight announcement-us list` |
|
|
@@ -132,7 +134,7 @@ description: |-
|
|
|
132
134
|
| 跨类型语义搜索(研报+纪要+...) | `ai knowledge-batch`(多个 `--resource-type`) |
|
|
133
135
|
| 知识库原文下载(搜到后取全文) | `ai knowledge-resource-download`(前置:`knowledge-batch` 拿 `resourceType`+`sourceId`;`250001`/旧 `433007`=组合不匹配) |
|
|
134
136
|
| 一页通 / 投资逻辑 / 同业对比 / 调研提纲 | `ai one-pager / investment-logic / peer-comparison / research-outline` |
|
|
135
|
-
| 个股看点 / 投研总结 / 公司速览 | `ai stock-summary`(`--security`
|
|
137
|
+
| 个股看点 / 投研总结 / 公司速览 | `ai stock-summary`(`--security` **只收具体代码**,单次最多 6000 个;仅 A 股/港股。⚠️ 2026-08-14 起**不再支持** `aShares`/`hkStocks` 全市场批量) |
|
|
136
138
|
| 业绩点评(异步) | `ai earnings-review` |
|
|
137
139
|
| 观点 PK / 多空辩论(异步) | `ai viewpoint-debate` |
|
|
138
140
|
| 投研线索 | `ai security-clue`(前置:`reference securities-search` 拿 `gts-code`) |
|
|
@@ -140,11 +142,10 @@ description: |-
|
|
|
140
142
|
| 热点话题 / 早午晚报 | `ai hot-topic` |
|
|
141
143
|
| 管理层讨论(财报) | `ai management-discuss-announcement` |
|
|
142
144
|
| 管理层讨论(业绩会) | `ai management-discuss-earnings-call` |
|
|
143
|
-
| A
|
|
144
|
-
|
|
|
145
|
-
| 美股日 K
|
|
146
|
-
|
|
|
147
|
-
| 分钟 K(A 股) | `quote minute-kline` |
|
|
145
|
+
| 日 K(历史,A 股 / 港股 / 美股 / 各类指数,可混查) | `quote day-kline` |
|
|
146
|
+
| 全部沪深京指数日 K / 要指数名称 | `quote index-day-kline`(**旧命令仍有两处 `day-kline` 做不到**:`--security all` 一次拿全部指数;返回 `securityName` 指数名称——`day-kline` 查指数只有代码没有名称) |
|
|
147
|
+
| ~~港股 / 美股日 K~~ | ⚠️ 已下线,用 `quote day-kline`(`day-kline-hk` / `day-kline-us` 仍可调但不校验代码) |
|
|
148
|
+
| 分钟 K(沪深 A 股 + 指数) | `quote minute-kline`(一次一只) |
|
|
148
149
|
| 实时行情(A / 港 / 美) | `quote realtime` |
|
|
149
150
|
| A股资金流向(主力/大单净流入,日频) | `quote fund-flow`(`--security` 或 `aShares` 全市场〔须带 `--start-date`/`--end-date`,按日自动分片〕;免费) |
|
|
150
151
|
| 单证券 A股完整利润表 / 资产负债 / 现金流(累计 / 单季) | `fundamental income-statement[-quarterly] / balance-sheet / cash-flow[-quarterly]` |
|
|
@@ -183,16 +184,16 @@ description: |-
|
|
|
183
184
|
- 港股代码用在 `insight foreign-opinion --security` 还是 `quote day-kline-hk --security`?前者要"境外"格式(`UBER.N`),后者要 `.HK`
|
|
184
185
|
- "成分股" → 题材深度(分组/重点标记/纳入理由)走 `alternative concept-securities`;板块(行业/概念分类树,纯代码名单)走 `reference sector-constituents`
|
|
185
186
|
- **证券基本面 / 指标先按任务形态路由,不是搜到 EDE 就一律走 EDE**:
|
|
186
|
-
- 单证券先优先对应 `fundamental` 专用命令(财务、估值、盈利预测、股东、主营或完整三大报表,多数免费 / 低价)。其中 `valuation-analysis` / `earning-forecast`
|
|
187
|
+
- 单证券先优先对应 `fundamental` 专用命令(财务、估值、盈利预测、股东、主营或完整三大报表,多数免费 / 低价)。其中 `valuation-analysis` / `earning-forecast` 当前仅支持 A 股,港 / 美股的**估值历史分位**与**盈利预测**无可用口径。但**估值指标本身别照抄旧结论**:`finc_pe_ttm` 港股、`qte_mkt_cptl`/`shr_tot` 港美股都已有数。⚠️ **凡「仅 A 股」「无数据」这类否定结论都只是某时点抽查**,数据覆盖在持续扩展;负面结论过期不会报错、只会让你白白拒掉一个能跑的查询。**一律以当次 `scopeList` + 抽查为准**
|
|
187
188
|
- 多证券批量取一组**已实现**财务 / 估值指标 → 优先 `indicator search` 后用 EDE 一次拉取,替代逐只循环;单日或同一报告期横向比较用 `cross-section`,区间走势用 `time-series`(后者不能多指标 × 多证券同时)。**批量按 code 回填加 `--key-by code`**(列头用 `indicatorCode`,防同名指标碰撞)。⚠️ **两个轴的顺序规则不同**:`indicatorList` = 请求顺序,但 **`securityCodeList` 是按代码升序重排的**(请求 `000858,600519,000001` → 回 `000001,000858,600519`)——**行绝不能按请求下标对位**,一律按 `security` 字段取值
|
|
188
|
-
- ⚠️ **估值指标的历史序列:两个接口的财报口径切换时点不同**——`indicator time-series`(EDE)按**正式财报披露日**切,`fundamental valuation-analysis` 按**业绩快报**口径切、通常更早,所以同一天取到的估值指标可能不一样(已验证 `finc_pe_ttm`/`peTtm`;`finc_pb_mrq` 等非 TTM 口径同样在报告期节点变化,规则未单独验证)。做估值分位 / 回测时**两个接口都拉一遍交叉核**,尤其业绩大幅变动的标的(抽查中出现过个别标的在 `valuation-analysis` 侧长期未更新)。分叉时用「总市值 ÷ PE 反推隐含净利润」对照利润表滚动 TTM
|
|
189
|
+
- ⚠️ **估值指标的历史序列:两个接口的财报口径切换时点不同**——`indicator time-series`(EDE)按**正式财报披露日**切,`fundamental valuation-analysis` 按**业绩快报**口径切、通常更早,所以同一天取到的估值指标可能不一样(已验证 `finc_pe_ttm`/`peTtm`;`finc_pb_mrq` 等非 TTM 口径同样在报告期节点变化,规则未单独验证)。做估值分位 / 回测时**两个接口都拉一遍交叉核**,尤其业绩大幅变动的标的(抽查中出现过个别标的在 `valuation-analysis` 侧长期未更新)。分叉时用「总市值 ÷ PE 反推隐含净利润」对照利润表滚动 TTM,即可判出哪侧是陈值。**判断某财报在某日是否已公开,用三大报表返回的 `earliestAnncDate`(首次公告日)**,不要用同一响应里的 `announcementDate`(它在部分证券上各期取值相同);要交叉核实就查 `insight announcement list` 的公告披露日。
|
|
189
190
|
- 始终排除 EDE:A股盈利预测 / 一致预期(含预测 EPS)→ `fundamental earning-forecast`;A股估值历史分位 → `fundamental valuation-analysis`;开高低收 / 成交量等行情与 K 线 → `quote`;单证券完整报表 → 对应三大报表命令。**例外:总市值只有 EDE 有**——`quote realtime` / `day-kline` 都不返回市值,走 `indicator cross-section --indicator qte_mkt_cptl`(2026-08-03 起 A/港/美股均有数,默认单位「元」,用 `--scale` 缩放)。EDE 搜到的基本 / 稀释 EPS 是已实现值,**不能冒充预测 EPS**;港 / 美股缺少上述专用能力时应如实说明不支持,不能用别的语义代替
|
|
190
191
|
- EDE 取数前必须用 `search --format json` 同时核对:`indicatorName` + `description` 语义准确、`scopeList` 覆盖全部目标市场 / 证券类型、`parameterList` 必填参数与枚举可满足;`scopeList` 缺失 / `null` / 空或任一项不符,都视为无法证明覆盖并回退专用接口。专用接口也不覆盖目标市场时,说明当前不可用,不要硬调。`scopeList` 按指标各不相同,不能因 EDE 服务支持 A / 港 / 美股就假定某个指标三市场都覆盖
|
|
191
192
|
- `indicator search` 免费,`cross-section` / `time-series` 按单元格计费;除多证券批量的效率收益外,仍优先免费 / 低价的 `quote` 或 `fundamental`
|
|
192
193
|
- 行业 / 宏观指标(空调销量、社融等,无证券维度)走 `alternative edb-*`(EDB),不要与证券级 EDE 混用
|
|
193
|
-
- **EDE「没数据」和「代码写错」是两回事**(2026-08-
|
|
194
|
-
- **EDE 指标参数名一律以 `indicator search --format json` 的 `parameterList`
|
|
195
|
-
-
|
|
194
|
+
- **EDE「没数据」和「代码写错」是两回事**(2026-08-15 实测):**①代码写错 → 直接报 `100003` 并指名是哪个**(指标写错报「指标 xxx 不存在」,证券写错报「xxx 不是有效证券或者板块ID」,美股用 `.US` 而非 `.O`/`.N` 同理),**无论同批有没有其它正确的 code 都会报**,所以拼写错误现在是一眼可见的、不用再靠对照推断;**②代码有效但无数据 / 无覆盖 / 未来日期 → 行列都保留**(含单指标 × 单证券的 1×1)并给一个占位单元格,**退出码 0**。🔴 **占位值不统一、由指标决定**:多数是 `null`,但个别(最常用的是 `is_dnrpnp` 扣非归母净利润)填 **`0`**,而 `0` 会照常穿过比较与聚合——`screener` 的 `F1 > 0` 因此可能筛出空集、整列求均值可能差几十倍(茅台时序实测差 52 倍)。**别把 `0` 当真值**,无覆盖时它与真值无法区分,**和已知有数的标的一起查做对照仍然必要**,详见 `references/commands/indicator.md`。日期语义按指标分两类:财务报表指标=报告期末(可为非交易日)、`finc_pe_ttm` / `finc_pb_mrq` 等日频估值=最新交易日(⚠️ `finc_pb_mrq` 是日频的,用报告期末日期会取到几个月前的陈值);混合取数按各自有效日期分次 `cross-section` 再按证券合并,别塞进同一个 `--date`。详见 `references/commands/indicator.md`
|
|
195
|
+
- **EDE 指标参数名一律以 `indicator search --format json` 的 `parameterList` 为准**,不要凭记忆或照抄示例。参数名写错会报 `100003` 并指出是哪个指标的哪个参数(如复权参数是 `adjustType`,写成 `adjustmentType` 直接报错),按报错信息改即可。⚠️ **报告期类指标(`is_*` 利润表等)必填 `reportDate`,不吃 `tradeDate`**——只传 `--date` 会报「不支持参数 tradeDate; 缺少必填参数 reportDate」,要补 `--indicator-param "code:reportDate=YYYY-MM-DD"`;行情 / 估值类(`qte_*` / `finc_*`)用 `--date` 即可。`--security` 支持板块 ID(`reference sector-search` 的 10 位 `sectorId`;`--indicator` 只收指标编码)。根级 `--scale` 只作用于声明了 `scale` 的指标,价格与金额可以混查。
|
|
196
|
+
- **新增指标**:融资融券 21 个 `mgn_*`(`mgn_bal` 两融余额 / `mgn_fin_*` 融资 / `mgn_sl_*` 融券 / `mgn_flag` 是否标的,**仅 A 股有数**;区间类的 `changePeriod` 2026-08-14 起改为**可选**);行业分类 `scr_indu`(一个指标覆盖申万/中信/恒生/GICS 四套,**必填** `industryType` 1-4 + `industryLevel` 0-4,A/港/美股均支持,返回字符串),2026-08-14 又加了三个**行业组合指标** `scr_indu_citic` / `scr_indu_sw` / `scr_indu_gics`(体系写进编码,只需可选的 `industryLevel`;单套体系用它们更省事,横比多套仍用 `scr_indu`)
|
|
196
197
|
- "业绩点评"双义消歧:**检索已有**(研报/纪要里的业绩点评内容)走 `insight ... list --llm-tag earningsReview`(0.1/条);**AI 现生成**一份走 `ai earnings-review`(异步、50/次)。不确定问一句
|
|
197
198
|
|
|
198
199
|
## 公司名 → 证券代码
|
|
@@ -264,7 +265,7 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
264
265
|
|
|
265
266
|
日期参数**按参数名判断、不按命令组**(命令组会误导——AI 里既有 `--start-time` 又有 `--date`/`--report-date`;Insight 里 `performance-calendar` 是唯一用 `--start-date`/`--end-date` 的 list):名字带 `-date` 的(`--start-date`/`--end-date`/`--date`/`--report-date`)一律 `YYYY-MM-DD`,覆盖 Quote/Fundamental、`insight performance-calendar`、AI 的 `theme-tracking`(`--date`)/`hot-topic`/`management-discuss-*`(`--report-date`)、Alternative `edb-data`、Indicator `cross-section`(`--date`)/`time-series`;名字带 `-time` 的(`--start-time`/`--end-time`)用 `YYYY-MM-DD[ HH:mm[:ss]]`(秒可省、空格或 `T` 分隔)或 10/13 位时间戳,覆盖 Insight/Vault 各 list、`quote minute-kline`、`ai security-clue`、`ai knowledge-batch`。其中 **A 股公告(`insight announcement list`)与 `knowledge-batch` 会把输入转成 13 位毫秒**(10 位秒自动 ×1000),其余 `-time` 命令(含 `announcement-hk`/`announcement-us`)原样透传字符串;CLI 输入统一接受 10/13 位纯数字或 `YYYY-MM-DD[ HH:mm[:ss]]`(同上:秒可省、空格或 `T` 分隔)。
|
|
266
267
|
|
|
267
|
-
|
|
268
|
+
支持排序切换的命令:opinion / summary / pamirs-summary / research / foreign-report / announcement / announcement-hk / announcement-us / foreign-opinion / independent-opinion / official-account。**要最新的加 `--rank-type 2`(严格按 `publishTime` 倒序);要最相关的用默认 `--rank-type 1` + `--keyword` + `--search-type 2`(综合排序按相关度挑条目)**——两者从同一结果集里取的是**不同的子集**,不是同一批内容换个排法。⚠️ 综合排序**挑完之后仍按时间倒序排列**,所以别用「结果是不是时间倒序」判断它有没有生效,要比条目 ID。没有 `--keyword` 时两者无差异,属正常。详见 `insight.md` 开头。其他 list 命令按 API 默认排序。
|
|
268
269
|
|
|
269
270
|
## 异常处理
|
|
270
271
|
|
|
@@ -283,30 +284,31 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
283
284
|
| 错误码 | 含义 | CLI 行为 | Agent 是否介入 |
|
|
284
285
|
|--------|------|---------|--------------|
|
|
285
286
|
| ✅ `100003` | 参数值非法——**最宽的兜底码**:类型错、`limit` 越界都归这里。**msg 通常已指明字段**(如「请求体字段类型不匹配: size 期望类型 Integer」「limit 最小为 1,最大为 10000」),先读 msg 再猜 | — | 按 msg 指的字段改;msg 没指明才对照 `--help` 查枚举拼写,**不要重试同命令** |
|
|
286
|
-
| ✅ `999999` | 系统错误。**`indicator`(EDE)2026-08-01 前用此码表示查询无数据,之后改为返回数据体**,所以这个码基本只剩真故障。⚠️
|
|
287
|
+
| ✅ `999999` | 系统错误。**`indicator`(EDE)2026-08-01 前用此码表示查询无数据,之后改为返回数据体**,所以这个码基本只剩真故障。⚠️ 现在「无数据」是保留行列的占位单元格(多为 `null`,个别指标是 `0`,见 `indicator.md`),**不是空表**;空表另有含义(整轴 code 未识别) | 普通端点自动重试 ×2;🔴 贵档与 `indicator` 端点不重试 | 确认参数无误仍报此码即服务端故障 |
|
|
287
288
|
| ✅ `410110` | **异步生成中**(HTTP 400,旧码未切)。新码 `140001`,CLI 两码都认 | 轮询视为 pending | 继续等 |
|
|
288
289
|
| ✅ `410111` | **异步生成失败**(HTTP 400,旧码未切)。新码 `140002`,CLI 两码都认 | 终态 | **不重试**,换参数 |
|
|
289
|
-
| ✅ `130002` |
|
|
290
|
+
| ✅ `130002` | 资源不存在——下载类的常见码:`reportId` 不存在 / 非数字多归此码。另有更具体的 `130003`(资源未生成 / 无附件)与 `130005`(`fileType` 非法) | — | 确认 ID 有效且本账号可见;换 `--file-type` 或换一篇验证 |
|
|
290
291
|
| ✅ `130001` | 数据未找到,或**该指标无权限**(`indicator` 内层失败会带具体 msg 如"指标无权限") | — | 检查查询条件与指标权限 |
|
|
291
292
|
| ✅ `100001` | 缺必填参数——**msg 带字段名**(「缺少必填参数: reportId」) | — | 按 msg 指的字段补上 |
|
|
292
|
-
| ✅ `110001` / `110002` | 日期格式错(msg 带字段名)/
|
|
293
|
-
| ✅ `120001` | 证券代码无效——msg 带原因(「非有效A
|
|
294
|
-
| ✅ `110003` |
|
|
293
|
+
| ✅ `110001` / `110002` | 日期格式错(msg 带字段名)/ 起晚于止。⚠️ **服务端 2026-08-14 起对多种格式做宽松解析**(`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 位毫秒 |
|
|
294
|
+
| ✅ `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`) |
|
|
295
|
+
| ✅ `110003` | **超出账号数据权限的时间范围**。范围按账号等级而定、**不是平台常量**,且**同一账号下按接口还可能不一样**——出现过同日同指标同证券 `cross-section` 取得到、`indicator screener` 却报此码的情况 | — | 把日期移进权限范围内;整个区间都早于下界时缩短窗口无用(`--fiscal-year 2015` 无论怎么缩都报错)。`screener` 撞界改用 `cross-section` 拉数再本地筛,否则联系客户经理开通 |
|
|
295
296
|
| ✅ `100006` | 查询/下载数量超限——**取代旧 `430007`**;实测 `fund-flow` 全市场不传日期即此码 | — | 缩短日期范围或调小 `--size`/`--limit`;全市场场景应已自动分片 |
|
|
296
297
|
| ✅ `240001` | 财报期未披露或超出查询期(`earnings-review` 提交阶段就报,**不扣积分**) | — | 换更早的 `--period`(`2025q3` → `2025interim`) |
|
|
297
298
|
| ✅ `250001` | 不支持的数据源——**取代旧 `433007`** | — | 检查 `resourceType + sourceId` 组合 |
|
|
298
299
|
| ✅ `999011` | 开发账号凭证无效——**取代旧 `8000014`/`8000015`,已合并,不再区分 AK 错还是 SK 错** | 登录即失败,**不重试** | 检查 `GANGTISE_ACCESS_KEY`/`GANGTISE_SECRET_KEY` 是否写反或未 export |
|
|
299
300
|
| ✅ `999010` | 接口地址不存在 | — | `raw call` 的 key 可能已下线,用 `gangtise raw list` 核对 |
|
|
301
|
+
| ✅ `999004` | 无资源权限。**整库未开通与单条记录不可见都走这个码**——`insight pamirs-summary list`(专家纪要库需单独购买)未开通时即报此码 | — | `list` 撞上多为整库未开通,先确认该数据库是否已购买;`download` 撞上再考虑换一条本账号可见的记录 |
|
|
300
302
|
| ✅ `0000001008` | Token 服务端失效(他处登录挤掉)——**旧码未切,token 自愈依赖它** | **强制重新登录并重试一次** | 无 AK/SK 时无法自愈,提示重新登录 |
|
|
301
303
|
| ✅ `0000001007` | 请求未携带 Bearer token | — | 检查 `GANGTISE_TOKEN` / AK/SK 是否已 export |
|
|
302
304
|
| ✅ `900002` | **请求方法不正确**(msg「请求类型有误」,HTTP 405) | — | `raw call` 时确认该 endpoint 是 GET 还是 POST |
|
|
303
305
|
| ✅ `140002` | 终态参数错:AI 异步生成失败,或 `indicator` 的指标必填参数缺失 / 枚举越界 / 表达式语法错(实测 2026-08-02) | **不重试**(终态码) | 按 msg 改参数重提;EDE 的参数名与枚举读 `indicator search --format json` 的 `parameterList` |
|
|
304
306
|
|
|
305
|
-
**⚠️
|
|
306
|
-
-
|
|
307
|
-
- **财报接口的日期按「报告期末」过滤**,不是公告日:`fundamental balance-sheet` 等的 `--start-date`/`--end-date` 匹配的是 `endDate` 字段(如 `20200630
|
|
308
|
-
-
|
|
309
|
-
-
|
|
307
|
+
**⚠️ 几类"不报错"的坑(最难发现,逐条都关系到拿没拿到对的数)**
|
|
308
|
+
- **日期只写 `YYYY-MM-DD`、时间只写 `YYYY-MM-DD HH:mm:ss`(或 10/13 位时间戳)**。CLI v0.28.0 起 date 与 datetime 两类(含所有 insight/vault 透传参数)都在发请求前校验,其余写法直接报 `ValidationError`。接口本身能接受多种格式,但**「年在后」写法(`01-07-2026`)存在月/日歧义**——会按月在前读作 1 月 7 日,本意写 7 月 1 日的就拿到差半年的数据且不报错。**CLI 只放行 `YYYY-MM-DD` 就是为了堵掉这个歧义,这是有意的**;绕过 CLI 直连接口时务必自己用标准格式
|
|
309
|
+
- **财报接口的日期按「报告期末」过滤**,不是公告日:`fundamental balance-sheet` 等的 `--start-date`/`--end-date` 匹配的是 `endDate` 字段(如 `20200630`);公告日看 `earliestAnncDate`(首次公告日,做时点对齐用这个)而不是 `announcementDate`。**查某期财报要传季度末日期**(`2020-06-30` / `2020-03-31` / `2020-09-30` / `2020-12-31`);传 `2020-07-01` 这类非报告期日期会返回 0 行,属正常行为,不是没数据
|
|
310
|
+
- **非法证券代码**:`quote day-kline` / `realtime` / `minute-kline` / `fund-flow` 与 Fundamental 系都会报 `120001`,按报错核对后缀即可。⚠️ **三个已下线的旧端点 `quote day-kline-hk` / `day-kline-us` / `index-day-kline` 则返回 `total:0`**,与"该票该区间真无数据"无法区分——**用这三个拿到空结果时先回头核对代码后缀**。它们的能力已并入统一 `day-kline`(支持 A 股 / 港股 / 美股 / 交易所指数 / 概念指数 `.GT` / 申万行业指数 `.SWI`),新代码直接用 `day-kline`
|
|
311
|
+
- **枚举值拼错的后果按端点不同,且部分端点不报错**:纪要 `summary`、三个公告 list、路演 `roadshow`、调研 `site-visit` 的 `--search-type` / `--rank-type` / `--category` / `--market` 等传非法值会报 `100005`;而 `insight research`(内资研报)、`foreign-report`(外资研报)、`official-account`、`opinion` 上非法值被**静默忽略**,该筛选按未传处理。🔴 **最坏的一种**:`research` / `foreign-report` 上非法的 `--search-type` 会**连带吞掉 `--keyword`**——`research list --keyword 茅台 --search-type 99` 拿到的是**未经筛选的全库**,与不传 `--keyword` 的结果一致,而不是搜索结果。**v0.32.0 起 CLI 本地拦截**:全部 `--search-type` / `--rank-type`(19 处)、全部 `--file-type`(9 处下载,`foreign-report` 为 1–4、其余 1–2)、`pamirs-summary` 的 `--category` / `--market`、`--top` 上限、以及 `reference securities-search` / `institution-search` / `official-account-search` 的 `--category`。**仍未覆盖**:`insight research/summary --category`、`--market`、`--source`、`--llm-tag` 等仍是自由字符串,拼错不报错也不生效——这些要自己核对。**拼错的筛选条件会伪装成"结果正常",枚举拼写要自己保证**
|
|
310
312
|
- **`viewpoint-debate` 传敏感内容不会被提前拦截**——实测不返回 `240002`,而是照常受理、扣满 50 积分、生成阶段才以 `410111` 失败。**提交前自己把关措辞**
|
|
311
313
|
- **`ai one-pager` 的非法 `mode` 被静默忽略**,照常生成并扣 50 积分
|
|
312
314
|
|
|
@@ -316,10 +318,10 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
316
318
|
|--------|------|---------|
|
|
317
319
|
| `999001` / `999002` | 缺 token / token 无效 | 实际返回旧码 `0000001007` / `0000001008` |
|
|
318
320
|
| `999007` / `999008` / `999009` | 方法/媒体类型/请求体不支持 | 实际返回 `900002` / `999999` / `100003` |
|
|
319
|
-
| `999003` / `
|
|
321
|
+
| `999003` / `999005` / `999006` | 无接口权限 / 积分不足 / 限流 | 未构造出(需特定账号状态) |
|
|
320
322
|
| `999012`–`999016` | 账号禁用/过期、租户失效、无长期 token、IP 不合规 | 未构造出 |
|
|
321
|
-
| `100002` / `100004` / `100005` | 类型错 / 分页非法 / 枚举非法 | 类型错归 `100003
|
|
322
|
-
| `
|
|
323
|
+
| `100002` / `100004` / `100005` | 类型错 / 分页非法 / 枚举非法 | 类型错归 `100003`。**`100005` 与 `100006` 2026-08-14 起在部分 list 上真会触发**(枚举非法、`size` 超 50);未覆盖的端点(`research` / `foreign-report` / `opinion` / `official-account`)仍按未传处理,见上方「实测发现的坑」 |
|
|
324
|
+
| `130004` | 下载 ID 非数字 | 多归 `130002` |
|
|
323
325
|
| `140001` / `140002` | 结果生成中 / 处理失败 | 异步端点仍用 `410110` / `410111` |
|
|
324
326
|
| `210001` / `220001` / `230001` | 研报/观点/分享文件不支持下载 | 未构造出 |
|
|
325
327
|
| `230002` | 微信账号未绑定(私域,2026-08-07 新增)。⚠️ **`vault wechat-*` 就在该模块下、够得着**:要求先绑定并激活群消息助理且助理已入群 | 未构造出(本账号已绑定) |
|
|
@@ -336,11 +338,10 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
336
338
|
**其他场景**:
|
|
337
339
|
- CLI 未安装 → `npm install -g gangtise-openapi-cli`
|
|
338
340
|
- **退出码 3 = `partial: true`,结果少了数据**(保留已取到的部分;stderr 有 warning,`--format json` 才看得见标记,table/csv/jsonl 只有数据行、看不出问题):
|
|
339
|
-
- 触发场景:翻页/K线分片有页失败、服务端返回行数与 `total`
|
|
340
|
-
- **EDE
|
|
341
|
-
- **`screener` 的缺列按表达式的布尔结构判**:把缺列的变量当作「无法求值」,看整个表达式**是否还有一条能成立的分支**——`A && B` 要两边都可求值,`A || B` 只要一边。**一条分支都不剩 → 退出码 1、不输出任何结果**(返回的行以「通过了该条件」的名义呈现,而条件根本无法证明被执行过);**还有分支可求值,或缺的只是未参与表达式的辅助变量 → `partial` + 退出码 3**。所以 `F1 || F2` 缺 F1 是降级(行可能靠 F2 正当命中),缺两个则是致命;`F1 && (F2 || F3)` 缺 F1 仍是致命(F1 是必选合取项)。⚠️
|
|
341
|
+
- 触发场景:翻页/K线分片有页失败、服务端返回行数与 `total` 矛盾(提前短页)、`total` 撞服务端上限(`totalCapped`,见 opinion 家族)。附带定位字段:页失败 `failedPages`;分片失败 `failedShards`、分片撞行数上限 `truncatedShards`(均带日期区间可缩窗补拉)。⚠️ EDE 的 `omittedIndicators` / `omittedSecurities` 仍在代码里,但 2026-08-15 起写错的 code 在服务端就被 `100003` 拒了(退出 1),这条路基本收不到样本
|
|
342
|
+
- **EDE 代码写错现在是退出码 1 + `100003`,msg 里直接点名是哪个 code**(美股后缀用 `.O`/`.N`,不是 `.US`),不用再从退出码 3 反推。真的没覆盖仍是占位单元格(多数 `null`、个别指标 `0`)+ 退出码 0
|
|
343
|
+
- **`screener` 的缺列按表达式的布尔结构判**:把缺列的变量当作「无法求值」,看整个表达式**是否还有一条能成立的分支**——`A && B` 要两边都可求值,`A || B` 只要一边。**一条分支都不剩 → 退出码 1、不输出任何结果**(返回的行以「通过了该条件」的名义呈现,而条件根本无法证明被执行过);**还有分支可求值,或缺的只是未参与表达式的辅助变量 → `partial` + 退出码 3**。所以 `F1 || F2` 缺 F1 是降级(行可能靠 F2 正当命中),缺两个则是致命;`F1 && (F2 || F3)` 缺 F1 仍是致命(F1 是必选合取项)。⚠️ **前提是服务端返回了命中行**——零命中时走「nothing matched」+ **退出码 0**。**空集别急着当成「条件成立但无标的符合」**:指标码写错现在会直接报 `100003`(退出 1),所以剩下的头号嫌疑是**占位值 `0`** —— `F1 > 0` 在 `is_dnrpnp` 这类填 `0` 的指标上必然筛出空集
|
|
342
344
|
- 拿到 3 就必须告知用户缺了哪段,不能当成功静默继续
|
|
343
|
-
- (历史:条件选股把同一指标绑到多个变量曾触发 `unreliable: true` + `duplicatedIndicators`,因服务端把所有绑定按最早日期取数。2026-08-08 服务端已修复,该标记**已从 CLI 移除**,这类查询现在正常可用)
|
|
344
345
|
- 空结果(list 为空数组) → 建议扩大时间范围、换关键词、去掉部分筛选
|
|
345
346
|
- 模糊公司名匹配多只("平安" → 中国平安 / 平安银行 / ...) → 列出让用户选
|
|
346
347
|
- 下载文件路径冲突 → 询问覆盖
|
|
@@ -358,10 +359,10 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
358
359
|
**`list` 全空但参数看着对**
|
|
359
360
|
1. 时间窗太窄 → 扩到 30 天试
|
|
360
361
|
2. `--security` 后缀拼错(如 `300750` 漏了 `.SZ`)
|
|
361
|
-
3. 行业 ID 用错体系:`--industry`(用 `citicIndustry` 码 `1008001xx`)/ `--research-area
|
|
362
|
+
3. 行业 ID 用错体系:`--industry`(用 `citicIndustry` 码 `1008001xx`)/ `--research-area`(行业同样用 `citicIndustry` 码 `1008001xx`,方向才用 `gangtiseIndustry` 码 `122000xxx`——`gangtiseIndustry` 里**只有 6 条方向码、没有行业**)/ `--gts-code`(申万 `821xxx.SWI`)。申万数字码 `104xx0000` 用于 `--research-area` 时多数端点返 0,详见 `references/commands/reference-and-lookup.md`
|
|
362
363
|
4. `--rating` / `--category` 等枚举值拼错(参考对应命令的 references 文件)
|
|
363
364
|
|
|
364
|
-
**`999011` 凭证无效**(旧码 `8000014`/`8000015
|
|
365
|
+
**`999011` 凭证无效**(旧码 `8000014`/`8000015`;不区分是 AK 错还是 SK 错,**登录直接失败、CLI 不重试**)
|
|
365
366
|
1. `echo $GANGTISE_ACCESS_KEY` 验环境变量是否 export
|
|
366
367
|
2. AK 和 SK 是否写反
|
|
367
368
|
3. 账号是否到期 / 异常(`gangtise auth status`;对应 `999012`/`999013`)
|
|
@@ -377,7 +378,9 @@ gangtise reference securities-search --keyword <公司名> --category stock --to
|
|
|
377
378
|
|
|
378
379
|
**翻页很慢 / 卡住** → `--verbose` 看哪一页慢;可 `GANGTISE_PAGE_CONCURRENCY=10` 提速,或缩小时间范围。
|
|
379
380
|
|
|
380
|
-
|
|
381
|
+
**全市场 K 线报 `100006`**(旧码 `430007`)→ 单日数据仍超 10K 行(极端情况)→ 临时改用更窄的 `--start-date`/`--end-date`,或改为单只 `--security` 单独拉。
|
|
382
|
+
|
|
383
|
+
**`quote day-kline --security all` 被 CLI 拒**(`'all' is not a whole-market keyword`)→ 服务端 2026-08-14 停止支持该写法 → 换成 `aShares` / `hkStocks` / `usStocks` 之一,单独传。
|
|
381
384
|
|
|
382
385
|
**AI agent 命令(one-pager 等)超时** → 服务端生成耗时长,CLI 默认 30s → `GANGTISE_TIMEOUT_MS=120000` 后重试。
|
|
383
386
|
|
|
@@ -53,10 +53,10 @@ gangtise ai peer-comparison --security-code <code>
|
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
gangtise ai stock-summary --security <code> [--security <code2> ...]
|
|
56
|
-
gangtise ai stock-summary --security <aShares|hkStocks>
|
|
57
56
|
```
|
|
58
57
|
|
|
59
|
-
- `--security
|
|
58
|
+
- `--security`(**必选**,可重复):**只收具体证券代码**,单次最多 6000 个
|
|
59
|
+
- 🔴 **2026-08-14 起不再支持全市场批量**:`aShares` / `hkStocks` 这类市场关键词已被移除,CLI 会本地报错(服务端对它们返 `120001`,读起来像代码写错,容易误判)。需要覆盖一批标的就先用 `reference securities-search` 或自有清单取到代码,再分批传入
|
|
60
60
|
- **仅支持 A 股和港股**
|
|
61
61
|
- **积分**:`3`/条;个股若无看点总结则不在返回列表中,也不扣分
|
|
62
62
|
- 返回字段:`securityCode` / `securityName` / `summary`(精炼投研总结)/ `date`(更新日期 `yyyy-MM-dd`)
|
|
@@ -64,7 +64,6 @@ gangtise ai stock-summary --security <aShares|hkStocks>
|
|
|
64
64
|
**示例:**
|
|
65
65
|
```bash
|
|
66
66
|
GANGTISE_TIMEOUT_MS=120000 gangtise ai stock-summary --security 600519.SH --security 00700.HK --format json # 茅台 / 腾讯看点
|
|
67
|
-
GANGTISE_TIMEOUT_MS=120000 gangtise ai stock-summary --security hkStocks --format json # 全部港股,total 2662
|
|
68
67
|
```
|
|
69
68
|
|
|
70
69
|
## 调研提纲 `ai research-outline`
|
|
@@ -16,8 +16,9 @@ gangtise fundamental <income-statement|balance-sheet|cash-flow> --security-code
|
|
|
16
16
|
- `--report-type`:`consolidated`(默认)| `consolidatedRestated` | `standalone` | `standaloneRestated`
|
|
17
17
|
- `--fiscal-year` 可重复:`--fiscal-year 2023 --fiscal-year 2024`
|
|
18
18
|
- `--start-date`/`--end-date` 有值时覆盖 `--fiscal-year`
|
|
19
|
-
- **固定返回字段**(无需 `--field` 指定):`securityCode` `companyName` `category` `announcementDate` `endDate` `fiscalYear` `period` `reportType` `companyType` `currency` `unit`
|
|
20
|
-
-
|
|
19
|
+
- **固定返回字段**(无需 `--field` 指定):`securityCode` `companyName` `category` `announcementDate` **`earliestAnncDate`** `endDate` `fiscalYear` `period` `reportType` `companyType` `currency` `unit`
|
|
20
|
+
- 🔴 **做时点对齐(「这份财报在某日是否已公开」)用 `earliestAnncDate`(首次公告日),不要用 `announcementDate`**:`announcementDate` 在部分证券上会把同一财年各期都填成同一个日期,据此判断「某期数据在某日是否已公开」会得出相反的结论;`earliestAnncDate` 是该报告期自身的首次公告日。盘后披露的报告计次日,即**永不早于**真实披露时点,用来做 point-in-time 是安全的方向。要交叉核实披露日可查 `insight announcement list`
|
|
21
|
+
- `companyType` / `currency` 在三张累计口径表上均正确(`companyType=一般企业`/`银行`/`保险公司`/`证券公司`,`currency=人民币`)。⚠️ **仅 A 股 `*-quarterly` 单季表例外**:`companyType` 返回未映射的数字码(如 `102119999`),`currency` 正确——单季表读公司类型时按需自行映射,科目数字本身不受影响
|
|
21
22
|
|
|
22
23
|
**常用字段速查:**
|
|
23
24
|
- 利润表:`totalOpRev` 营收 | `netProfit` 净利润 | `netProfitAttrParent` 归母 | `basicEPS` EPS | `rdExp` 研发
|