gangtise-openapi-cli 0.41.0 → 0.42.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 +48 -50
- package/dist/src/cli.js +23 -1131
- package/dist/src/commands/ai.js +155 -0
- package/dist/src/commands/alternative.js +56 -0
- package/dist/src/commands/auth.js +37 -0
- package/dist/src/commands/bond.js +76 -0
- package/dist/src/commands/fundamental.js +137 -0
- package/dist/src/commands/indicator.js +134 -0
- package/dist/src/commands/insight.js +337 -0
- package/dist/src/commands/quote.js +147 -0
- package/dist/src/commands/raw.js +49 -0
- package/dist/src/commands/reference.js +58 -0
- package/dist/src/commands/shared.js +145 -0
- package/dist/src/commands/tool.js +55 -0
- package/dist/src/commands/vault.js +203 -0
- package/dist/src/core/args.js +7 -4
- package/dist/src/core/asyncContent.js +32 -19
- package/dist/src/core/client.js +11 -4
- package/dist/src/core/config.js +16 -3
- package/dist/src/core/endpoints.js +3 -0
- package/dist/src/core/exitStatus.js +17 -0
- package/dist/src/core/fileParse.js +3 -20
- package/dist/src/core/opinionDetail.js +39 -0
- package/dist/src/core/output.js +18 -4
- package/dist/src/core/printer.js +7 -8
- package/dist/src/core/rowSink.js +19 -7
- package/dist/src/core/transport.js +4 -3
- package/dist/src/version.js +1 -1
- package/gangtise-openapi/SKILL.md +5 -5
- package/gangtise-openapi/references/commands/ai.md +1 -1
- 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 +8 -5
- package/gangtise-openapi/references/examples.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
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { isAsyncPending,
|
|
3
|
+
import { isAsyncPending, pollUntilDone } from "./asyncContent.js";
|
|
4
4
|
import { saveDownloadResult } from "./download.js";
|
|
5
5
|
import { ApiError, ValidationError } from "./errors.js";
|
|
6
|
-
import { isTransientError } from "./transport.js";
|
|
7
6
|
export const FILE_PARSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
8
7
|
export async function submitFileParse(client, filePath) {
|
|
9
8
|
const resolved = path.resolve(filePath);
|
|
@@ -50,22 +49,6 @@ export async function fetchFileParseResult(client, taskId, output) {
|
|
|
50
49
|
return "ok";
|
|
51
50
|
}
|
|
52
51
|
export async function pollFileParseResult(client, taskId, output) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (await fetchFileParseResult(client, taskId, output) === "ok")
|
|
56
|
-
return "ok";
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
if (!isTransientError(error))
|
|
60
|
-
throw error;
|
|
61
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
62
|
-
process.stderr.write(`Attempt ${attempt}/${POLL_MAX_ATTEMPTS}: transient error (${msg.slice(0, 80)}), continuing to wait...\n`);
|
|
63
|
-
}
|
|
64
|
-
if (attempt < POLL_MAX_ATTEMPTS) {
|
|
65
|
-
const delay = nextPollDelayMs(attempt);
|
|
66
|
-
process.stderr.write(`Attempt ${attempt}/${POLL_MAX_ATTEMPTS}: parse result not ready, retrying in ${Math.round(delay / 1000)}s...\n`);
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return "timeout";
|
|
52
|
+
const outcome = await pollUntilDone(async () => await fetchFileParseResult(client, taskId, output), "parse result not ready");
|
|
53
|
+
return outcome === "ok" ? "ok" : "timeout";
|
|
71
54
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ApiError, ValidationError } from "./errors.js";
|
|
2
|
+
export async function fetchOpinionDetails(client, endpointKey, idListField, idField, ids) {
|
|
3
|
+
const BATCH = 20;
|
|
4
|
+
const unique = [...new Set(ids)];
|
|
5
|
+
if (!unique.length)
|
|
6
|
+
throw new ValidationError("pass at least one opinion ID");
|
|
7
|
+
const list = [];
|
|
8
|
+
let unfetched = [];
|
|
9
|
+
let unfetchedError;
|
|
10
|
+
for (let i = 0; i < unique.length; i += BATCH) {
|
|
11
|
+
try {
|
|
12
|
+
const rows = await client.call(endpointKey, { [idListField]: unique.slice(i, i + BATCH) });
|
|
13
|
+
list.push(...rows);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (i === 0)
|
|
17
|
+
throw error;
|
|
18
|
+
unfetched = unique.slice(i);
|
|
19
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20
|
+
unfetchedError = { message, ...(error instanceof ApiError ? { code: error.code, traceId: error.traceId } : {}) };
|
|
21
|
+
process.stderr.write(`[gangtise] warning: ${endpointKey} failed on batch ${i / BATCH + 1} (${message}); ${unfetched.length} ID(s) not fetched: ${unfetched.join(", ")}. The bodies already returned are kept below — re-run for the IDs in unfetchedIds only.\n`);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const returned = new Set(list.map((row) => String(row[idField])));
|
|
26
|
+
const missing = unique.filter((id) => !returned.has(id) && !unfetched.includes(id));
|
|
27
|
+
const out = { total: list.length, list };
|
|
28
|
+
if (missing.length > 0) {
|
|
29
|
+
process.stderr.write(`[gangtise] warning: no body returned for ${missing.length} ID(s): ${missing.join(", ")} — the server skips an ID it has no body for without an error (e.g. a wrong ID; a just-published opinion may also not have its body yet). Result marked partial (exit 3).\n`);
|
|
30
|
+
out.missingIds = missing;
|
|
31
|
+
}
|
|
32
|
+
if (unfetched.length > 0) {
|
|
33
|
+
out.unfetchedIds = unfetched;
|
|
34
|
+
out.unfetchedError = unfetchedError;
|
|
35
|
+
}
|
|
36
|
+
if (missing.length > 0 || unfetched.length > 0)
|
|
37
|
+
out.partial = true;
|
|
38
|
+
return out;
|
|
39
|
+
}
|
package/dist/src/core/output.js
CHANGED
|
@@ -170,16 +170,26 @@ export async function streamOutputToFile(value, format, outputPath) {
|
|
|
170
170
|
stream.on("error", () => { });
|
|
171
171
|
try {
|
|
172
172
|
if (format === "jsonl") {
|
|
173
|
+
let chunk = [];
|
|
173
174
|
for (const item of jsonlItems(value)) {
|
|
174
|
-
|
|
175
|
+
chunk.push(JSON.stringify(item));
|
|
176
|
+
if (chunk.length >= LINES_PER_WRITE) {
|
|
177
|
+
await writeLines(stream, chunk);
|
|
178
|
+
chunk = [];
|
|
179
|
+
}
|
|
175
180
|
}
|
|
181
|
+
await writeLines(stream, chunk);
|
|
176
182
|
}
|
|
177
183
|
else {
|
|
178
|
-
|
|
184
|
+
let chunk = ["\ufeff" + csvColumns.map(csvEscape).join(",")];
|
|
179
185
|
for (const row of csvRows) {
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
chunk.push(csvColumns.map((column) => csvEscape(formatScalar(row[column]))).join(","));
|
|
187
|
+
if (chunk.length >= LINES_PER_WRITE) {
|
|
188
|
+
await writeLines(stream, chunk);
|
|
189
|
+
chunk = [];
|
|
190
|
+
}
|
|
182
191
|
}
|
|
192
|
+
await writeLines(stream, chunk);
|
|
183
193
|
}
|
|
184
194
|
await new Promise((resolve, reject) => {
|
|
185
195
|
stream.end((err) => err ? reject(err) : resolve());
|
|
@@ -215,6 +225,10 @@ export function csvEscape(value) {
|
|
|
215
225
|
return `"${out.replaceAll("\"", "\"\"")}"`;
|
|
216
226
|
return out;
|
|
217
227
|
}
|
|
228
|
+
export const LINES_PER_WRITE = 1000;
|
|
229
|
+
export function writeLines(stream, lines) {
|
|
230
|
+
return lines.length === 0 ? Promise.resolve() : writeLine(stream, lines.join("\n"));
|
|
231
|
+
}
|
|
218
232
|
export function writeLine(stream, line) {
|
|
219
233
|
return new Promise((resolve, reject) => {
|
|
220
234
|
const ok = stream.write(line + "\n", (err) => err ? reject(err) : undefined);
|
package/dist/src/core/printer.js
CHANGED
|
@@ -5,16 +5,16 @@ import { countOutputRows, digestFile, pickList, renderOutput, saveOutputIfNeeded
|
|
|
5
5
|
import { getRowSink } from "./rowSink.js";
|
|
6
6
|
import { extractTitles, writeTitleCache } from "./titleCache.js";
|
|
7
7
|
import { CLI_VERSION } from "../version.js";
|
|
8
|
+
import { decidedExitCode, EXIT_SUPERSEDED, markIncomplete, markSuperseded } from "./exitStatus.js";
|
|
8
9
|
const LARGE_RESULT_ROWS = 50_000;
|
|
9
|
-
export const SUPERSEDED_EXIT =
|
|
10
|
+
export const SUPERSEDED_EXIT = EXIT_SUPERSEDED;
|
|
10
11
|
export async function warnIfSuperseded(output, digest) {
|
|
11
12
|
const actual = await digestFile(output).catch(() => undefined);
|
|
12
13
|
if (!actual || actual.sha256 === digest.sha256)
|
|
13
14
|
return;
|
|
14
15
|
process.stderr.write(`[gangtise] warning: ${output} was replaced by another export to the same path while this one was finishing — the file there is not this command's output `
|
|
15
16
|
+ `(wrote sha256 ${digest.sha256.slice(0, 12)}…, found ${actual.sha256.slice(0, 12)}…). Give concurrent exports distinct --output paths.\n`);
|
|
16
|
-
|
|
17
|
-
process.exitCode = SUPERSEDED_EXIT;
|
|
17
|
+
markSuperseded();
|
|
18
18
|
}
|
|
19
19
|
function warnIfLargeInMemory(items, format) {
|
|
20
20
|
if (items && items.length >= LARGE_RESULT_ROWS) {
|
|
@@ -63,7 +63,7 @@ async function stageExportMeta(output, format, rows, columns, normalized, comple
|
|
|
63
63
|
format,
|
|
64
64
|
rows,
|
|
65
65
|
complete,
|
|
66
|
-
exitCode:
|
|
66
|
+
exitCode: decidedExitCode(),
|
|
67
67
|
command: redactSecrets(process.argv.slice(2)),
|
|
68
68
|
cliVersion: CLI_VERSION,
|
|
69
69
|
fetchedAt: localIso(new Date()),
|
|
@@ -121,11 +121,10 @@ export async function printData(data, format, output, cache) {
|
|
|
121
121
|
if (typeof meta.total === "number" && format !== "json") {
|
|
122
122
|
process.stderr.write(`Total: ${meta.total}, showing: ${showing}\n`);
|
|
123
123
|
}
|
|
124
|
-
if (meta.partial === true)
|
|
125
|
-
|
|
126
|
-
}
|
|
124
|
+
if (meta.partial === true)
|
|
125
|
+
markIncomplete();
|
|
127
126
|
}
|
|
128
|
-
const complete =
|
|
127
|
+
const complete = decidedExitCode() === 0;
|
|
129
128
|
if (output) {
|
|
130
129
|
const staged = format === "csv" || format === "jsonl"
|
|
131
130
|
? await stageExportMeta(output, format, streamed ? streamed.dataRows : countOutputRows(normalized, format), columns, normalized, complete)
|
package/dist/src/core/rowSink.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createReadStream, createWriteStream } from "node:fs";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { assertColumnarHeader, zipFieldRow } from "./normalize.js";
|
|
5
|
-
import { csvEscape, digestFile, formatScalar, stagingPath, writeLine } from "./output.js";
|
|
5
|
+
import { csvEscape, digestFile, formatScalar, stagingPath, writeLine, writeLines, LINES_PER_WRITE } from "./output.js";
|
|
6
6
|
import { extractTitles, MAX_TITLES_PER_ENDPOINT } from "./titleCache.js";
|
|
7
7
|
export const ROW_SINK = Symbol("gangtise.rowSink");
|
|
8
8
|
const STREAM_THRESHOLD = 1000;
|
|
@@ -100,6 +100,7 @@ export class ExportSink {
|
|
|
100
100
|
}
|
|
101
101
|
async write(rows) {
|
|
102
102
|
const stream = this.stream;
|
|
103
|
+
let chunk = [];
|
|
103
104
|
for (const row of rows) {
|
|
104
105
|
let item = row;
|
|
105
106
|
if (Array.isArray(row)) {
|
|
@@ -118,7 +119,11 @@ export class ExportSink {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
chunk.push(JSON.stringify(item));
|
|
123
|
+
if (chunk.length >= LINES_PER_WRITE) {
|
|
124
|
+
await writeLines(stream, chunk);
|
|
125
|
+
chunk = [];
|
|
126
|
+
}
|
|
122
127
|
this.rows++;
|
|
123
128
|
if (this.cache && this.titleCount < MAX_TITLES_PER_ENDPOINT) {
|
|
124
129
|
for (const [id, title] of Object.entries(extractTitles([item], this.cache))) {
|
|
@@ -128,6 +133,7 @@ export class ExportSink {
|
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
135
|
}
|
|
136
|
+
await writeLines(stream, chunk);
|
|
131
137
|
}
|
|
132
138
|
async assembleCsv() {
|
|
133
139
|
const out = createWriteStream(this.partPath, { encoding: "utf8" });
|
|
@@ -139,17 +145,18 @@ export class ExportSink {
|
|
|
139
145
|
const input = createReadStream(this.rowsPath, { encoding: "utf8" });
|
|
140
146
|
try {
|
|
141
147
|
let index = 0;
|
|
142
|
-
|
|
148
|
+
let csvLines = [];
|
|
149
|
+
const emit = (line) => {
|
|
143
150
|
if (!line)
|
|
144
151
|
return;
|
|
145
152
|
const item = JSON.parse(line);
|
|
146
153
|
if (objectMode) {
|
|
147
154
|
if (!isObjectRow(item))
|
|
148
155
|
return;
|
|
149
|
-
|
|
156
|
+
csvLines.push(columns.map((column) => csvEscape(formatScalar(item[column]))).join(","));
|
|
150
157
|
}
|
|
151
158
|
else {
|
|
152
|
-
|
|
159
|
+
csvLines.push(`${csvEscape(String(index++))},${csvEscape(formatScalar(item))}`);
|
|
153
160
|
}
|
|
154
161
|
};
|
|
155
162
|
let rest = "";
|
|
@@ -157,9 +164,14 @@ export class ExportSink {
|
|
|
157
164
|
const lines = (rest + String(chunk)).split("\n");
|
|
158
165
|
rest = lines.pop() ?? "";
|
|
159
166
|
for (const line of lines)
|
|
160
|
-
|
|
167
|
+
emit(line);
|
|
168
|
+
if (csvLines.length >= LINES_PER_WRITE) {
|
|
169
|
+
await writeLines(out, csvLines);
|
|
170
|
+
csvLines = [];
|
|
171
|
+
}
|
|
161
172
|
}
|
|
162
|
-
|
|
173
|
+
emit(rest);
|
|
174
|
+
await writeLines(out, csvLines);
|
|
163
175
|
await endStream(out);
|
|
164
176
|
}
|
|
165
177
|
catch (error) {
|
|
@@ -39,7 +39,7 @@ export function getDispatcher() {
|
|
|
39
39
|
cachedDispatcher = new Agent({
|
|
40
40
|
keepAliveTimeout: 60_000,
|
|
41
41
|
keepAliveMaxTimeout: 600_000,
|
|
42
|
-
connections: 16,
|
|
42
|
+
connections: Math.max(16, PAGE_CONCURRENCY),
|
|
43
43
|
pipelining: 1,
|
|
44
44
|
});
|
|
45
45
|
}
|
|
@@ -62,8 +62,9 @@ export async function runWithConcurrency(items, concurrency, fn) {
|
|
|
62
62
|
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
63
63
|
return results;
|
|
64
64
|
}
|
|
65
|
-
export async function runInOrder(items, concurrency, fn, consume) {
|
|
65
|
+
export async function runInOrder(items, concurrency, fn, consume, window = concurrency) {
|
|
66
66
|
const width = Math.max(1, concurrency);
|
|
67
|
+
const maxPending = Math.max(width, window);
|
|
67
68
|
const pending = new Map();
|
|
68
69
|
let next = 0;
|
|
69
70
|
let failure = null;
|
|
@@ -104,7 +105,7 @@ export async function runInOrder(items, concurrency, fn, consume) {
|
|
|
104
105
|
return;
|
|
105
106
|
pending.set(index, result);
|
|
106
107
|
drain();
|
|
107
|
-
while (!failure && pending.size >=
|
|
108
|
+
while (!failure && pending.size >= maxPending) {
|
|
108
109
|
await new Promise((resolve) => waiters.push(resolve));
|
|
109
110
|
}
|
|
110
111
|
});
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.
|
|
1
|
+
export const CLI_VERSION = "0.42.0";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gangtise-openapi
|
|
3
|
-
version: "0.
|
|
3
|
+
version: "0.42.0"
|
|
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`,不会静默丢列) |
|
|
@@ -115,7 +115,7 @@ gangtise ai theme-tracking --theme-id <id> --date <yyyy-MM-dd> [--type <name>]
|
|
|
115
115
|
**示例:**
|
|
116
116
|
```bash
|
|
117
117
|
# 查"核电"主题 2026-05-09 的晚报
|
|
118
|
-
|
|
118
|
+
gangtise ai theme-tracking --theme-id 121000002 --date 2026-05-09 --type night --format json
|
|
119
119
|
# 返回 [{"type":"night","date":"2026-05-09","content":"..."}]
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -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 会在 stderr 提示原因);数值列与不存在的名字混传时,回显的字段比每行的值多,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` 组合 |
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
| 情形 | CLI 行为 | Agent 是否介入 |
|
|
70
70
|
|------|---------|--------------|
|
|
71
71
|
| HTTP 5xx / `ECONNRESET` / 超时 | **自动指数退避重试 ×2**(🔴 贵档端点不重放) | 仍失败提示用户 |
|
|
72
|
-
| `ValidationError` | 本地参数校验失败 | 检查 `--from` / `--size` / `--limit`
|
|
72
|
+
| `ValidationError` | 本地参数校验失败 | 检查 `--from` / `--size` / `--limit` 数值(数值只收十进制写法,`1e3` / `0x10` / `5.0` 会被拒),**不要重试同命令** |
|
|
73
|
+
| 返回结构与约定不符(报错含 `returned no … payload` 与 trace) | 不猜测结构、按失败处理:单次请求的命令退出 1;分批 / 分片中某一批出现时,已取到的保留、其余标为未取(退出 3) | 带 trace 反馈;换参数重试没用 |
|
|
73
74
|
|
|
74
75
|
## 退出码 3:结果少了数据
|
|
75
76
|
|
|
@@ -81,10 +82,12 @@
|
|
|
81
82
|
|
|
82
83
|
## 退出码 4:数据写全了,但文件被另一次导出替换
|
|
83
84
|
|
|
84
|
-
本次导出的数据完整写出了,`--output` 指向的文件却在收尾期间被**另一次写向同一路径的导出**替换掉——文件在,只是不是这条命令的产物。stderr 会给出两边的 `sha256` 前缀。给并发的导出各自的 `--output`
|
|
85
|
+
本次导出的数据完整写出了,`--output` 指向的文件却在收尾期间被**另一次写向同一路径的导出**替换掉——文件在,只是不是这条命令的产物。stderr 会给出两边的 `sha256` 前缀。给并发的导出各自的 `--output` 即可。本次生成的 `.meta.json` 记录的是替换检查之前的判定;并发导出时,数据文件和元信息都可能被另一次导出覆盖。所以退出码 4 以本次进程的退出码和 stderr 为准,`sha256` 只能说明当前的数据文件与旁边的元信息是否匹配。
|
|
85
86
|
|
|
86
87
|
与 `3` 分开是因为行没有缺、缺的是目的地。**两者同时发生时退出码是 `3`**——少了行是更重的那一个,替换另在 stderr 说明。
|
|
87
88
|
|
|
89
|
+
**接管道时**:`gangtise … | head` 这类提前关掉读端的用法,CLI 仍以已判定的退出码(`3` / `4`)结束;shell 默认只看管道里最后一个命令,脚本要 `set -o pipefail` 才看得到。
|
|
90
|
+
|
|
88
91
|
## 其他场景
|
|
89
92
|
|
|
90
93
|
- CLI 未安装 → `npm install -g gangtise-openapi-cli`
|
|
@@ -128,7 +131,7 @@
|
|
|
128
131
|
|
|
129
132
|
**`quote day-kline --security all` 被 CLI 拒**(`'all' is not a whole-market keyword`)→ 服务端不支持该写法 → 换成 `aShares` / `hkStocks` / `usStocks` 之一,单独传。
|
|
130
133
|
|
|
131
|
-
**AI
|
|
134
|
+
**AI 生成命令超时** → `one-pager` 等 7 个 agent 类 CLI 已内置 120 秒超时下限,无需再设 `GANGTISE_TIMEOUT_MS`。超时报错时内容可能已在服务端生成并扣费,**同参数重跑会再扣一次**:先告知用户,别自动重跑(详见 `references/commands/ai.md`)。`stock-summary` / `hot-topic` 可前置 `GANGTISE_TIMEOUT_MS=120000`(写整数毫秒;没按所写数值生效时 stderr 会提示)。
|
|
132
135
|
|
|
133
136
|
**估值结果出现大量 `null`** → 最新交易日数据未入库 → 加 `--skip-null` 过滤掉 `value` / `percentileRank` 为 null 的行。
|
|
134
137
|
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
1. 路由 → ai one-pager
|
|
54
54
|
2. 比亚迪 → 002594.SZ(速查表)
|
|
55
55
|
注意:one-pager 用 --security-code,不是 --security
|
|
56
|
-
3. Pre-flight:首次生成常 >30s
|
|
57
|
-
4.
|
|
56
|
+
3. Pre-flight:首次生成常 >30s,CLI 已内置 120 秒超时下限,无需前缀;告知用户"生成中..."
|
|
57
|
+
4. gangtise ai one-pager --security-code 002594.SZ --format json
|
|
58
58
|
5. 返回 {content: "markdown"} → 直接呈现 content,不要展示 JSON 包装
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -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.
|
|
3
|
+
"version": "0.42.0",
|
|
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",
|