@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 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 starts with mainland China A-shares and indices published
76
- by the Shanghai Stock Exchange, Shenzhen Stock Exchange, and China Securities
77
- Index. Availability varies by deployed service and enabled providers.
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
- "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: 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" }, ...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,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
- adjustment: { type: "choice", choices: ["none", "forward", "backward"], default: "none" },
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) };
@@ -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.1",
4
- "description": "EVA Tick CLI for deterministic, machine-readable financial data queries by AI agents, LLMs, and automation",
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",