toobit-trade-cli 1.0.0

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,491 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import {
5
+ loadConfig,
6
+ ToobitRestClient,
7
+ createToolRunner,
8
+ toToolErrorPayload,
9
+ configFilePath as configFilePath2
10
+ } from "@toobit_agent/agent-toobitkit-core";
11
+
12
+ // src/parser.ts
13
+ import { parseArgs } from "util";
14
+ function parseCli(argv = process.argv.slice(2)) {
15
+ const STRING_OPTIONS = /* @__PURE__ */ new Set([
16
+ "profile",
17
+ "symbol",
18
+ "interval",
19
+ "limit",
20
+ "side",
21
+ "type",
22
+ "quantity",
23
+ "price",
24
+ "orderId",
25
+ "clientOrderId",
26
+ "leverage",
27
+ "orderType",
28
+ "tokenId",
29
+ "instId",
30
+ "period",
31
+ "bar",
32
+ "startTime",
33
+ "endTime",
34
+ "marginType"
35
+ ]);
36
+ let command = "help";
37
+ let subcommand = "";
38
+ const flagArgs = [];
39
+ let positionalCount = 0;
40
+ for (let i = 0; i < argv.length; i++) {
41
+ const arg = argv[i];
42
+ if (arg.startsWith("-")) {
43
+ flagArgs.push(arg);
44
+ const optName = arg.replace(/^--?/, "");
45
+ if (STRING_OPTIONS.has(optName) && i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
46
+ flagArgs.push(argv[++i]);
47
+ }
48
+ } else {
49
+ if (positionalCount === 0) command = arg;
50
+ else if (positionalCount === 1) subcommand = arg;
51
+ positionalCount++;
52
+ }
53
+ }
54
+ const parsed = parseArgs({
55
+ args: flagArgs,
56
+ options: {
57
+ profile: { type: "string" },
58
+ json: { type: "boolean", default: false },
59
+ "read-only": { type: "boolean", default: false },
60
+ symbol: { type: "string" },
61
+ interval: { type: "string" },
62
+ limit: { type: "string" },
63
+ side: { type: "string" },
64
+ type: { type: "string" },
65
+ quantity: { type: "string" },
66
+ price: { type: "string" },
67
+ orderId: { type: "string" },
68
+ clientOrderId: { type: "string" },
69
+ leverage: { type: "string" },
70
+ orderType: { type: "string" },
71
+ tokenId: { type: "string" },
72
+ instId: { type: "string" },
73
+ period: { type: "string" },
74
+ bar: { type: "string" },
75
+ startTime: { type: "string" },
76
+ endTime: { type: "string" },
77
+ marginType: { type: "string" }
78
+ },
79
+ strict: false,
80
+ allowPositionals: true
81
+ });
82
+ return {
83
+ command,
84
+ subcommand,
85
+ profile: parsed.values.profile,
86
+ json: parsed.values.json ?? false,
87
+ readOnly: parsed.values["read-only"] ?? false,
88
+ flags: parsed.values,
89
+ positionals: parsed.positionals
90
+ };
91
+ }
92
+
93
+ // src/formatter.ts
94
+ function formatJson(data, json) {
95
+ if (json) return JSON.stringify(data, null, 2);
96
+ if (data === null || data === void 0) return "No data.";
97
+ if (typeof data !== "object") return String(data);
98
+ return JSON.stringify(data, null, 2);
99
+ }
100
+
101
+ // src/commands/market.ts
102
+ async function handleMarketCommand(cli, run) {
103
+ const f = cli.flags;
104
+ let result;
105
+ switch (cli.subcommand) {
106
+ case "time":
107
+ result = await run("market_get_server_time", {});
108
+ break;
109
+ case "info":
110
+ result = await run("market_get_exchange_info", {});
111
+ break;
112
+ case "ticker":
113
+ result = await run("market_get_ticker_price", { symbol: f.symbol });
114
+ break;
115
+ case "ticker-24hr":
116
+ result = await run("market_get_ticker_24hr", { symbol: f.symbol });
117
+ break;
118
+ case "depth":
119
+ result = await run("market_get_depth", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : void 0 });
120
+ break;
121
+ case "trades":
122
+ result = await run("market_get_trades", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : void 0 });
123
+ break;
124
+ case "klines":
125
+ case "candles":
126
+ result = await run("market_get_klines", {
127
+ symbol: f.symbol,
128
+ interval: f.interval ?? f.bar ?? "1h",
129
+ limit: f.limit ? Number(f.limit) : void 0,
130
+ startTime: f.startTime ? Number(f.startTime) : void 0,
131
+ endTime: f.endTime ? Number(f.endTime) : void 0
132
+ });
133
+ break;
134
+ case "book-ticker":
135
+ result = await run("market_get_book_ticker", { symbol: f.symbol });
136
+ break;
137
+ case "mark-price":
138
+ result = await run("market_get_mark_price", { symbol: f.symbol });
139
+ break;
140
+ case "funding-rate":
141
+ result = await run("market_get_funding_rate", { symbol: f.symbol });
142
+ break;
143
+ case "funding-rate-history":
144
+ result = await run("market_get_funding_rate_history", {
145
+ symbol: f.symbol,
146
+ limit: f.limit ? Number(f.limit) : void 0
147
+ });
148
+ break;
149
+ case "open-interest":
150
+ result = await run("market_get_open_interest", { symbol: f.symbol });
151
+ break;
152
+ case "index":
153
+ result = await run("market_get_index_price", { symbol: f.symbol });
154
+ break;
155
+ case "contract-ticker":
156
+ result = await run("market_get_contract_ticker_24hr", { symbol: f.symbol });
157
+ break;
158
+ case "long-short-ratio":
159
+ result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
160
+ break;
161
+ default:
162
+ process.stdout.write(`Unknown market subcommand: ${cli.subcommand}
163
+ 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
164
+ `);
165
+ return;
166
+ }
167
+ process.stdout.write(formatJson(result, cli.json) + "\n");
168
+ }
169
+
170
+ // src/commands/spot.ts
171
+ async function handleSpotCommand(cli, run) {
172
+ const f = cli.flags;
173
+ let result;
174
+ switch (cli.subcommand) {
175
+ case "place":
176
+ result = await run("spot_place_order", {
177
+ symbol: f.symbol,
178
+ side: f.side,
179
+ type: f.type ?? "MARKET",
180
+ quantity: f.quantity,
181
+ price: f.price
182
+ });
183
+ break;
184
+ case "cancel":
185
+ result = await run("spot_cancel_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
186
+ break;
187
+ case "cancel-all":
188
+ result = await run("spot_cancel_open_orders", { symbol: f.symbol });
189
+ break;
190
+ case "get":
191
+ result = await run("spot_get_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
192
+ break;
193
+ case "open-orders":
194
+ case "orders":
195
+ result = await run("spot_get_open_orders", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : void 0 });
196
+ break;
197
+ case "history":
198
+ result = await run("spot_get_trade_orders", {
199
+ symbol: f.symbol,
200
+ limit: f.limit ? Number(f.limit) : void 0,
201
+ startTime: f.startTime ? Number(f.startTime) : void 0,
202
+ endTime: f.endTime ? Number(f.endTime) : void 0
203
+ });
204
+ break;
205
+ case "fills":
206
+ result = await run("spot_get_fills", {
207
+ symbol: f.symbol,
208
+ limit: f.limit ? Number(f.limit) : void 0
209
+ });
210
+ break;
211
+ default:
212
+ process.stdout.write(`Unknown spot subcommand: ${cli.subcommand}
213
+ Available: place, cancel, cancel-all, get, orders, open-orders, history, fills
214
+ `);
215
+ return;
216
+ }
217
+ process.stdout.write(formatJson(result, cli.json) + "\n");
218
+ }
219
+
220
+ // src/commands/futures.ts
221
+ async function handleFuturesCommand(cli, run) {
222
+ const f = cli.flags;
223
+ let result;
224
+ switch (cli.subcommand) {
225
+ case "place":
226
+ result = await run("futures_place_order", {
227
+ symbol: f.symbol,
228
+ side: f.side,
229
+ orderType: f.orderType ?? f.type ?? "MARKET",
230
+ quantity: f.quantity,
231
+ price: f.price,
232
+ leverage: f.leverage
233
+ });
234
+ break;
235
+ case "cancel":
236
+ result = await run("futures_cancel_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
237
+ break;
238
+ case "cancel-all":
239
+ result = await run("futures_cancel_all_orders", { symbol: f.symbol });
240
+ break;
241
+ case "amend":
242
+ result = await run("futures_amend_order", { orderId: f.orderId, quantity: f.quantity, price: f.price });
243
+ break;
244
+ case "get":
245
+ result = await run("futures_get_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
246
+ break;
247
+ case "orders":
248
+ case "open-orders":
249
+ result = await run("futures_get_open_orders", { symbol: f.symbol });
250
+ break;
251
+ case "history":
252
+ result = await run("futures_get_history_orders", {
253
+ symbol: f.symbol,
254
+ limit: f.limit ? Number(f.limit) : void 0
255
+ });
256
+ break;
257
+ case "positions":
258
+ result = await run("futures_get_positions", { symbol: f.symbol });
259
+ break;
260
+ case "history-positions":
261
+ result = await run("futures_get_history_positions", { symbol: f.symbol });
262
+ break;
263
+ case "leverage":
264
+ if (f.leverage) {
265
+ result = await run("futures_set_leverage", { symbol: f.symbol, leverage: Number(f.leverage) });
266
+ } else {
267
+ result = await run("futures_get_leverage", { symbol: f.symbol });
268
+ }
269
+ break;
270
+ case "margin-type":
271
+ result = await run("futures_set_margin_type", { symbol: f.symbol, marginType: f.marginType });
272
+ break;
273
+ case "flash-close":
274
+ result = await run("futures_flash_close", { symbol: f.symbol, side: f.side });
275
+ break;
276
+ case "balance":
277
+ result = await run("futures_get_balance", {});
278
+ break;
279
+ case "fills":
280
+ result = await run("futures_get_fills", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : void 0 });
281
+ break;
282
+ case "pnl":
283
+ result = await run("futures_get_today_pnl", {});
284
+ break;
285
+ case "commission":
286
+ result = await run("futures_get_commission_rate", { symbol: f.symbol });
287
+ break;
288
+ default:
289
+ process.stdout.write(`Unknown futures subcommand: ${cli.subcommand}
290
+ Available: place, cancel, cancel-all, amend, get, orders, history, positions, history-positions, leverage, margin-type, flash-close, balance, fills, pnl, commission
291
+ `);
292
+ return;
293
+ }
294
+ process.stdout.write(formatJson(result, cli.json) + "\n");
295
+ }
296
+
297
+ // src/commands/account.ts
298
+ async function handleAccountCommand(cli, run) {
299
+ const f = cli.flags;
300
+ let result;
301
+ switch (cli.subcommand) {
302
+ case "balance":
303
+ case "info":
304
+ case "":
305
+ result = await run("account_get_info", {});
306
+ break;
307
+ case "balance-flow":
308
+ result = await run("account_get_balance_flow", {
309
+ tokenId: f.tokenId,
310
+ limit: f.limit ? Number(f.limit) : void 0
311
+ });
312
+ break;
313
+ case "sub-accounts":
314
+ result = await run("account_get_sub_accounts", {});
315
+ break;
316
+ case "check-api-key":
317
+ result = await run("account_check_api_key", {});
318
+ break;
319
+ case "deposit-address":
320
+ result = await run("account_get_deposit_address", { tokenId: f.tokenId });
321
+ break;
322
+ case "deposits":
323
+ result = await run("account_get_deposit_orders", {
324
+ tokenId: f.tokenId,
325
+ limit: f.limit ? Number(f.limit) : void 0
326
+ });
327
+ break;
328
+ case "withdrawals":
329
+ result = await run("account_get_withdraw_orders", {
330
+ tokenId: f.tokenId,
331
+ limit: f.limit ? Number(f.limit) : void 0
332
+ });
333
+ break;
334
+ case "audit":
335
+ result = await run("trade_get_history", { limit: f.limit ? Number(f.limit) : void 0 });
336
+ break;
337
+ default:
338
+ process.stdout.write(`Unknown account subcommand: ${cli.subcommand}
339
+ Available: balance, info, balance-flow, sub-accounts, check-api-key, deposit-address, deposits, withdrawals, audit
340
+ `);
341
+ return;
342
+ }
343
+ process.stdout.write(formatJson(result, cli.json) + "\n");
344
+ }
345
+
346
+ // src/commands/config.ts
347
+ import * as readline from "readline";
348
+ import {
349
+ configFilePath,
350
+ readFullConfig,
351
+ writeFullConfig
352
+ } from "@toobit_agent/agent-toobitkit-core";
353
+ function prompt(question) {
354
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
355
+ return new Promise((resolve) => {
356
+ rl.question(question, (answer) => {
357
+ rl.close();
358
+ resolve(answer.trim());
359
+ });
360
+ });
361
+ }
362
+ async function handleConfigCommand(cli) {
363
+ switch (cli.subcommand) {
364
+ case "init": {
365
+ process.stdout.write("Toobit API Configuration Wizard\n\n");
366
+ const profileName = await prompt("Profile name (default: live): ") || "live";
367
+ const apiKey = await prompt("API Key: ");
368
+ const secretKey = await prompt("Secret Key: ");
369
+ if (!apiKey || !secretKey) {
370
+ process.stdout.write("API Key and Secret Key are required.\n");
371
+ return;
372
+ }
373
+ const config = readFullConfig();
374
+ config.default_profile = config.default_profile ?? profileName;
375
+ if (!config.profiles) config.profiles = {};
376
+ config.profiles[profileName] = { api_key: apiKey, secret_key: secretKey };
377
+ writeFullConfig(config);
378
+ process.stdout.write(`
379
+ \u2713 Saved to ${configFilePath()}
380
+ Profile: ${profileName}
381
+ `);
382
+ break;
383
+ }
384
+ case "show": {
385
+ const config = readFullConfig();
386
+ process.stdout.write(`Config file: ${configFilePath()}
387
+
388
+ `);
389
+ process.stdout.write(`Default profile: ${config.default_profile ?? "(none)"}
390
+ `);
391
+ process.stdout.write(`Profiles: ${Object.keys(config.profiles ?? {}).join(", ") || "(none)"}
392
+ `);
393
+ break;
394
+ }
395
+ case "list-profiles":
396
+ case "list": {
397
+ const config = readFullConfig();
398
+ const profiles = Object.keys(config.profiles ?? {});
399
+ if (profiles.length === 0) {
400
+ process.stdout.write("No profiles configured.\n");
401
+ } else {
402
+ for (const name of profiles) {
403
+ const marker = name === config.default_profile ? " (default)" : "";
404
+ process.stdout.write(` ${name}${marker}
405
+ `);
406
+ }
407
+ }
408
+ break;
409
+ }
410
+ default:
411
+ process.stdout.write(`Unknown config subcommand: ${cli.subcommand}
412
+ Available: init, show, list
413
+ `);
414
+ }
415
+ }
416
+
417
+ // src/index.ts
418
+ function printHelp() {
419
+ const help = `
420
+ Toobit Trade CLI \u2014 Trade from your terminal
421
+
422
+ Usage: toobit <command> <subcommand> [options]
423
+
424
+ Commands:
425
+ market Market data (ticker, depth, klines, funding-rate, etc.)
426
+ spot Spot trading (place, cancel, orders, fills)
427
+ futures USDT-M futures (place, cancel, positions, leverage, etc.)
428
+ account Account info (balance, deposits, withdrawals, audit)
429
+ config Configuration management (init, show, list)
430
+
431
+ Global Options:
432
+ --profile <name> Profile from ${configFilePath2()}
433
+ --json Output raw JSON
434
+ --read-only Disable write operations
435
+ --help Show this help
436
+
437
+ Examples:
438
+ toobit market ticker --symbol BTCUSDT
439
+ toobit market candles --symbol BTCUSDT --interval 1h --limit 10
440
+ toobit market funding-rate --symbol BTCUSDT
441
+ toobit spot place --symbol BTCUSDT --side BUY --type MARKET --quantity 0.001
442
+ toobit futures positions --symbol BTCUSDT
443
+ toobit account balance
444
+ toobit config init
445
+ `;
446
+ process.stdout.write(help);
447
+ }
448
+ async function main() {
449
+ const cli = parseCli();
450
+ if (cli.command === "help" || cli.flags.help) {
451
+ printHelp();
452
+ return;
453
+ }
454
+ if (cli.command === "config") {
455
+ await handleConfigCommand(cli);
456
+ return;
457
+ }
458
+ const config = loadConfig({
459
+ modules: "all",
460
+ profile: cli.profile,
461
+ readOnly: cli.readOnly,
462
+ userAgent: "toobit-trade-cli/1.0.0",
463
+ sourceTag: "CLI"
464
+ });
465
+ const client = new ToobitRestClient(config);
466
+ const run = createToolRunner(client, config);
467
+ switch (cli.command) {
468
+ case "market":
469
+ await handleMarketCommand(cli, run);
470
+ break;
471
+ case "spot":
472
+ await handleSpotCommand(cli, run);
473
+ break;
474
+ case "futures":
475
+ await handleFuturesCommand(cli, run);
476
+ break;
477
+ case "account":
478
+ await handleAccountCommand(cli, run);
479
+ break;
480
+ default:
481
+ process.stdout.write(`Unknown command: ${cli.command}
482
+ `);
483
+ printHelp();
484
+ }
485
+ }
486
+ main().catch((error) => {
487
+ const payload = toToolErrorPayload(error);
488
+ process.stderr.write(`${JSON.stringify(payload, null, 2)}
489
+ `);
490
+ process.exitCode = 1;
491
+ });
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "toobit-trade-cli",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "toobit": "dist/index.js"
7
+ },
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "typecheck": "tsc --noEmit",
19
+ "clean": "rm -rf dist"
20
+ },
21
+ "dependencies": {
22
+ "@toobit_agent/agent-toobitkit-core": "workspace:*",
23
+ "smol-toml": "^1.3.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.0.0",
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.5.0"
29
+ }
30
+ }
@@ -0,0 +1,51 @@
1
+ import type { ToolRunner } from "@toobit_agent/agent-toobitkit-core";
2
+ import type { CliParsed } from "../parser.js";
3
+ import { formatJson } from "../formatter.js";
4
+
5
+ export async function handleAccountCommand(cli: CliParsed, run: ToolRunner): Promise<void> {
6
+ const f = cli.flags;
7
+ let result;
8
+
9
+ switch (cli.subcommand) {
10
+ case "balance":
11
+ case "info":
12
+ case "":
13
+ result = await run("account_get_info", {});
14
+ break;
15
+ case "balance-flow":
16
+ result = await run("account_get_balance_flow", {
17
+ tokenId: f.tokenId,
18
+ limit: f.limit ? Number(f.limit) : undefined,
19
+ });
20
+ break;
21
+ case "sub-accounts":
22
+ result = await run("account_get_sub_accounts", {});
23
+ break;
24
+ case "check-api-key":
25
+ result = await run("account_check_api_key", {});
26
+ break;
27
+ case "deposit-address":
28
+ result = await run("account_get_deposit_address", { tokenId: f.tokenId });
29
+ break;
30
+ case "deposits":
31
+ result = await run("account_get_deposit_orders", {
32
+ tokenId: f.tokenId,
33
+ limit: f.limit ? Number(f.limit) : undefined,
34
+ });
35
+ break;
36
+ case "withdrawals":
37
+ result = await run("account_get_withdraw_orders", {
38
+ tokenId: f.tokenId,
39
+ limit: f.limit ? Number(f.limit) : undefined,
40
+ });
41
+ break;
42
+ case "audit":
43
+ result = await run("trade_get_history", { limit: f.limit ? Number(f.limit) : undefined });
44
+ break;
45
+ default:
46
+ process.stdout.write(`Unknown account subcommand: ${cli.subcommand}\nAvailable: balance, info, balance-flow, sub-accounts, check-api-key, deposit-address, deposits, withdrawals, audit\n`);
47
+ return;
48
+ }
49
+
50
+ process.stdout.write(formatJson(result, cli.json) + "\n");
51
+ }
@@ -0,0 +1,64 @@
1
+ import * as readline from "node:readline";
2
+ import {
3
+ configFilePath,
4
+ readFullConfig,
5
+ writeFullConfig,
6
+ } from "@toobit_agent/agent-toobitkit-core";
7
+ import type { CliParsed } from "../parser.js";
8
+
9
+ function prompt(question: string): Promise<string> {
10
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11
+ return new Promise((resolve) => {
12
+ rl.question(question, (answer) => {
13
+ rl.close();
14
+ resolve(answer.trim());
15
+ });
16
+ });
17
+ }
18
+
19
+ export async function handleConfigCommand(cli: CliParsed): Promise<void> {
20
+ switch (cli.subcommand) {
21
+ case "init": {
22
+ process.stdout.write("Toobit API Configuration Wizard\n\n");
23
+ const profileName = (await prompt("Profile name (default: live): ")) || "live";
24
+ const apiKey = await prompt("API Key: ");
25
+ const secretKey = await prompt("Secret Key: ");
26
+
27
+ if (!apiKey || !secretKey) {
28
+ process.stdout.write("API Key and Secret Key are required.\n");
29
+ return;
30
+ }
31
+
32
+ const config = readFullConfig();
33
+ config.default_profile = config.default_profile ?? profileName;
34
+ if (!config.profiles) config.profiles = {};
35
+ config.profiles[profileName] = { api_key: apiKey, secret_key: secretKey };
36
+ writeFullConfig(config);
37
+ process.stdout.write(`\n✓ Saved to ${configFilePath()}\n Profile: ${profileName}\n`);
38
+ break;
39
+ }
40
+ case "show": {
41
+ const config = readFullConfig();
42
+ process.stdout.write(`Config file: ${configFilePath()}\n\n`);
43
+ process.stdout.write(`Default profile: ${config.default_profile ?? "(none)"}\n`);
44
+ process.stdout.write(`Profiles: ${Object.keys(config.profiles ?? {}).join(", ") || "(none)"}\n`);
45
+ break;
46
+ }
47
+ case "list-profiles":
48
+ case "list": {
49
+ const config = readFullConfig();
50
+ const profiles = Object.keys(config.profiles ?? {});
51
+ if (profiles.length === 0) {
52
+ process.stdout.write("No profiles configured.\n");
53
+ } else {
54
+ for (const name of profiles) {
55
+ const marker = name === config.default_profile ? " (default)" : "";
56
+ process.stdout.write(` ${name}${marker}\n`);
57
+ }
58
+ }
59
+ break;
60
+ }
61
+ default:
62
+ process.stdout.write(`Unknown config subcommand: ${cli.subcommand}\nAvailable: init, show, list\n`);
63
+ }
64
+ }
@@ -0,0 +1,79 @@
1
+ import type { ToolRunner } from "@toobit_agent/agent-toobitkit-core";
2
+ import type { CliParsed } from "../parser.js";
3
+ import { formatJson } from "../formatter.js";
4
+
5
+ export async function handleFuturesCommand(cli: CliParsed, run: ToolRunner): Promise<void> {
6
+ const f = cli.flags;
7
+ let result;
8
+
9
+ switch (cli.subcommand) {
10
+ case "place":
11
+ result = await run("futures_place_order", {
12
+ symbol: f.symbol,
13
+ side: f.side,
14
+ orderType: f.orderType ?? f.type ?? "MARKET",
15
+ quantity: f.quantity,
16
+ price: f.price,
17
+ leverage: f.leverage,
18
+ });
19
+ break;
20
+ case "cancel":
21
+ result = await run("futures_cancel_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
22
+ break;
23
+ case "cancel-all":
24
+ result = await run("futures_cancel_all_orders", { symbol: f.symbol });
25
+ break;
26
+ case "amend":
27
+ result = await run("futures_amend_order", { orderId: f.orderId, quantity: f.quantity, price: f.price });
28
+ break;
29
+ case "get":
30
+ result = await run("futures_get_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
31
+ break;
32
+ case "orders":
33
+ case "open-orders":
34
+ result = await run("futures_get_open_orders", { symbol: f.symbol });
35
+ break;
36
+ case "history":
37
+ result = await run("futures_get_history_orders", {
38
+ symbol: f.symbol,
39
+ limit: f.limit ? Number(f.limit) : undefined,
40
+ });
41
+ break;
42
+ case "positions":
43
+ result = await run("futures_get_positions", { symbol: f.symbol });
44
+ break;
45
+ case "history-positions":
46
+ result = await run("futures_get_history_positions", { symbol: f.symbol });
47
+ break;
48
+ case "leverage":
49
+ if (f.leverage) {
50
+ result = await run("futures_set_leverage", { symbol: f.symbol, leverage: Number(f.leverage) });
51
+ } else {
52
+ result = await run("futures_get_leverage", { symbol: f.symbol });
53
+ }
54
+ break;
55
+ case "margin-type":
56
+ result = await run("futures_set_margin_type", { symbol: f.symbol, marginType: f.marginType });
57
+ break;
58
+ case "flash-close":
59
+ result = await run("futures_flash_close", { symbol: f.symbol, side: f.side });
60
+ break;
61
+ case "balance":
62
+ result = await run("futures_get_balance", {});
63
+ break;
64
+ case "fills":
65
+ result = await run("futures_get_fills", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : undefined });
66
+ break;
67
+ case "pnl":
68
+ result = await run("futures_get_today_pnl", {});
69
+ break;
70
+ case "commission":
71
+ result = await run("futures_get_commission_rate", { symbol: f.symbol });
72
+ break;
73
+ default:
74
+ process.stdout.write(`Unknown futures subcommand: ${cli.subcommand}\nAvailable: place, cancel, cancel-all, amend, get, orders, history, positions, history-positions, leverage, margin-type, flash-close, balance, fills, pnl, commission\n`);
75
+ return;
76
+ }
77
+
78
+ process.stdout.write(formatJson(result, cli.json) + "\n");
79
+ }
@@ -0,0 +1,71 @@
1
+ import type { ToolRunner } from "@toobit_agent/agent-toobitkit-core";
2
+ import type { CliParsed } from "../parser.js";
3
+ import { formatJson } from "../formatter.js";
4
+
5
+ export async function handleMarketCommand(cli: CliParsed, run: ToolRunner): Promise<void> {
6
+ const f = cli.flags;
7
+ let result;
8
+
9
+ switch (cli.subcommand) {
10
+ case "time":
11
+ result = await run("market_get_server_time", {});
12
+ break;
13
+ case "info":
14
+ result = await run("market_get_exchange_info", {});
15
+ break;
16
+ case "ticker":
17
+ result = await run("market_get_ticker_price", { symbol: f.symbol });
18
+ break;
19
+ case "ticker-24hr":
20
+ result = await run("market_get_ticker_24hr", { symbol: f.symbol });
21
+ break;
22
+ case "depth":
23
+ result = await run("market_get_depth", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : undefined });
24
+ break;
25
+ case "trades":
26
+ result = await run("market_get_trades", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : undefined });
27
+ break;
28
+ case "klines":
29
+ case "candles":
30
+ result = await run("market_get_klines", {
31
+ symbol: f.symbol,
32
+ interval: f.interval ?? f.bar ?? "1h",
33
+ limit: f.limit ? Number(f.limit) : undefined,
34
+ startTime: f.startTime ? Number(f.startTime) : undefined,
35
+ endTime: f.endTime ? Number(f.endTime) : undefined,
36
+ });
37
+ break;
38
+ case "book-ticker":
39
+ result = await run("market_get_book_ticker", { symbol: f.symbol });
40
+ break;
41
+ case "mark-price":
42
+ result = await run("market_get_mark_price", { symbol: f.symbol });
43
+ break;
44
+ case "funding-rate":
45
+ result = await run("market_get_funding_rate", { symbol: f.symbol });
46
+ break;
47
+ case "funding-rate-history":
48
+ result = await run("market_get_funding_rate_history", {
49
+ symbol: f.symbol,
50
+ limit: f.limit ? Number(f.limit) : undefined,
51
+ });
52
+ break;
53
+ case "open-interest":
54
+ result = await run("market_get_open_interest", { symbol: f.symbol });
55
+ break;
56
+ case "index":
57
+ result = await run("market_get_index_price", { symbol: f.symbol });
58
+ break;
59
+ case "contract-ticker":
60
+ result = await run("market_get_contract_ticker_24hr", { symbol: f.symbol });
61
+ break;
62
+ case "long-short-ratio":
63
+ result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
64
+ break;
65
+ 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`);
67
+ return;
68
+ }
69
+
70
+ process.stdout.write(formatJson(result, cli.json) + "\n");
71
+ }
@@ -0,0 +1,52 @@
1
+ import type { ToolRunner } from "@toobit_agent/agent-toobitkit-core";
2
+ import type { CliParsed } from "../parser.js";
3
+ import { formatJson } from "../formatter.js";
4
+
5
+ export async function handleSpotCommand(cli: CliParsed, run: ToolRunner): Promise<void> {
6
+ const f = cli.flags;
7
+ let result;
8
+
9
+ switch (cli.subcommand) {
10
+ case "place":
11
+ result = await run("spot_place_order", {
12
+ symbol: f.symbol,
13
+ side: f.side,
14
+ type: f.type ?? "MARKET",
15
+ quantity: f.quantity,
16
+ price: f.price,
17
+ });
18
+ break;
19
+ case "cancel":
20
+ result = await run("spot_cancel_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
21
+ break;
22
+ case "cancel-all":
23
+ result = await run("spot_cancel_open_orders", { symbol: f.symbol });
24
+ break;
25
+ case "get":
26
+ result = await run("spot_get_order", { orderId: f.orderId, clientOrderId: f.clientOrderId });
27
+ break;
28
+ case "open-orders":
29
+ case "orders":
30
+ result = await run("spot_get_open_orders", { symbol: f.symbol, limit: f.limit ? Number(f.limit) : undefined });
31
+ break;
32
+ case "history":
33
+ result = await run("spot_get_trade_orders", {
34
+ symbol: f.symbol,
35
+ limit: f.limit ? Number(f.limit) : undefined,
36
+ startTime: f.startTime ? Number(f.startTime) : undefined,
37
+ endTime: f.endTime ? Number(f.endTime) : undefined,
38
+ });
39
+ break;
40
+ case "fills":
41
+ result = await run("spot_get_fills", {
42
+ symbol: f.symbol,
43
+ limit: f.limit ? Number(f.limit) : undefined,
44
+ });
45
+ break;
46
+ default:
47
+ process.stdout.write(`Unknown spot subcommand: ${cli.subcommand}\nAvailable: place, cancel, cancel-all, get, orders, open-orders, history, fills\n`);
48
+ return;
49
+ }
50
+
51
+ process.stdout.write(formatJson(result, cli.json) + "\n");
52
+ }
@@ -0,0 +1,22 @@
1
+ export function formatJson(data: unknown, json: boolean): string {
2
+ if (json) return JSON.stringify(data, null, 2);
3
+ if (data === null || data === undefined) return "No data.";
4
+ if (typeof data !== "object") return String(data);
5
+ return JSON.stringify(data, null, 2);
6
+ }
7
+
8
+ export function formatTable(rows: Record<string, unknown>[], columns?: string[]): string {
9
+ if (rows.length === 0) return "No data.";
10
+ const keys = columns ?? Object.keys(rows[0]);
11
+ const widths = keys.map((k) =>
12
+ Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length)),
13
+ );
14
+
15
+ const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
16
+ const separator = widths.map((w) => "-".repeat(w)).join(" ");
17
+ const body = rows.map((row) =>
18
+ keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" "),
19
+ ).join("\n");
20
+
21
+ return `${header}\n${separator}\n${body}`;
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,93 @@
1
+ import {
2
+ loadConfig,
3
+ ToobitRestClient,
4
+ createToolRunner,
5
+ toToolErrorPayload,
6
+ configFilePath,
7
+ } from "@toobit_agent/agent-toobitkit-core";
8
+ import { parseCli } from "./parser.js";
9
+ import { handleMarketCommand } from "./commands/market.js";
10
+ import { handleSpotCommand } from "./commands/spot.js";
11
+ import { handleFuturesCommand } from "./commands/futures.js";
12
+ import { handleAccountCommand } from "./commands/account.js";
13
+ import { handleConfigCommand } from "./commands/config.js";
14
+
15
+ function printHelp(): void {
16
+ const help = `
17
+ Toobit Trade CLI — Trade from your terminal
18
+
19
+ Usage: toobit <command> <subcommand> [options]
20
+
21
+ Commands:
22
+ market Market data (ticker, depth, klines, funding-rate, etc.)
23
+ spot Spot trading (place, cancel, orders, fills)
24
+ futures USDT-M futures (place, cancel, positions, leverage, etc.)
25
+ account Account info (balance, deposits, withdrawals, audit)
26
+ config Configuration management (init, show, list)
27
+
28
+ Global Options:
29
+ --profile <name> Profile from ${configFilePath()}
30
+ --json Output raw JSON
31
+ --read-only Disable write operations
32
+ --help Show this help
33
+
34
+ Examples:
35
+ toobit market ticker --symbol BTCUSDT
36
+ toobit market candles --symbol BTCUSDT --interval 1h --limit 10
37
+ toobit market funding-rate --symbol BTCUSDT
38
+ toobit spot place --symbol BTCUSDT --side BUY --type MARKET --quantity 0.001
39
+ toobit futures positions --symbol BTCUSDT
40
+ toobit account balance
41
+ toobit config init
42
+ `;
43
+ process.stdout.write(help);
44
+ }
45
+
46
+ async function main(): Promise<void> {
47
+ const cli = parseCli();
48
+
49
+ if (cli.command === "help" || cli.flags.help) {
50
+ printHelp();
51
+ return;
52
+ }
53
+
54
+ if (cli.command === "config") {
55
+ await handleConfigCommand(cli);
56
+ return;
57
+ }
58
+
59
+ const config = loadConfig({
60
+ modules: "all",
61
+ profile: cli.profile,
62
+ readOnly: cli.readOnly,
63
+ userAgent: "toobit-trade-cli/1.0.0",
64
+ sourceTag: "CLI",
65
+ });
66
+
67
+ const client = new ToobitRestClient(config);
68
+ const run = createToolRunner(client, config);
69
+
70
+ switch (cli.command) {
71
+ case "market":
72
+ await handleMarketCommand(cli, run);
73
+ break;
74
+ case "spot":
75
+ await handleSpotCommand(cli, run);
76
+ break;
77
+ case "futures":
78
+ await handleFuturesCommand(cli, run);
79
+ break;
80
+ case "account":
81
+ await handleAccountCommand(cli, run);
82
+ break;
83
+ default:
84
+ process.stdout.write(`Unknown command: ${cli.command}\n`);
85
+ printHelp();
86
+ }
87
+ }
88
+
89
+ main().catch((error: unknown) => {
90
+ const payload = toToolErrorPayload(error);
91
+ process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
92
+ process.exitCode = 1;
93
+ });
package/src/parser.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ export interface CliParsed {
4
+ command: string;
5
+ subcommand: string;
6
+ profile?: string;
7
+ json: boolean;
8
+ readOnly: boolean;
9
+ flags: Record<string, string | boolean | undefined>;
10
+ positionals: string[];
11
+ }
12
+
13
+ export function parseCli(argv = process.argv.slice(2)): CliParsed {
14
+ const STRING_OPTIONS = new Set([
15
+ "profile", "symbol", "interval", "limit", "side", "type",
16
+ "quantity", "price", "orderId", "clientOrderId", "leverage",
17
+ "orderType", "tokenId", "instId", "period", "bar",
18
+ "startTime", "endTime", "marginType",
19
+ ]);
20
+
21
+ let command = "help";
22
+ let subcommand = "";
23
+ const flagArgs: string[] = [];
24
+ let positionalCount = 0;
25
+
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const arg = argv[i];
28
+ if (arg.startsWith("-")) {
29
+ flagArgs.push(arg);
30
+ const optName = arg.replace(/^--?/, "");
31
+ if (STRING_OPTIONS.has(optName) && i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
32
+ flagArgs.push(argv[++i]);
33
+ }
34
+ } else {
35
+ if (positionalCount === 0) command = arg;
36
+ else if (positionalCount === 1) subcommand = arg;
37
+ positionalCount++;
38
+ }
39
+ }
40
+
41
+ const parsed = parseArgs({
42
+ args: flagArgs,
43
+ options: {
44
+ profile: { type: "string" },
45
+ json: { type: "boolean", default: false },
46
+ "read-only": { type: "boolean", default: false },
47
+ symbol: { type: "string" },
48
+ interval: { type: "string" },
49
+ limit: { type: "string" },
50
+ side: { type: "string" },
51
+ type: { type: "string" },
52
+ quantity: { type: "string" },
53
+ price: { type: "string" },
54
+ orderId: { type: "string" },
55
+ clientOrderId: { type: "string" },
56
+ leverage: { type: "string" },
57
+ orderType: { type: "string" },
58
+ tokenId: { type: "string" },
59
+ instId: { type: "string" },
60
+ period: { type: "string" },
61
+ bar: { type: "string" },
62
+ startTime: { type: "string" },
63
+ endTime: { type: "string" },
64
+ marginType: { type: "string" },
65
+ },
66
+ strict: false,
67
+ allowPositionals: true,
68
+ });
69
+
70
+ return {
71
+ command,
72
+ subcommand,
73
+ profile: parsed.values.profile as string | undefined,
74
+ json: (parsed.values.json as boolean) ?? false,
75
+ readOnly: (parsed.values["read-only"] as boolean) ?? false,
76
+ flags: parsed.values as Record<string, string | boolean | undefined>,
77
+ positionals: parsed.positionals as string[],
78
+ };
79
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"]
8
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["esm"],
6
+ dts: true,
7
+ clean: true,
8
+ banner: { js: "#!/usr/bin/env node" },
9
+ });