toobit-trade-cli 1.0.1 → 1.0.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/dist/index.js CHANGED
@@ -167,7 +167,9 @@ var TOOBIT_CODE_BEHAVIORS = {
167
167
  "-2013": { retry: false, suggestion: "Order does not exist." },
168
168
  "-2014": { retry: false, suggestion: "Bad API key format." },
169
169
  "-2015": { retry: false, suggestion: "Invalid API key, IP, or permission." },
170
- "-2017": { retry: false, suggestion: "API key expired. Generate a new one." }
170
+ "-2017": { retry: false, suggestion: "API key expired. Generate a new one." },
171
+ "-1130": { retry: false, suggestion: "Invalid symbol format. Futures endpoints require BTC-SWAP-USDT format; spot endpoints use BTCUSDT." },
172
+ "-1107": { retry: false, suggestion: "API key is missing or malformed. Check X-BB-APIKEY header." }
171
173
  };
172
174
  function isDefined(value) {
173
175
  return value !== void 0 && value !== null;
@@ -285,34 +287,42 @@ var ToobitRestClient = class {
285
287
  `${config.method} ${config.path}`
286
288
  );
287
289
  }
288
- if (!response.ok) {
289
- throw new ToobitApiError(
290
- `HTTP ${response.status} from Toobit: ${parsed.msg ?? "Unknown error"}`,
291
- { code: String(response.status), endpoint: `${config.method} ${config.path}` }
292
- );
293
- }
294
290
  const responseCode = parsed.code;
295
- if (responseCode !== void 0 && responseCode !== 0) {
296
- const message = parsed.msg || "Toobit API request failed.";
297
- const endpoint = `${config.method} ${config.path}`;
291
+ const responseMsg = parsed.msg || void 0;
292
+ const endpoint = `${config.method} ${config.path}`;
293
+ const hasBusinessCode = responseCode !== void 0 && responseCode !== 0;
294
+ if (hasBusinessCode) {
298
295
  const codeStr = String(responseCode);
299
- if (codeStr === "-1002" || codeStr === "-1022" || codeStr === "-2014" || codeStr === "-2015") {
296
+ const message = responseMsg || "Toobit API request failed.";
297
+ const behavior = TOOBIT_CODE_BEHAVIORS[codeStr];
298
+ if (codeStr === "-1002" || codeStr === "-1022" || codeStr === "-1107" || codeStr === "-2014" || codeStr === "-2015" || codeStr === "-2017") {
300
299
  throw new AuthenticationError(
301
300
  message,
302
- "Check API key, secret key and permissions.",
301
+ behavior?.suggestion ?? "Check API key, secret key and permissions.",
303
302
  endpoint
304
303
  );
305
304
  }
306
305
  if (codeStr === "-1003") {
307
306
  throw new RateLimitError(message, "Too many requests. Back off.", endpoint);
308
307
  }
309
- const behavior = TOOBIT_CODE_BEHAVIORS[codeStr];
310
308
  throw new ToobitApiError(message, {
311
309
  code: codeStr,
312
310
  endpoint,
313
311
  suggestion: behavior?.suggestion
314
312
  });
315
313
  }
314
+ if (!response.ok) {
315
+ const rawMsg = responseMsg ?? "Unknown error";
316
+ let suggestion;
317
+ if (/symbol|paramter|parameter/i.test(rawMsg)) {
318
+ const isFuturesPath = /futures|fundingRate|openInterest|markPrice|contract|longShort|insurance|riskLimit/i.test(config.path);
319
+ suggestion = isFuturesPath ? "Futures endpoints require contract symbol format, e.g. BTC-SWAP-USDT instead of BTCUSDT." : "Spot endpoints require symbol format like BTCUSDT.";
320
+ }
321
+ throw new ToobitApiError(
322
+ `HTTP ${response.status} from Toobit: ${rawMsg}`,
323
+ { code: String(response.status), endpoint, suggestion }
324
+ );
325
+ }
316
326
  return {
317
327
  endpoint: `${config.method} ${config.path}`,
318
328
  requestTime: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1298,6 +1308,13 @@ function registerFuturesTools() {
1298
1308
  }
1299
1309
  ];
1300
1310
  }
1311
+ function readPositiveInt(args, key) {
1312
+ const value = readNumber(args, key);
1313
+ if (value !== void 0 && value < 1) {
1314
+ throw new ValidationError(`Parameter "${key}" must be a positive integer.`);
1315
+ }
1316
+ return value;
1317
+ }
1301
1318
  function normalize3(response) {
1302
1319
  return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
1303
1320
  }
@@ -1342,7 +1359,7 @@ function registerMarketTools() {
1342
1359
  const args = asRecord(rawArgs);
1343
1360
  const response = await context.client.publicGet(
1344
1361
  "/quote/v1/depth",
1345
- compactObject({ symbol: requireString(args, "symbol"), limit: readNumber(args, "limit") }),
1362
+ compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
1346
1363
  publicRateLimit("market_get_depth", 20)
1347
1364
  );
1348
1365
  return normalize3(response);
@@ -1366,7 +1383,7 @@ function registerMarketTools() {
1366
1383
  const args = asRecord(rawArgs);
1367
1384
  const response = await context.client.publicGet(
1368
1385
  "/quote/v1/depth/merged",
1369
- compactObject({ symbol: requireString(args, "symbol"), scale: readNumber(args, "scale"), limit: readNumber(args, "limit") }),
1386
+ compactObject({ symbol: requireString(args, "symbol"), scale: readNumber(args, "scale"), limit: readPositiveInt(args, "limit") }),
1370
1387
  publicRateLimit("market_get_merged_depth", 20)
1371
1388
  );
1372
1389
  return normalize3(response);
@@ -1389,7 +1406,7 @@ function registerMarketTools() {
1389
1406
  const args = asRecord(rawArgs);
1390
1407
  const response = await context.client.publicGet(
1391
1408
  "/quote/v1/trades",
1392
- compactObject({ symbol: requireString(args, "symbol"), limit: readNumber(args, "limit") }),
1409
+ compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
1393
1410
  publicRateLimit("market_get_trades", 20)
1394
1411
  );
1395
1412
  return normalize3(response);
@@ -1420,7 +1437,7 @@ function registerMarketTools() {
1420
1437
  interval: requireString(args, "interval"),
1421
1438
  startTime: readNumber(args, "startTime"),
1422
1439
  endTime: readNumber(args, "endTime"),
1423
- limit: readNumber(args, "limit")
1440
+ limit: readPositiveInt(args, "limit")
1424
1441
  }),
1425
1442
  publicRateLimit("market_get_klines", 20)
1426
1443
  );
@@ -1430,12 +1447,12 @@ function registerMarketTools() {
1430
1447
  {
1431
1448
  name: "market_get_ticker_24hr",
1432
1449
  module: "market",
1433
- description: "Get 24h price change statistics for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
1450
+ description: "Get 24h price change statistics for a spot symbol. Omitting symbol returns ALL symbols (900+). Always specify a symbol unless you need the full list. Public endpoint. Rate limit: 20 req/s.",
1434
1451
  isWrite: false,
1435
1452
  inputSchema: {
1436
1453
  type: "object",
1437
1454
  properties: {
1438
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
1455
+ symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
1439
1456
  }
1440
1457
  },
1441
1458
  handler: async (rawArgs, context) => {
@@ -1451,12 +1468,12 @@ function registerMarketTools() {
1451
1468
  {
1452
1469
  name: "market_get_ticker_price",
1453
1470
  module: "market",
1454
- description: "Get latest price for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
1471
+ description: "Get latest price for a spot symbol. Omitting symbol returns ALL symbols (900+), which may consume many tokens. Always specify a symbol unless you need the full list. Public endpoint. Rate limit: 20 req/s.",
1455
1472
  isWrite: false,
1456
1473
  inputSchema: {
1457
1474
  type: "object",
1458
1475
  properties: {
1459
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
1476
+ symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
1460
1477
  }
1461
1478
  },
1462
1479
  handler: async (rawArgs, context) => {
@@ -1472,12 +1489,12 @@ function registerMarketTools() {
1472
1489
  {
1473
1490
  name: "market_get_book_ticker",
1474
1491
  module: "market",
1475
- description: "Get best bid/ask price for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
1492
+ description: "Get best bid/ask price for a spot symbol. Omitting symbol returns ALL symbols. Always specify a symbol unless you need the full list. Public endpoint. Rate limit: 20 req/s.",
1476
1493
  isWrite: false,
1477
1494
  inputSchema: {
1478
1495
  type: "object",
1479
1496
  properties: {
1480
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
1497
+ symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
1481
1498
  }
1482
1499
  },
1483
1500
  handler: async (rawArgs, context) => {
@@ -1515,7 +1532,7 @@ function registerMarketTools() {
1515
1532
  interval: requireString(args, "interval"),
1516
1533
  startTime: readNumber(args, "startTime"),
1517
1534
  endTime: readNumber(args, "endTime"),
1518
- limit: readNumber(args, "limit")
1535
+ limit: readPositiveInt(args, "limit")
1519
1536
  }),
1520
1537
  publicRateLimit("market_get_index_klines", 20)
1521
1538
  );
@@ -1525,12 +1542,12 @@ function registerMarketTools() {
1525
1542
  {
1526
1543
  name: "market_get_mark_price",
1527
1544
  module: "market",
1528
- description: "Get latest mark price for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
1545
+ description: "Get latest mark price for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1529
1546
  isWrite: false,
1530
1547
  inputSchema: {
1531
1548
  type: "object",
1532
1549
  properties: {
1533
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
1550
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
1534
1551
  }
1535
1552
  },
1536
1553
  handler: async (rawArgs, context) => {
@@ -1546,12 +1563,12 @@ function registerMarketTools() {
1546
1563
  {
1547
1564
  name: "market_get_mark_price_klines",
1548
1565
  module: "market",
1549
- description: "Get mark price K-line data. Public endpoint. Rate limit: 20 req/s.",
1566
+ description: "Get mark price K-line data. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1550
1567
  isWrite: false,
1551
1568
  inputSchema: {
1552
1569
  type: "object",
1553
1570
  properties: {
1554
- symbol: { type: "string", description: "e.g. BTCUSDT" },
1571
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
1555
1572
  interval: { type: "string", enum: [...TOOBIT_CANDLE_BARS] },
1556
1573
  startTime: { type: "number" },
1557
1574
  endTime: { type: "number" },
@@ -1568,7 +1585,7 @@ function registerMarketTools() {
1568
1585
  interval: requireString(args, "interval"),
1569
1586
  startTime: readNumber(args, "startTime"),
1570
1587
  endTime: readNumber(args, "endTime"),
1571
- limit: readNumber(args, "limit")
1588
+ limit: readPositiveInt(args, "limit")
1572
1589
  }),
1573
1590
  publicRateLimit("market_get_mark_price_klines", 20)
1574
1591
  );
@@ -1578,12 +1595,12 @@ function registerMarketTools() {
1578
1595
  {
1579
1596
  name: "market_get_funding_rate",
1580
1597
  module: "market",
1581
- description: "Get current funding rate for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
1598
+ description: "Get current funding rate for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1582
1599
  isWrite: false,
1583
1600
  inputSchema: {
1584
1601
  type: "object",
1585
1602
  properties: {
1586
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
1603
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
1587
1604
  }
1588
1605
  },
1589
1606
  handler: async (rawArgs, context) => {
@@ -1599,12 +1616,12 @@ function registerMarketTools() {
1599
1616
  {
1600
1617
  name: "market_get_funding_rate_history",
1601
1618
  module: "market",
1602
- description: "Get historical funding rates for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
1619
+ description: "Get historical funding rates for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1603
1620
  isWrite: false,
1604
1621
  inputSchema: {
1605
1622
  type: "object",
1606
1623
  properties: {
1607
- symbol: { type: "string", description: "e.g. BTCUSDT" },
1624
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
1608
1625
  startTime: { type: "number" },
1609
1626
  endTime: { type: "number" },
1610
1627
  limit: { type: "number", description: "Default 100, max 1000" }
@@ -1619,7 +1636,7 @@ function registerMarketTools() {
1619
1636
  symbol: requireString(args, "symbol"),
1620
1637
  startTime: readNumber(args, "startTime"),
1621
1638
  endTime: readNumber(args, "endTime"),
1622
- limit: readNumber(args, "limit")
1639
+ limit: readPositiveInt(args, "limit")
1623
1640
  }),
1624
1641
  publicRateLimit("market_get_funding_rate_history", 20)
1625
1642
  );
@@ -1629,12 +1646,12 @@ function registerMarketTools() {
1629
1646
  {
1630
1647
  name: "market_get_open_interest",
1631
1648
  module: "market",
1632
- description: "Get total open interest for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
1649
+ description: "Get total open interest for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1633
1650
  isWrite: false,
1634
1651
  inputSchema: {
1635
1652
  type: "object",
1636
1653
  properties: {
1637
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
1654
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
1638
1655
  }
1639
1656
  },
1640
1657
  handler: async (rawArgs, context) => {
@@ -1655,7 +1672,7 @@ function registerMarketTools() {
1655
1672
  inputSchema: {
1656
1673
  type: "object",
1657
1674
  properties: {
1658
- symbol: { type: "string", description: "e.g. BTCUSDT" },
1675
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
1659
1676
  period: { type: "string", description: "e.g. 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d" },
1660
1677
  startTime: { type: "number" },
1661
1678
  endTime: { type: "number" },
@@ -1672,7 +1689,7 @@ function registerMarketTools() {
1672
1689
  period: requireString(args, "period"),
1673
1690
  startTime: readNumber(args, "startTime"),
1674
1691
  endTime: readNumber(args, "endTime"),
1675
- limit: readNumber(args, "limit")
1692
+ limit: readPositiveInt(args, "limit")
1676
1693
  }),
1677
1694
  publicRateLimit("market_get_long_short_ratio", 20)
1678
1695
  );
@@ -1682,12 +1699,12 @@ function registerMarketTools() {
1682
1699
  {
1683
1700
  name: "market_get_contract_ticker_24hr",
1684
1701
  module: "market",
1685
- description: "Get 24h price change statistics for a futures contract. Public endpoint. Rate limit: 20 req/s.",
1702
+ description: "Get 24h price change statistics for a futures contract. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1686
1703
  isWrite: false,
1687
1704
  inputSchema: {
1688
1705
  type: "object",
1689
1706
  properties: {
1690
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
1707
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
1691
1708
  }
1692
1709
  },
1693
1710
  handler: async (rawArgs, context) => {
@@ -1703,12 +1720,12 @@ function registerMarketTools() {
1703
1720
  {
1704
1721
  name: "market_get_contract_ticker_price",
1705
1722
  module: "market",
1706
- description: "Get latest price for a futures contract. Public endpoint. Rate limit: 20 req/s.",
1723
+ description: "Get latest price for a futures contract. Supports both spot (BTCUSDT) and contract (BTC-SWAP-USDT) symbol formats. Public endpoint. Rate limit: 20 req/s.",
1707
1724
  isWrite: false,
1708
1725
  inputSchema: {
1709
1726
  type: "object",
1710
1727
  properties: {
1711
- symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
1728
+ symbol: { type: "string", description: "e.g. BTC-SWAP-USDT or BTCUSDT. Omit for all." }
1712
1729
  }
1713
1730
  },
1714
1731
  handler: async (rawArgs, context) => {
@@ -1745,12 +1762,12 @@ function registerMarketTools() {
1745
1762
  {
1746
1763
  name: "market_get_insurance_fund",
1747
1764
  module: "market",
1748
- description: "Get insurance fund balance for a symbol. Public endpoint. Rate limit: 20 req/s.",
1765
+ description: "Get insurance fund balance for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1749
1766
  isWrite: false,
1750
1767
  inputSchema: {
1751
1768
  type: "object",
1752
1769
  properties: {
1753
- symbol: { type: "string", description: "e.g. BTCUSDT" }
1770
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
1754
1771
  },
1755
1772
  required: ["symbol"]
1756
1773
  },
@@ -1767,12 +1784,12 @@ function registerMarketTools() {
1767
1784
  {
1768
1785
  name: "market_get_risk_limits",
1769
1786
  module: "market",
1770
- description: "Get risk limits configuration for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
1787
+ description: "Get risk limits configuration for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
1771
1788
  isWrite: false,
1772
1789
  inputSchema: {
1773
1790
  type: "object",
1774
1791
  properties: {
1775
- symbol: { type: "string", description: "e.g. BTCUSDT" }
1792
+ symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
1776
1793
  },
1777
1794
  required: ["symbol"]
1778
1795
  },
@@ -2078,12 +2095,15 @@ function allToolSpecs() {
2078
2095
  ];
2079
2096
  }
2080
2097
  function createToolRunner(client, config) {
2081
- const fullConfig = { ...config, modules: [...MODULES], readOnly: false };
2098
+ const fullConfig = { ...config, modules: [...MODULES] };
2082
2099
  const tools = allToolSpecs();
2083
2100
  const toolMap = new Map(tools.map((t) => [t.name, t]));
2084
2101
  return async (toolName, args) => {
2085
2102
  const tool = toolMap.get(toolName);
2086
2103
  if (!tool) throw new Error(`Unknown tool: ${toolName}`);
2104
+ if (config.readOnly && tool.isWrite) {
2105
+ throw new Error(`Tool "${toolName}" is a write operation and is blocked in read-only mode.`);
2106
+ }
2087
2107
  const result = await tool.handler(args, { config: fullConfig, client });
2088
2108
  return result;
2089
2109
  };
@@ -2264,11 +2284,58 @@ function parseCli(argv = process.argv.slice(2)) {
2264
2284
  }
2265
2285
 
2266
2286
  // src/formatter.ts
2287
+ function extractRows(value) {
2288
+ if (Array.isArray(value)) return value;
2289
+ if (typeof value === "object" && value !== null) {
2290
+ const entries = Object.values(value);
2291
+ for (const v of entries) {
2292
+ if (Array.isArray(v)) return v;
2293
+ }
2294
+ }
2295
+ return null;
2296
+ }
2267
2297
  function formatJson(data, json) {
2268
2298
  if (json) return JSON.stringify(data, null, 2);
2269
2299
  if (data === null || data === void 0) return "No data.";
2270
2300
  if (typeof data !== "object") return String(data);
2271
- return JSON.stringify(data, null, 2);
2301
+ const record = data;
2302
+ const inner = record.data ?? record;
2303
+ const rows = extractRows(inner);
2304
+ if (rows) {
2305
+ if (rows.length === 0) return "No data.";
2306
+ if (typeof rows[0] === "object" && rows[0] !== null) {
2307
+ return formatTable(rows);
2308
+ }
2309
+ return rows.map(String).join("\n");
2310
+ }
2311
+ if (typeof inner === "object" && inner !== null) {
2312
+ return formatKv(inner);
2313
+ }
2314
+ return String(inner);
2315
+ }
2316
+ function formatKv(data) {
2317
+ const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null);
2318
+ if (entries.length === 0) return "No data.";
2319
+ const maxKey = Math.max(...entries.map(([k]) => k.length));
2320
+ return entries.map(([k, v]) => {
2321
+ if (typeof v === "object") return `${k.padEnd(maxKey)} ${JSON.stringify(v)}`;
2322
+ return `${k.padEnd(maxKey)} ${String(v)}`;
2323
+ }).join("\n");
2324
+ }
2325
+ function formatTable(rows, columns) {
2326
+ if (rows.length === 0) return "No data.";
2327
+ const keys = columns ?? Object.keys(rows[0]);
2328
+ const widths = keys.map(
2329
+ (k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
2330
+ );
2331
+ const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
2332
+ const separator = widths.map((w) => "-".repeat(w)).join(" ");
2333
+ const body = rows.map(
2334
+ (row) => keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" ")
2335
+ ).join("\n");
2336
+ return `${header}
2337
+ ${separator}
2338
+ ${body}`;
2272
2339
  }
2273
2340
 
2274
2341
  // src/commands/market.ts
@@ -2331,9 +2398,18 @@ async function handleMarketCommand(cli, run) {
2331
2398
  case "long-short-ratio":
2332
2399
  result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
2333
2400
  break;
2401
+ case "contract-ticker-price":
2402
+ result = await run("market_get_contract_ticker_price", { symbol: f.symbol });
2403
+ break;
2404
+ case "insurance-fund":
2405
+ result = await run("market_get_insurance_fund", { symbol: f.symbol });
2406
+ break;
2407
+ case "risk-limits":
2408
+ result = await run("market_get_risk_limits", { symbol: f.symbol });
2409
+ break;
2334
2410
  default:
2335
2411
  process.stdout.write(`Unknown market subcommand: ${cli.subcommand}
2336
- Available: time, info, ticker, ticker-24hr, depth, trades, klines, candles, book-ticker, mark-price, funding-rate, funding-rate-history, open-interest, index, contract-ticker, long-short-ratio
2412
+ Available: time, info, ticker, ticker-24hr, depth, trades, klines, candles, book-ticker, mark-price, funding-rate, funding-rate-history, open-interest, index, contract-ticker, contract-ticker-price, long-short-ratio, insurance-fund, risk-limits
2337
2413
  `);
2338
2414
  return;
2339
2415
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toobit-trade-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "toobit": "dist/index.js"
@@ -62,8 +62,17 @@ export async function handleMarketCommand(cli: CliParsed, run: ToolRunner): Prom
62
62
  case "long-short-ratio":
63
63
  result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
64
64
  break;
65
+ case "contract-ticker-price":
66
+ result = await run("market_get_contract_ticker_price", { symbol: f.symbol });
67
+ break;
68
+ case "insurance-fund":
69
+ result = await run("market_get_insurance_fund", { symbol: f.symbol });
70
+ break;
71
+ case "risk-limits":
72
+ result = await run("market_get_risk_limits", { symbol: f.symbol });
73
+ break;
65
74
  default:
66
- process.stdout.write(`Unknown market subcommand: ${cli.subcommand}\nAvailable: time, info, ticker, ticker-24hr, depth, trades, klines, candles, book-ticker, mark-price, funding-rate, funding-rate-history, open-interest, index, contract-ticker, long-short-ratio\n`);
75
+ process.stdout.write(`Unknown market subcommand: ${cli.subcommand}\nAvailable: time, info, ticker, ticker-24hr, depth, trades, klines, candles, book-ticker, mark-price, funding-rate, funding-rate-history, open-interest, index, contract-ticker, contract-ticker-price, long-short-ratio, insurance-fund, risk-limits\n`);
67
76
  return;
68
77
  }
69
78
 
package/src/formatter.ts CHANGED
@@ -1,8 +1,46 @@
1
+ function extractRows(value: unknown): unknown[] | null {
2
+ if (Array.isArray(value)) return value;
3
+ if (typeof value === "object" && value !== null) {
4
+ const entries = Object.values(value as Record<string, unknown>);
5
+ for (const v of entries) {
6
+ if (Array.isArray(v)) return v;
7
+ }
8
+ }
9
+ return null;
10
+ }
11
+
1
12
  export function formatJson(data: unknown, json: boolean): string {
2
13
  if (json) return JSON.stringify(data, null, 2);
3
14
  if (data === null || data === undefined) return "No data.";
4
15
  if (typeof data !== "object") return String(data);
5
- return JSON.stringify(data, null, 2);
16
+
17
+ const record = data as Record<string, unknown>;
18
+ const inner = record.data ?? record;
19
+
20
+ const rows = extractRows(inner);
21
+ if (rows) {
22
+ if (rows.length === 0) return "No data.";
23
+ if (typeof rows[0] === "object" && rows[0] !== null) {
24
+ return formatTable(rows as Record<string, unknown>[]);
25
+ }
26
+ return rows.map(String).join("\n");
27
+ }
28
+
29
+ if (typeof inner === "object" && inner !== null) {
30
+ return formatKv(inner as Record<string, unknown>);
31
+ }
32
+
33
+ return String(inner);
34
+ }
35
+
36
+ export function formatKv(data: Record<string, unknown>): string {
37
+ const entries = Object.entries(data).filter(([, v]) => v !== undefined && v !== null);
38
+ if (entries.length === 0) return "No data.";
39
+ const maxKey = Math.max(...entries.map(([k]) => k.length));
40
+ return entries.map(([k, v]) => {
41
+ if (typeof v === "object") return `${k.padEnd(maxKey)} ${JSON.stringify(v)}`;
42
+ return `${k.padEnd(maxKey)} ${String(v)}`;
43
+ }).join("\n");
6
44
  }
7
45
 
8
46
  export function formatTable(rows: Record<string, unknown>[], columns?: string[]): string {