@evatick/cli 0.4.0 → 0.4.2
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 +83 -10
- package/lib/cli.js +36 -19
- package/lib/http-client.js +12 -2
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
# EVA CLI
|
|
1
|
+
# EVA Tick CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Deterministic, machine-readable financial data for AI agents, LLMs, and
|
|
4
|
+
automation.
|
|
5
|
+
|
|
6
|
+
EVA Tick gives agents canonical instrument IDs, stable JSON, structured errors,
|
|
7
|
+
and predictable commands instead of provider-specific identifiers and
|
|
8
|
+
functions.
|
|
9
|
+
|
|
10
|
+

|
|
5
11
|
|
|
6
12
|
## Install
|
|
7
13
|
|
|
@@ -10,17 +16,84 @@ npm install --global @evatick/cli
|
|
|
10
16
|
eva version
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
Run it without installing:
|
|
14
20
|
|
|
15
21
|
```shell
|
|
16
22
|
npx @evatick/cli health
|
|
17
23
|
```
|
|
18
24
|
|
|
19
|
-
The npm package is implemented in Node.js
|
|
20
|
-
Python
|
|
25
|
+
The npm package is implemented in Node.js, has no runtime dependencies, and
|
|
26
|
+
does not require Python or platform-specific native executables.
|
|
27
|
+
|
|
28
|
+
## First query
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
eva health
|
|
32
|
+
eva instrument search --query 平安银行 --type equity --limit 3
|
|
33
|
+
eva instrument resolve --query 000001 --type equity
|
|
34
|
+
eva stock bars --symbol cn:equity:XSHE:000001 \
|
|
35
|
+
--interval 1d \
|
|
36
|
+
--start 2026-08-18 \
|
|
37
|
+
--end 2026-08-21 \
|
|
38
|
+
--adjustment forward \
|
|
39
|
+
--limit 3
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Common commands
|
|
43
|
+
|
|
44
|
+
- `eva instrument search` finds canonical financial instruments by name, code,
|
|
45
|
+
or alias.
|
|
46
|
+
- `eva stock bars` queries normalized OHLCV bars.
|
|
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.
|
|
50
|
+
|
|
51
|
+
Run `eva COMMAND --help` or `eva GROUP COMMAND --help` for supported options.
|
|
52
|
+
|
|
53
|
+
## Hosted or self-hosted
|
|
54
|
+
|
|
55
|
+
The CLI connects to the hosted EVA Tick service at `https://api.evatick.com` by
|
|
56
|
+
default. Point it at a self-hosted [EVA Tick Server](https://github.com/xiaochaohit/evatick-server)
|
|
57
|
+
temporarily with an environment variable:
|
|
58
|
+
|
|
59
|
+
```shell
|
|
60
|
+
EVA_SERVER_URL=http://127.0.0.1:8765 eva health
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or save the service URL:
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
eva config set --base-url http://127.0.0.1:8765
|
|
67
|
+
eva config show
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
When a service requires authentication, use `EVA_API_KEY` or
|
|
71
|
+
`eva config set --api-key 'eva_...'`. The CLI sends a key only when configured,
|
|
72
|
+
and `eva config show` masks stored keys.
|
|
73
|
+
|
|
74
|
+
## Current availability
|
|
75
|
+
|
|
76
|
+
EVA Tick is designed as a provider-independent financial data layer. The
|
|
77
|
+
current public service covers mainland China A-shares; SSE, SZSE, and CSI
|
|
78
|
+
indices; CFFEX, SHFE, INE, and CZCE futures; and exchange-scoped Binance and
|
|
79
|
+
Coinbase cryptocurrency markets. Availability varies by deployed service and
|
|
80
|
+
enabled providers.
|
|
81
|
+
|
|
82
|
+
## Output and safety
|
|
83
|
+
|
|
84
|
+
- Successful results are compact JSON on stdout.
|
|
85
|
+
- Failures are structured JSON on stderr with stable error codes.
|
|
86
|
+
- JSON, JSONL, and CSV file exports are supported by the npm CLI.
|
|
87
|
+
- Existing files are never overwritten unless `--overwrite` is explicit.
|
|
88
|
+
- Parquet export is available in the Python distribution with
|
|
89
|
+
`pip install 'evatick[parquet]'`.
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
21
92
|
|
|
22
|
-
|
|
23
|
-
|
|
93
|
+
- [Complete CLI documentation and examples](https://github.com/xiaochaohit/evatick-cli#readme)
|
|
94
|
+
- [EVA Tick Skill for Codex](https://www.npmjs.com/package/@evatick/skill)
|
|
95
|
+
- [Self-hosted server and OpenAPI contract](https://github.com/xiaochaohit/evatick-server)
|
|
96
|
+
- [License and data-source notices](https://github.com/xiaochaohit/evatick-cli#license)
|
|
24
97
|
|
|
25
|
-
|
|
26
|
-
|
|
98
|
+
Market data is provided for research and reference only and does not constitute
|
|
99
|
+
investment advice.
|
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: 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",
|
|
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,30 @@ 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: { type: "choice", choices: ["none", "forward", "backward"], default: "none" },
|
|
352
|
+
"as-of": { type: "string" },
|
|
353
|
+
}),
|
|
340
354
|
...dataDefinitions,
|
|
341
|
-
}, ["symbol"]);
|
|
342
|
-
const instrumentType = command === "stock" ? "equity" : "index";
|
|
355
|
+
}, command === "crypto" ? ["symbol", "venue"] : ["symbol"]);
|
|
356
|
+
const instrumentType = command === "stock" ? "equity" : command === "index" ? "index" : command === "futures" ? "future" : "crypto";
|
|
343
357
|
const http = client(context, options);
|
|
344
|
-
const instrumentId = await http.resolve(options.symbol, instrumentType, "bars");
|
|
358
|
+
const instrumentId = await http.resolve(options.symbol, instrumentType, "bars", options.venue);
|
|
345
359
|
const response = await http.request(
|
|
346
360
|
"GET",
|
|
347
361
|
`/v1/instruments/${encodeURIComponent(instrumentId)}/bars?${queryString({
|
|
348
362
|
interval: options.interval,
|
|
349
363
|
start: options.start,
|
|
350
364
|
end: options.end,
|
|
351
|
-
adjustment: options.adjustment,
|
|
365
|
+
adjustment: options.adjustment || "none",
|
|
366
|
+
as_of: options.asOf,
|
|
352
367
|
})}`,
|
|
353
368
|
);
|
|
354
369
|
emit(response.data ?? [], options);
|
|
@@ -374,7 +389,7 @@ async function dispatch(context, command, subcommand, arguments_) {
|
|
|
374
389
|
function commandParts(remaining) {
|
|
375
390
|
const command = remaining[0];
|
|
376
391
|
if (!command) return {};
|
|
377
|
-
if (!["config", "health", "version", "instrument", "stock", "index"].includes(command)) {
|
|
392
|
+
if (!["config", "health", "version", "instrument", "stock", "index", "futures", "crypto"].includes(command)) {
|
|
378
393
|
throw usage(`No such command '${command}'.`);
|
|
379
394
|
}
|
|
380
395
|
if (["health", "version"].includes(command)) {
|
|
@@ -389,6 +404,8 @@ function commandParts(remaining) {
|
|
|
389
404
|
instrument: ["list", "search", "resolve", "show"],
|
|
390
405
|
stock: ["quotes", "bars"],
|
|
391
406
|
index: ["quotes", "bars", "constituents"],
|
|
407
|
+
futures: ["quotes", "bars"],
|
|
408
|
+
crypto: ["quotes", "bars"],
|
|
392
409
|
}[command];
|
|
393
410
|
if (!valid.includes(subcommand)) throw usage(`No such command '${subcommand}'.`);
|
|
394
411
|
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
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"description": "EVA Tick CLI for deterministic, machine-readable financial data queries by AI agents, LLMs, and automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -23,5 +23,19 @@
|
|
|
23
23
|
],
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=18"
|
|
26
|
-
}
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"eva-tick",
|
|
29
|
+
"financial-data",
|
|
30
|
+
"market-data",
|
|
31
|
+
"ai-agent",
|
|
32
|
+
"llm",
|
|
33
|
+
"codex",
|
|
34
|
+
"automation",
|
|
35
|
+
"quantitative-finance",
|
|
36
|
+
"openapi",
|
|
37
|
+
"akshare",
|
|
38
|
+
"a-share",
|
|
39
|
+
"china-stock-market"
|
|
40
|
+
]
|
|
27
41
|
}
|