@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 CHANGED
@@ -1,7 +1,13 @@
1
- # EVA CLI
1
+ # EVA Tick CLI
2
2
 
3
- EVA CLI is a stable, machine-readable market-data command-line client for LLMs,
4
- AI agents, and automation. It connects to the versioned EVA HTTP API.
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
+ ![Codex queries financial data through EVA Tick](https://raw.githubusercontent.com/xiaochaohit/evatick-cli/main/docs/assets/eva-tick-codex-demo.gif)
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
- It can also be run without a global installation:
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 and has no runtime dependencies.
20
- Python and platform-specific native executables are not required.
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
- JSON, JSONL, and CSV exports are supported. Parquet export remains available in
23
- the Python distribution (`pip install 'evatick[parquet]'`).
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
- See the [EVA CLI repository](https://github.com/xiaochaohit/evatick-cli) for
26
- commands, configuration, source code, and license information.
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
- "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 --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",
57
- "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 --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",
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" }, ...requestDefinitions,
322
- }, ["symbol"]);
323
- const instrumentType = command === "stock" ? "equity" : "index";
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
- adjustment: { type: "choice", choices: ["none", "forward", "backward"], default: "none" },
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) };
@@ -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: { instrument_type: instrumentType, capability },
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.0",
4
- "description": "EVA market data command-line client for LLMs, agents, and automation",
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
  }