@evatick/cli 0.4.1 → 0.4.3
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 +8 -5
- package/lib/cli.js +40 -19
- package/lib/http-client.js +12 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,7 +35,6 @@ eva stock bars --symbol cn:equity:XSHE:000001 \
|
|
|
35
35
|
--interval 1d \
|
|
36
36
|
--start 2026-08-18 \
|
|
37
37
|
--end 2026-08-21 \
|
|
38
|
-
--adjustment forward \
|
|
39
38
|
--limit 3
|
|
40
39
|
```
|
|
41
40
|
|
|
@@ -43,8 +42,11 @@ eva stock bars --symbol cn:equity:XSHE:000001 \
|
|
|
43
42
|
|
|
44
43
|
- `eva instrument search` finds canonical financial instruments by name, code,
|
|
45
44
|
or alias.
|
|
46
|
-
- `eva stock bars` queries normalized OHLCV bars
|
|
45
|
+
- `eva stock bars` queries normalized OHLCV bars with forward adjustment by
|
|
46
|
+
default; pass `--adjustment none` for raw prices.
|
|
47
47
|
- `eva index constituents` queries index constituent memberships.
|
|
48
|
+
- `eva futures bars` queries official mainland China futures daily bars.
|
|
49
|
+
- `eva crypto quotes` queries one explicitly selected cryptocurrency exchange.
|
|
48
50
|
|
|
49
51
|
Run `eva COMMAND --help` or `eva GROUP COMMAND --help` for supported options.
|
|
50
52
|
|
|
@@ -72,9 +74,10 @@ and `eva config show` masks stored keys.
|
|
|
72
74
|
## Current availability
|
|
73
75
|
|
|
74
76
|
EVA Tick is designed as a provider-independent financial data layer. The
|
|
75
|
-
current public service
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
current public service covers mainland China A-shares; SSE, SZSE, and CSI
|
|
78
|
+
indices; CFFEX, SHFE, INE, CZCE, DCE, and GFEX futures; and exchange-scoped
|
|
79
|
+
Binance and Coinbase cryptocurrency markets. Availability varies by deployed
|
|
80
|
+
service and enabled providers.
|
|
78
81
|
|
|
79
82
|
## Output and safety
|
|
80
83
|
|
package/lib/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ const ROOT_HELP = `NAME
|
|
|
19
19
|
eva
|
|
20
20
|
|
|
21
21
|
PURPOSE
|
|
22
|
-
通过 EVA
|
|
22
|
+
通过 EVA 查询股票、指数、中国期货和交易所加密货币数据。
|
|
23
23
|
|
|
24
24
|
USAGE
|
|
25
25
|
eva [--config PATH] [--server-url URL] [--api-key KEY] COMMAND [OPTIONS]
|
|
@@ -31,6 +31,8 @@ OPTIONS
|
|
|
31
31
|
|
|
32
32
|
COMMANDS
|
|
33
33
|
config
|
|
34
|
+
crypto
|
|
35
|
+
futures
|
|
34
36
|
health
|
|
35
37
|
index
|
|
36
38
|
instrument
|
|
@@ -45,16 +47,22 @@ const HELP = {
|
|
|
45
47
|
health: "Usage: eva health [OPTIONS]\n\nOptions:\n --timeout FLOAT [default: 10]\n --retries INTEGER [default: 0]\n --help\n",
|
|
46
48
|
version: "Usage: eva version [OPTIONS]\n\nOptions:\n --help\n",
|
|
47
49
|
instrument: "Usage: eva instrument [OPTIONS] COMMAND [ARGS]...\n\n Discover and resolve canonical instruments.\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n list\n resolve\n search\n show\n",
|
|
48
|
-
"instrument list": "Usage: eva instrument list [OPTIONS]\n\nOptions:\n --type [equity|index]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --cursor TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
49
|
-
"instrument search": "Usage: eva instrument search [OPTIONS]\n\nOptions:\n --query TEXT [required]\n --type [equity|index]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
50
|
-
"instrument resolve": "Usage: eva instrument resolve [OPTIONS]\n\nOptions:\n --query TEXT [required]\n --type [equity|index]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
50
|
+
"instrument list": "Usage: eva instrument list [OPTIONS]\n\nOptions:\n --type [equity|index|future|crypto]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --cursor TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
51
|
+
"instrument search": "Usage: eva instrument search [OPTIONS]\n\nOptions:\n --query TEXT [required]\n --type [equity|index|future|crypto]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
52
|
+
"instrument resolve": "Usage: eva instrument resolve [OPTIONS]\n\nOptions:\n --query TEXT [required]\n --type [equity|index|future|crypto]\n --venue TEXT\n --publisher TEXT\n --capability [quote|bars|constituents]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
51
53
|
"instrument show": "Usage: eva instrument show [OPTIONS]\n\nOptions:\n --id TEXT [required]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
52
54
|
stock: "Usage: eva stock [OPTIONS] COMMAND [ARGS]...\n\n Query mainland A-share data.\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n bars\n quotes\n",
|
|
53
55
|
index: "Usage: eva index [OPTIONS] COMMAND [ARGS]...\n\n Query SSE, SZSE, and CSI index data.\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n bars\n constituents\n quotes\n",
|
|
56
|
+
futures: "Usage: eva futures [OPTIONS] COMMAND [ARGS]...\n\n Query mainland China futures contracts.\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n bars\n quotes\n",
|
|
57
|
+
crypto: "Usage: eva crypto [OPTIONS] COMMAND [ARGS]...\n\n Query exchange-scoped cryptocurrency market data.\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n bars\n quotes\n",
|
|
54
58
|
"stock quotes": "Usage: eva stock quotes [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
55
59
|
"index quotes": "Usage: eva index quotes [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
60
|
+
"futures quotes": "Usage: eva futures quotes [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
61
|
+
"crypto quotes": "Usage: eva crypto quotes [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --venue [BINANCE|COINBASE] [required]\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
62
|
+
"stock bars": "Usage: eva stock bars [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --interval [1m|5m|15m|30m|60m|1d|1w|1mo] [default: 1d]\n --start TEXT\n --end TEXT\n --adjustment [none|forward|backward] [default: forward]\n --as-of TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
63
|
+
"index bars": "Usage: eva index bars [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --interval [1m|5m|15m|30m|60m|1d|1w|1mo] [default: 1d]\n --start TEXT\n --end TEXT\n --adjustment [none|forward|backward] [default: none]\n --as-of TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
64
|
+
"futures bars": "Usage: eva futures bars [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --interval [1m|5m|15m|30m|60m|1d|1w|1mo] [default: 1d]\n --start TEXT\n --end TEXT\n --adjustment [none|forward|backward] [default: none]\n --as-of TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
65
|
+
"crypto bars": "Usage: eva crypto bars [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --venue [BINANCE|COINBASE] [required]\n --interval [1m|5m|15m|30m|60m|1d|1w|1mo] [default: 1d]\n --start TEXT\n --end TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
58
66
|
"index constituents": "Usage: eva index constituents [OPTIONS]\n\nOptions:\n --symbol TEXT [required]\n --as-of TEXT\n --limit INTEGER\n --output PATH\n --format [json|jsonl|csv|parquet]\n --overwrite\n --timeout FLOAT [default: 120]\n --retries INTEGER [default: 2]\n --help\n",
|
|
59
67
|
};
|
|
60
68
|
|
|
@@ -75,7 +83,7 @@ const dataDefinitions = {
|
|
|
75
83
|
...requestDefinitions,
|
|
76
84
|
};
|
|
77
85
|
const discoveryDefinitions = {
|
|
78
|
-
type: { type: "choice", choices: ["equity", "index"] },
|
|
86
|
+
type: { type: "choice", choices: ["equity", "index", "future", "crypto"] },
|
|
79
87
|
venue: { type: "string" },
|
|
80
88
|
publisher: { type: "string" },
|
|
81
89
|
capability: { type: "choice", choices: ["quote", "bars", "constituents"] },
|
|
@@ -316,13 +324,15 @@ async function dispatch(context, command, subcommand, arguments_) {
|
|
|
316
324
|
process.stdout.write(`${dumps(response.data ?? {})}\n`);
|
|
317
325
|
return;
|
|
318
326
|
}
|
|
319
|
-
if (key === "stock quotes" || key === "index quotes") {
|
|
327
|
+
if (key === "stock quotes" || key === "index quotes" || key === "futures quotes" || key === "crypto quotes") {
|
|
320
328
|
const options = parseOptions(arguments_, {
|
|
321
|
-
symbol: { type: "string" },
|
|
322
|
-
|
|
323
|
-
|
|
329
|
+
symbol: { type: "string" },
|
|
330
|
+
...(command === "crypto" ? { venue: { type: "choice", choices: ["BINANCE", "COINBASE"] } } : {}),
|
|
331
|
+
...requestDefinitions,
|
|
332
|
+
}, command === "crypto" ? ["symbol", "venue"] : ["symbol"]);
|
|
333
|
+
const instrumentType = command === "stock" ? "equity" : command === "index" ? "index" : command === "futures" ? "future" : "crypto";
|
|
324
334
|
const http = client(context, options);
|
|
325
|
-
const instrumentId = await http.resolve(options.symbol, instrumentType, "quote");
|
|
335
|
+
const instrumentId = await http.resolve(options.symbol, instrumentType, "quote", options.venue);
|
|
326
336
|
const response = await http.request(
|
|
327
337
|
"GET",
|
|
328
338
|
`/v1/instruments/${encodeURIComponent(instrumentId)}/quote`,
|
|
@@ -330,25 +340,34 @@ async function dispatch(context, command, subcommand, arguments_) {
|
|
|
330
340
|
process.stdout.write(`${dumps(response.data ?? {})}\n`);
|
|
331
341
|
return;
|
|
332
342
|
}
|
|
333
|
-
if (key === "stock bars" || key === "index bars") {
|
|
343
|
+
if (key === "stock bars" || key === "index bars" || key === "futures bars" || key === "crypto bars") {
|
|
334
344
|
const options = parseOptions(arguments_, {
|
|
335
345
|
symbol: { type: "string" },
|
|
346
|
+
...(command === "crypto" ? { venue: { type: "choice", choices: ["BINANCE", "COINBASE"] } } : {}),
|
|
336
347
|
interval: { type: "choice", choices: INTERVALS, default: "1d" },
|
|
337
348
|
start: { type: "string" },
|
|
338
349
|
end: { type: "string" },
|
|
339
|
-
|
|
350
|
+
...(command === "crypto" ? {} : {
|
|
351
|
+
adjustment: {
|
|
352
|
+
type: "choice",
|
|
353
|
+
choices: ["none", "forward", "backward"],
|
|
354
|
+
default: command === "stock" ? "forward" : "none",
|
|
355
|
+
},
|
|
356
|
+
"as-of": { type: "string" },
|
|
357
|
+
}),
|
|
340
358
|
...dataDefinitions,
|
|
341
|
-
}, ["symbol"]);
|
|
342
|
-
const instrumentType = command === "stock" ? "equity" : "index";
|
|
359
|
+
}, command === "crypto" ? ["symbol", "venue"] : ["symbol"]);
|
|
360
|
+
const instrumentType = command === "stock" ? "equity" : command === "index" ? "index" : command === "futures" ? "future" : "crypto";
|
|
343
361
|
const http = client(context, options);
|
|
344
|
-
const instrumentId = await http.resolve(options.symbol, instrumentType, "bars");
|
|
362
|
+
const instrumentId = await http.resolve(options.symbol, instrumentType, "bars", options.venue);
|
|
345
363
|
const response = await http.request(
|
|
346
364
|
"GET",
|
|
347
365
|
`/v1/instruments/${encodeURIComponent(instrumentId)}/bars?${queryString({
|
|
348
366
|
interval: options.interval,
|
|
349
367
|
start: options.start,
|
|
350
368
|
end: options.end,
|
|
351
|
-
adjustment: options.adjustment,
|
|
369
|
+
adjustment: options.adjustment || "none",
|
|
370
|
+
as_of: options.asOf,
|
|
352
371
|
})}`,
|
|
353
372
|
);
|
|
354
373
|
emit(response.data ?? [], options);
|
|
@@ -374,7 +393,7 @@ async function dispatch(context, command, subcommand, arguments_) {
|
|
|
374
393
|
function commandParts(remaining) {
|
|
375
394
|
const command = remaining[0];
|
|
376
395
|
if (!command) return {};
|
|
377
|
-
if (!["config", "health", "version", "instrument", "stock", "index"].includes(command)) {
|
|
396
|
+
if (!["config", "health", "version", "instrument", "stock", "index", "futures", "crypto"].includes(command)) {
|
|
378
397
|
throw usage(`No such command '${command}'.`);
|
|
379
398
|
}
|
|
380
399
|
if (["health", "version"].includes(command)) {
|
|
@@ -389,6 +408,8 @@ function commandParts(remaining) {
|
|
|
389
408
|
instrument: ["list", "search", "resolve", "show"],
|
|
390
409
|
stock: ["quotes", "bars"],
|
|
391
410
|
index: ["quotes", "bars", "constituents"],
|
|
411
|
+
futures: ["quotes", "bars"],
|
|
412
|
+
crypto: ["quotes", "bars"],
|
|
392
413
|
}[command];
|
|
393
414
|
if (!valid.includes(subcommand)) throw usage(`No such command '${subcommand}'.`);
|
|
394
415
|
return { command, subcommand, arguments_: remaining.slice(2) };
|
package/lib/http-client.js
CHANGED
|
@@ -91,10 +91,20 @@ class EvaHttpClient {
|
|
|
91
91
|
throw new Error("HTTP retry loop exhausted");
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
async resolve(query, instrumentType, capability) {
|
|
94
|
+
async resolve(query, instrumentType, capability, venue) {
|
|
95
|
+
const canonicalParts = query.split(":", 4);
|
|
96
|
+
if (
|
|
97
|
+
canonicalParts.length === 4 &&
|
|
98
|
+
canonicalParts[1] === instrumentType &&
|
|
99
|
+
(!venue || canonicalParts[2].toUpperCase() === venue.toUpperCase())
|
|
100
|
+
) return query;
|
|
95
101
|
const response = await this.request("POST", "/v1/instrument-resolve", {
|
|
96
102
|
query,
|
|
97
|
-
context: {
|
|
103
|
+
context: {
|
|
104
|
+
instrument_type: instrumentType,
|
|
105
|
+
capability,
|
|
106
|
+
...(venue ? { venue } : {}),
|
|
107
|
+
},
|
|
98
108
|
});
|
|
99
109
|
const data = response.data || {};
|
|
100
110
|
const instrumentId = data.instrument && data.instrument.instrument_id;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evatick/cli",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "EVA Tick CLI for deterministic
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "EVA Tick CLI for deterministic A-share, Chinese index, mainland futures, and exchange-scoped crypto market data",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|