toobit-trade-cli 1.0.1 → 1.0.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/dist/index.js +163 -78
- package/package.json +1 -1
- package/src/commands/market.ts +10 -1
- package/src/formatter.ts +39 -1
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
291
|
+
const responseMsg = parsed.msg || void 0;
|
|
292
|
+
const endpoint = `${config.method} ${config.path}`;
|
|
293
|
+
const hasBusinessCode = responseCode !== void 0 && responseCode !== 0 && responseCode !== 200;
|
|
294
|
+
if (hasBusinessCode) {
|
|
298
295
|
const codeStr = String(responseCode);
|
|
299
|
-
|
|
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(),
|
|
@@ -459,24 +469,26 @@ function registerAccountTools() {
|
|
|
459
469
|
inputSchema: {
|
|
460
470
|
type: "object",
|
|
461
471
|
properties: {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
472
|
+
fromUid: { type: "number", description: "From user ID" },
|
|
473
|
+
toUid: { type: "number", description: "To user ID" },
|
|
474
|
+
fromAccountType: { type: "string", enum: ["MAIN", "FUTURES", "COPY_TRADING"], description: "MAIN=spot, FUTURES=contract, COPY_TRADING=copy trading" },
|
|
475
|
+
toAccountType: { type: "string", enum: ["MAIN", "FUTURES", "COPY_TRADING"], description: "MAIN=spot, FUTURES=contract, COPY_TRADING=copy trading" },
|
|
476
|
+
asset: { type: "string", description: "Asset name, e.g. USDT" },
|
|
477
|
+
quantity: { type: "string", description: "Transfer quantity" }
|
|
467
478
|
},
|
|
468
|
-
required: ["
|
|
479
|
+
required: ["fromUid", "toUid", "fromAccountType", "toAccountType", "asset", "quantity"]
|
|
469
480
|
},
|
|
470
481
|
handler: async (rawArgs, context) => {
|
|
471
482
|
const args = asRecord(rawArgs);
|
|
472
483
|
const response = await context.client.privatePost(
|
|
473
484
|
"/api/v1/subAccount/transfer",
|
|
474
485
|
compactObject({
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
486
|
+
fromUid: readNumber(args, "fromUid"),
|
|
487
|
+
toUid: readNumber(args, "toUid"),
|
|
488
|
+
fromAccountType: requireString(args, "fromAccountType"),
|
|
489
|
+
toAccountType: requireString(args, "toAccountType"),
|
|
490
|
+
asset: requireString(args, "asset"),
|
|
491
|
+
quantity: requireString(args, "quantity")
|
|
480
492
|
}),
|
|
481
493
|
privateRateLimit("account_sub_transfer", 5)
|
|
482
494
|
);
|
|
@@ -571,18 +583,18 @@ function registerAccountTools() {
|
|
|
571
583
|
inputSchema: {
|
|
572
584
|
type: "object",
|
|
573
585
|
properties: {
|
|
574
|
-
|
|
575
|
-
chainType: { type: "string" }
|
|
586
|
+
coin: { type: "string", description: "Asset name, e.g. USDT" },
|
|
587
|
+
chainType: { type: "string", description: "Chain type, e.g. ERC20, TRC20, OMNI" }
|
|
576
588
|
},
|
|
577
|
-
required: ["
|
|
589
|
+
required: ["coin", "chainType"]
|
|
578
590
|
},
|
|
579
591
|
handler: async (rawArgs, context) => {
|
|
580
592
|
const args = asRecord(rawArgs);
|
|
581
593
|
const response = await context.client.privateGet(
|
|
582
594
|
"/api/v1/account/deposit/address",
|
|
583
595
|
compactObject({
|
|
584
|
-
|
|
585
|
-
chainType:
|
|
596
|
+
coin: requireString(args, "coin"),
|
|
597
|
+
chainType: requireString(args, "chainType")
|
|
586
598
|
}),
|
|
587
599
|
privateRateLimit("account_get_deposit_address", 20)
|
|
588
600
|
);
|
|
@@ -665,38 +677,45 @@ function registerFuturesTools() {
|
|
|
665
677
|
{
|
|
666
678
|
name: "futures_place_order",
|
|
667
679
|
module: "futures",
|
|
668
|
-
description: "Place a USDT-M futures order. [CAUTION] Executes real trades. Private endpoint. Rate limit: 20 req/s.",
|
|
680
|
+
description: "Place a USDT-M futures order. For market orders, set orderType to MARKET \u2014 the handler automatically converts to type=LIMIT + priceType=MARKET as required by Toobit API. [CAUTION] Executes real trades. Private endpoint. Rate limit: 20 req/s.",
|
|
669
681
|
isWrite: true,
|
|
670
682
|
inputSchema: {
|
|
671
683
|
type: "object",
|
|
672
684
|
properties: {
|
|
673
|
-
symbol: { type: "string", description: "e.g.
|
|
685
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
674
686
|
side: { type: "string", enum: ["BUY_OPEN", "SELL_OPEN", "BUY_CLOSE", "SELL_CLOSE"] },
|
|
675
|
-
orderType: { type: "string", enum: ["LIMIT", "MARKET"], description: "
|
|
687
|
+
orderType: { type: "string", enum: ["LIMIT", "MARKET", "STOP"], description: "LIMIT=limit order, MARKET=market order (auto-converted), STOP=conditional order" },
|
|
676
688
|
quantity: { type: "string", description: "Order quantity (contracts)" },
|
|
677
|
-
price: { type: "string", description: "Required for LIMIT" },
|
|
689
|
+
price: { type: "string", description: "Required for LIMIT orders with priceType=INPUT" },
|
|
678
690
|
leverage: { type: "string", description: "Leverage, e.g. 10" },
|
|
679
|
-
|
|
680
|
-
priceType: { type: "string", enum: ["INPUT", "OPPONENT", "QUEUE", "OVER", "MARKET"], description: "Price type
|
|
681
|
-
|
|
682
|
-
timeInForce: { type: "string", enum: ["GTC", "IOC", "FOK"
|
|
691
|
+
newClientOrderId: { type: "string", description: "Unique client order ID. Auto-generated if omitted." },
|
|
692
|
+
priceType: { type: "string", enum: ["INPUT", "OPPONENT", "QUEUE", "OVER", "MARKET"], description: "Price type. INPUT=specified price, MARKET=market price" },
|
|
693
|
+
stopPrice: { type: "string", description: "Trigger price for STOP orders" },
|
|
694
|
+
timeInForce: { type: "string", enum: ["GTC", "IOC", "FOK"] }
|
|
683
695
|
},
|
|
684
696
|
required: ["symbol", "side", "orderType", "quantity"]
|
|
685
697
|
},
|
|
686
698
|
handler: async (rawArgs, context) => {
|
|
687
699
|
const args = asRecord(rawArgs);
|
|
700
|
+
let orderType = requireString(args, "orderType");
|
|
701
|
+
let priceType = readString(args, "priceType");
|
|
702
|
+
if (orderType === "MARKET") {
|
|
703
|
+
orderType = "LIMIT";
|
|
704
|
+
priceType = "MARKET";
|
|
705
|
+
}
|
|
706
|
+
const clientId = readString(args, "newClientOrderId") ?? `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
688
707
|
const response = await context.client.privatePost(
|
|
689
708
|
"/api/v1/futures/order",
|
|
690
709
|
compactObject({
|
|
691
710
|
symbol: requireString(args, "symbol"),
|
|
692
711
|
side: requireString(args, "side"),
|
|
693
|
-
|
|
712
|
+
type: orderType,
|
|
694
713
|
quantity: requireString(args, "quantity"),
|
|
695
714
|
price: readString(args, "price"),
|
|
696
715
|
leverage: readString(args, "leverage"),
|
|
697
|
-
|
|
698
|
-
priceType
|
|
699
|
-
|
|
716
|
+
newClientOrderId: clientId,
|
|
717
|
+
priceType,
|
|
718
|
+
stopPrice: readString(args, "stopPrice"),
|
|
700
719
|
timeInForce: readString(args, "timeInForce")
|
|
701
720
|
}),
|
|
702
721
|
privateRateLimit("futures_place_order", 20)
|
|
@@ -1027,7 +1046,7 @@ function registerFuturesTools() {
|
|
|
1027
1046
|
type: "object",
|
|
1028
1047
|
properties: {
|
|
1029
1048
|
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1030
|
-
marginType: { type: "string", enum: ["
|
|
1049
|
+
marginType: { type: "string", enum: ["CROSS", "ISOLATED"], description: "CROSS=cross margin, ISOLATED=isolated margin" }
|
|
1031
1050
|
},
|
|
1032
1051
|
required: ["symbol", "marginType"]
|
|
1033
1052
|
},
|
|
@@ -1298,6 +1317,13 @@ function registerFuturesTools() {
|
|
|
1298
1317
|
}
|
|
1299
1318
|
];
|
|
1300
1319
|
}
|
|
1320
|
+
function readPositiveInt(args, key) {
|
|
1321
|
+
const value = readNumber(args, key);
|
|
1322
|
+
if (value !== void 0 && value < 1) {
|
|
1323
|
+
throw new ValidationError(`Parameter "${key}" must be a positive integer.`);
|
|
1324
|
+
}
|
|
1325
|
+
return value;
|
|
1326
|
+
}
|
|
1301
1327
|
function normalize3(response) {
|
|
1302
1328
|
return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
|
|
1303
1329
|
}
|
|
@@ -1342,7 +1368,7 @@ function registerMarketTools() {
|
|
|
1342
1368
|
const args = asRecord(rawArgs);
|
|
1343
1369
|
const response = await context.client.publicGet(
|
|
1344
1370
|
"/quote/v1/depth",
|
|
1345
|
-
compactObject({ symbol: requireString(args, "symbol"), limit:
|
|
1371
|
+
compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
|
|
1346
1372
|
publicRateLimit("market_get_depth", 20)
|
|
1347
1373
|
);
|
|
1348
1374
|
return normalize3(response);
|
|
@@ -1366,7 +1392,7 @@ function registerMarketTools() {
|
|
|
1366
1392
|
const args = asRecord(rawArgs);
|
|
1367
1393
|
const response = await context.client.publicGet(
|
|
1368
1394
|
"/quote/v1/depth/merged",
|
|
1369
|
-
compactObject({ symbol: requireString(args, "symbol"), scale: readNumber(args, "scale"), limit:
|
|
1395
|
+
compactObject({ symbol: requireString(args, "symbol"), scale: readNumber(args, "scale"), limit: readPositiveInt(args, "limit") }),
|
|
1370
1396
|
publicRateLimit("market_get_merged_depth", 20)
|
|
1371
1397
|
);
|
|
1372
1398
|
return normalize3(response);
|
|
@@ -1389,7 +1415,7 @@ function registerMarketTools() {
|
|
|
1389
1415
|
const args = asRecord(rawArgs);
|
|
1390
1416
|
const response = await context.client.publicGet(
|
|
1391
1417
|
"/quote/v1/trades",
|
|
1392
|
-
compactObject({ symbol: requireString(args, "symbol"), limit:
|
|
1418
|
+
compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
|
|
1393
1419
|
publicRateLimit("market_get_trades", 20)
|
|
1394
1420
|
);
|
|
1395
1421
|
return normalize3(response);
|
|
@@ -1420,7 +1446,7 @@ function registerMarketTools() {
|
|
|
1420
1446
|
interval: requireString(args, "interval"),
|
|
1421
1447
|
startTime: readNumber(args, "startTime"),
|
|
1422
1448
|
endTime: readNumber(args, "endTime"),
|
|
1423
|
-
limit:
|
|
1449
|
+
limit: readPositiveInt(args, "limit")
|
|
1424
1450
|
}),
|
|
1425
1451
|
publicRateLimit("market_get_klines", 20)
|
|
1426
1452
|
);
|
|
@@ -1430,12 +1456,12 @@ function registerMarketTools() {
|
|
|
1430
1456
|
{
|
|
1431
1457
|
name: "market_get_ticker_24hr",
|
|
1432
1458
|
module: "market",
|
|
1433
|
-
description: "Get 24h price change statistics for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1459
|
+
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
1460
|
isWrite: false,
|
|
1435
1461
|
inputSchema: {
|
|
1436
1462
|
type: "object",
|
|
1437
1463
|
properties: {
|
|
1438
|
-
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
|
|
1464
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1439
1465
|
}
|
|
1440
1466
|
},
|
|
1441
1467
|
handler: async (rawArgs, context) => {
|
|
@@ -1451,12 +1477,12 @@ function registerMarketTools() {
|
|
|
1451
1477
|
{
|
|
1452
1478
|
name: "market_get_ticker_price",
|
|
1453
1479
|
module: "market",
|
|
1454
|
-
description: "Get latest price for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1480
|
+
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
1481
|
isWrite: false,
|
|
1456
1482
|
inputSchema: {
|
|
1457
1483
|
type: "object",
|
|
1458
1484
|
properties: {
|
|
1459
|
-
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
|
|
1485
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1460
1486
|
}
|
|
1461
1487
|
},
|
|
1462
1488
|
handler: async (rawArgs, context) => {
|
|
@@ -1472,12 +1498,12 @@ function registerMarketTools() {
|
|
|
1472
1498
|
{
|
|
1473
1499
|
name: "market_get_book_ticker",
|
|
1474
1500
|
module: "market",
|
|
1475
|
-
description: "Get best bid/ask price for a spot symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1501
|
+
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
1502
|
isWrite: false,
|
|
1477
1503
|
inputSchema: {
|
|
1478
1504
|
type: "object",
|
|
1479
1505
|
properties: {
|
|
1480
|
-
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
|
|
1506
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1481
1507
|
}
|
|
1482
1508
|
},
|
|
1483
1509
|
handler: async (rawArgs, context) => {
|
|
@@ -1515,7 +1541,7 @@ function registerMarketTools() {
|
|
|
1515
1541
|
interval: requireString(args, "interval"),
|
|
1516
1542
|
startTime: readNumber(args, "startTime"),
|
|
1517
1543
|
endTime: readNumber(args, "endTime"),
|
|
1518
|
-
limit:
|
|
1544
|
+
limit: readPositiveInt(args, "limit")
|
|
1519
1545
|
}),
|
|
1520
1546
|
publicRateLimit("market_get_index_klines", 20)
|
|
1521
1547
|
);
|
|
@@ -1525,12 +1551,12 @@ function registerMarketTools() {
|
|
|
1525
1551
|
{
|
|
1526
1552
|
name: "market_get_mark_price",
|
|
1527
1553
|
module: "market",
|
|
1528
|
-
description: "Get latest mark price for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1554
|
+
description: "Get latest mark price for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1529
1555
|
isWrite: false,
|
|
1530
1556
|
inputSchema: {
|
|
1531
1557
|
type: "object",
|
|
1532
1558
|
properties: {
|
|
1533
|
-
symbol: { type: "string", description: "e.g.
|
|
1559
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1534
1560
|
}
|
|
1535
1561
|
},
|
|
1536
1562
|
handler: async (rawArgs, context) => {
|
|
@@ -1546,12 +1572,12 @@ function registerMarketTools() {
|
|
|
1546
1572
|
{
|
|
1547
1573
|
name: "market_get_mark_price_klines",
|
|
1548
1574
|
module: "market",
|
|
1549
|
-
description: "Get mark price K-line data. Public endpoint. Rate limit: 20 req/s.",
|
|
1575
|
+
description: "Get mark price K-line data. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1550
1576
|
isWrite: false,
|
|
1551
1577
|
inputSchema: {
|
|
1552
1578
|
type: "object",
|
|
1553
1579
|
properties: {
|
|
1554
|
-
symbol: { type: "string", description: "e.g.
|
|
1580
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1555
1581
|
interval: { type: "string", enum: [...TOOBIT_CANDLE_BARS] },
|
|
1556
1582
|
startTime: { type: "number" },
|
|
1557
1583
|
endTime: { type: "number" },
|
|
@@ -1568,7 +1594,7 @@ function registerMarketTools() {
|
|
|
1568
1594
|
interval: requireString(args, "interval"),
|
|
1569
1595
|
startTime: readNumber(args, "startTime"),
|
|
1570
1596
|
endTime: readNumber(args, "endTime"),
|
|
1571
|
-
limit:
|
|
1597
|
+
limit: readPositiveInt(args, "limit")
|
|
1572
1598
|
}),
|
|
1573
1599
|
publicRateLimit("market_get_mark_price_klines", 20)
|
|
1574
1600
|
);
|
|
@@ -1578,12 +1604,12 @@ function registerMarketTools() {
|
|
|
1578
1604
|
{
|
|
1579
1605
|
name: "market_get_funding_rate",
|
|
1580
1606
|
module: "market",
|
|
1581
|
-
description: "Get current funding rate for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1607
|
+
description: "Get current funding rate for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1582
1608
|
isWrite: false,
|
|
1583
1609
|
inputSchema: {
|
|
1584
1610
|
type: "object",
|
|
1585
1611
|
properties: {
|
|
1586
|
-
symbol: { type: "string", description: "e.g.
|
|
1612
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1587
1613
|
}
|
|
1588
1614
|
},
|
|
1589
1615
|
handler: async (rawArgs, context) => {
|
|
@@ -1599,12 +1625,12 @@ function registerMarketTools() {
|
|
|
1599
1625
|
{
|
|
1600
1626
|
name: "market_get_funding_rate_history",
|
|
1601
1627
|
module: "market",
|
|
1602
|
-
description: "Get historical funding rates for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1628
|
+
description: "Get historical funding rates for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1603
1629
|
isWrite: false,
|
|
1604
1630
|
inputSchema: {
|
|
1605
1631
|
type: "object",
|
|
1606
1632
|
properties: {
|
|
1607
|
-
symbol: { type: "string", description: "e.g.
|
|
1633
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1608
1634
|
startTime: { type: "number" },
|
|
1609
1635
|
endTime: { type: "number" },
|
|
1610
1636
|
limit: { type: "number", description: "Default 100, max 1000" }
|
|
@@ -1619,7 +1645,7 @@ function registerMarketTools() {
|
|
|
1619
1645
|
symbol: requireString(args, "symbol"),
|
|
1620
1646
|
startTime: readNumber(args, "startTime"),
|
|
1621
1647
|
endTime: readNumber(args, "endTime"),
|
|
1622
|
-
limit:
|
|
1648
|
+
limit: readPositiveInt(args, "limit")
|
|
1623
1649
|
}),
|
|
1624
1650
|
publicRateLimit("market_get_funding_rate_history", 20)
|
|
1625
1651
|
);
|
|
@@ -1629,12 +1655,12 @@ function registerMarketTools() {
|
|
|
1629
1655
|
{
|
|
1630
1656
|
name: "market_get_open_interest",
|
|
1631
1657
|
module: "market",
|
|
1632
|
-
description: "Get total open interest for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1658
|
+
description: "Get total open interest for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1633
1659
|
isWrite: false,
|
|
1634
1660
|
inputSchema: {
|
|
1635
1661
|
type: "object",
|
|
1636
1662
|
properties: {
|
|
1637
|
-
symbol: { type: "string", description: "e.g.
|
|
1663
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1638
1664
|
}
|
|
1639
1665
|
},
|
|
1640
1666
|
handler: async (rawArgs, context) => {
|
|
@@ -1655,7 +1681,7 @@ function registerMarketTools() {
|
|
|
1655
1681
|
inputSchema: {
|
|
1656
1682
|
type: "object",
|
|
1657
1683
|
properties: {
|
|
1658
|
-
symbol: { type: "string", description: "e.g.
|
|
1684
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1659
1685
|
period: { type: "string", description: "e.g. 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d" },
|
|
1660
1686
|
startTime: { type: "number" },
|
|
1661
1687
|
endTime: { type: "number" },
|
|
@@ -1672,7 +1698,7 @@ function registerMarketTools() {
|
|
|
1672
1698
|
period: requireString(args, "period"),
|
|
1673
1699
|
startTime: readNumber(args, "startTime"),
|
|
1674
1700
|
endTime: readNumber(args, "endTime"),
|
|
1675
|
-
limit:
|
|
1701
|
+
limit: readPositiveInt(args, "limit")
|
|
1676
1702
|
}),
|
|
1677
1703
|
publicRateLimit("market_get_long_short_ratio", 20)
|
|
1678
1704
|
);
|
|
@@ -1682,12 +1708,12 @@ function registerMarketTools() {
|
|
|
1682
1708
|
{
|
|
1683
1709
|
name: "market_get_contract_ticker_24hr",
|
|
1684
1710
|
module: "market",
|
|
1685
|
-
description: "Get 24h price change statistics for a futures contract. Public endpoint. Rate limit: 20 req/s.",
|
|
1711
|
+
description: "Get 24h price change statistics for a futures contract. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1686
1712
|
isWrite: false,
|
|
1687
1713
|
inputSchema: {
|
|
1688
1714
|
type: "object",
|
|
1689
1715
|
properties: {
|
|
1690
|
-
symbol: { type: "string", description: "e.g.
|
|
1716
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1691
1717
|
}
|
|
1692
1718
|
},
|
|
1693
1719
|
handler: async (rawArgs, context) => {
|
|
@@ -1703,12 +1729,12 @@ function registerMarketTools() {
|
|
|
1703
1729
|
{
|
|
1704
1730
|
name: "market_get_contract_ticker_price",
|
|
1705
1731
|
module: "market",
|
|
1706
|
-
description: "Get latest price for a futures contract. Public endpoint. Rate limit: 20 req/s.",
|
|
1732
|
+
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
1733
|
isWrite: false,
|
|
1708
1734
|
inputSchema: {
|
|
1709
1735
|
type: "object",
|
|
1710
1736
|
properties: {
|
|
1711
|
-
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
|
|
1737
|
+
symbol: { type: "string", description: "e.g. BTC-SWAP-USDT or BTCUSDT. Omit for all." }
|
|
1712
1738
|
}
|
|
1713
1739
|
},
|
|
1714
1740
|
handler: async (rawArgs, context) => {
|
|
@@ -1745,12 +1771,12 @@ function registerMarketTools() {
|
|
|
1745
1771
|
{
|
|
1746
1772
|
name: "market_get_insurance_fund",
|
|
1747
1773
|
module: "market",
|
|
1748
|
-
description: "Get insurance fund balance for a symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1774
|
+
description: "Get insurance fund balance for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1749
1775
|
isWrite: false,
|
|
1750
1776
|
inputSchema: {
|
|
1751
1777
|
type: "object",
|
|
1752
1778
|
properties: {
|
|
1753
|
-
symbol: { type: "string", description: "e.g.
|
|
1779
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
|
|
1754
1780
|
},
|
|
1755
1781
|
required: ["symbol"]
|
|
1756
1782
|
},
|
|
@@ -1767,12 +1793,12 @@ function registerMarketTools() {
|
|
|
1767
1793
|
{
|
|
1768
1794
|
name: "market_get_risk_limits",
|
|
1769
1795
|
module: "market",
|
|
1770
|
-
description: "Get risk limits configuration for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1796
|
+
description: "Get risk limits configuration for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1771
1797
|
isWrite: false,
|
|
1772
1798
|
inputSchema: {
|
|
1773
1799
|
type: "object",
|
|
1774
1800
|
properties: {
|
|
1775
|
-
symbol: { type: "string", description: "e.g.
|
|
1801
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
|
|
1776
1802
|
},
|
|
1777
1803
|
required: ["symbol"]
|
|
1778
1804
|
},
|
|
@@ -2078,12 +2104,15 @@ function allToolSpecs() {
|
|
|
2078
2104
|
];
|
|
2079
2105
|
}
|
|
2080
2106
|
function createToolRunner(client, config) {
|
|
2081
|
-
const fullConfig = { ...config, modules: [...MODULES]
|
|
2107
|
+
const fullConfig = { ...config, modules: [...MODULES] };
|
|
2082
2108
|
const tools = allToolSpecs();
|
|
2083
2109
|
const toolMap = new Map(tools.map((t) => [t.name, t]));
|
|
2084
2110
|
return async (toolName, args) => {
|
|
2085
2111
|
const tool = toolMap.get(toolName);
|
|
2086
2112
|
if (!tool) throw new Error(`Unknown tool: ${toolName}`);
|
|
2113
|
+
if (config.readOnly && tool.isWrite) {
|
|
2114
|
+
throw new Error(`Tool "${toolName}" is a write operation and is blocked in read-only mode.`);
|
|
2115
|
+
}
|
|
2087
2116
|
const result = await tool.handler(args, { config: fullConfig, client });
|
|
2088
2117
|
return result;
|
|
2089
2118
|
};
|
|
@@ -2264,11 +2293,58 @@ function parseCli(argv = process.argv.slice(2)) {
|
|
|
2264
2293
|
}
|
|
2265
2294
|
|
|
2266
2295
|
// src/formatter.ts
|
|
2296
|
+
function extractRows(value) {
|
|
2297
|
+
if (Array.isArray(value)) return value;
|
|
2298
|
+
if (typeof value === "object" && value !== null) {
|
|
2299
|
+
const entries = Object.values(value);
|
|
2300
|
+
for (const v of entries) {
|
|
2301
|
+
if (Array.isArray(v)) return v;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2267
2306
|
function formatJson(data, json) {
|
|
2268
2307
|
if (json) return JSON.stringify(data, null, 2);
|
|
2269
2308
|
if (data === null || data === void 0) return "No data.";
|
|
2270
2309
|
if (typeof data !== "object") return String(data);
|
|
2271
|
-
|
|
2310
|
+
const record = data;
|
|
2311
|
+
const inner = record.data ?? record;
|
|
2312
|
+
const rows = extractRows(inner);
|
|
2313
|
+
if (rows) {
|
|
2314
|
+
if (rows.length === 0) return "No data.";
|
|
2315
|
+
if (typeof rows[0] === "object" && rows[0] !== null) {
|
|
2316
|
+
return formatTable(rows);
|
|
2317
|
+
}
|
|
2318
|
+
return rows.map(String).join("\n");
|
|
2319
|
+
}
|
|
2320
|
+
if (typeof inner === "object" && inner !== null) {
|
|
2321
|
+
return formatKv(inner);
|
|
2322
|
+
}
|
|
2323
|
+
return String(inner);
|
|
2324
|
+
}
|
|
2325
|
+
function formatKv(data) {
|
|
2326
|
+
const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null);
|
|
2327
|
+
if (entries.length === 0) return "No data.";
|
|
2328
|
+
const maxKey = Math.max(...entries.map(([k]) => k.length));
|
|
2329
|
+
return entries.map(([k, v]) => {
|
|
2330
|
+
if (typeof v === "object") return `${k.padEnd(maxKey)} ${JSON.stringify(v)}`;
|
|
2331
|
+
return `${k.padEnd(maxKey)} ${String(v)}`;
|
|
2332
|
+
}).join("\n");
|
|
2333
|
+
}
|
|
2334
|
+
function formatTable(rows, columns) {
|
|
2335
|
+
if (rows.length === 0) return "No data.";
|
|
2336
|
+
const keys = columns ?? Object.keys(rows[0]);
|
|
2337
|
+
const widths = keys.map(
|
|
2338
|
+
(k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
|
|
2339
|
+
);
|
|
2340
|
+
const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
|
|
2341
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
2342
|
+
const body = rows.map(
|
|
2343
|
+
(row) => keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" ")
|
|
2344
|
+
).join("\n");
|
|
2345
|
+
return `${header}
|
|
2346
|
+
${separator}
|
|
2347
|
+
${body}`;
|
|
2272
2348
|
}
|
|
2273
2349
|
|
|
2274
2350
|
// src/commands/market.ts
|
|
@@ -2331,9 +2407,18 @@ async function handleMarketCommand(cli, run) {
|
|
|
2331
2407
|
case "long-short-ratio":
|
|
2332
2408
|
result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
|
|
2333
2409
|
break;
|
|
2410
|
+
case "contract-ticker-price":
|
|
2411
|
+
result = await run("market_get_contract_ticker_price", { symbol: f.symbol });
|
|
2412
|
+
break;
|
|
2413
|
+
case "insurance-fund":
|
|
2414
|
+
result = await run("market_get_insurance_fund", { symbol: f.symbol });
|
|
2415
|
+
break;
|
|
2416
|
+
case "risk-limits":
|
|
2417
|
+
result = await run("market_get_risk_limits", { symbol: f.symbol });
|
|
2418
|
+
break;
|
|
2334
2419
|
default:
|
|
2335
2420
|
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
|
|
2421
|
+
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
2422
|
`);
|
|
2338
2423
|
return;
|
|
2339
2424
|
}
|
package/package.json
CHANGED
package/src/commands/market.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|