toobit-trade-cli 1.0.0 → 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 +2260 -16
- package/package.json +2 -2
- package/src/commands/market.ts +10 -1
- package/src/formatter.ts +39 -1
- package/tsup.config.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -1,13 +1,2206 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "
|
|
3
|
+
// ../core/dist/index.js
|
|
4
|
+
import { createHmac } from "crypto";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
9
|
+
import { join as join2, dirname } from "path";
|
|
10
|
+
import { homedir as homedir2 } from "os";
|
|
11
|
+
import { parse, stringify } from "smol-toml";
|
|
12
|
+
function getTimestamp() {
|
|
13
|
+
return Date.now();
|
|
14
|
+
}
|
|
15
|
+
function signToobitPayload(queryString, secretKey) {
|
|
16
|
+
return createHmac("sha256", secretKey).update(queryString).digest("hex");
|
|
17
|
+
}
|
|
18
|
+
var ToobitMcpError = class extends Error {
|
|
19
|
+
type;
|
|
20
|
+
code;
|
|
21
|
+
suggestion;
|
|
22
|
+
endpoint;
|
|
23
|
+
constructor(type, message, options) {
|
|
24
|
+
super(message, options?.cause ? { cause: options.cause } : void 0);
|
|
25
|
+
this.name = type;
|
|
26
|
+
this.type = type;
|
|
27
|
+
this.code = options?.code;
|
|
28
|
+
this.suggestion = options?.suggestion;
|
|
29
|
+
this.endpoint = options?.endpoint;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var ConfigError = class extends ToobitMcpError {
|
|
33
|
+
constructor(message, suggestion) {
|
|
34
|
+
super("ConfigError", message, { suggestion });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var ValidationError = class extends ToobitMcpError {
|
|
38
|
+
constructor(message, suggestion) {
|
|
39
|
+
super("ValidationError", message, { suggestion });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var RateLimitError = class extends ToobitMcpError {
|
|
43
|
+
constructor(message, suggestion, endpoint) {
|
|
44
|
+
super("RateLimitError", message, { suggestion, endpoint });
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var AuthenticationError = class extends ToobitMcpError {
|
|
48
|
+
constructor(message, suggestion, endpoint) {
|
|
49
|
+
super("AuthenticationError", message, { suggestion, endpoint });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var ToobitApiError = class extends ToobitMcpError {
|
|
53
|
+
constructor(message, options) {
|
|
54
|
+
super("ToobitApiError", message, options);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var NetworkError = class extends ToobitMcpError {
|
|
58
|
+
constructor(message, endpoint, cause) {
|
|
59
|
+
super("NetworkError", message, {
|
|
60
|
+
endpoint,
|
|
61
|
+
cause,
|
|
62
|
+
suggestion: "Please check network connectivity and retry the request in a few seconds."
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function toToolErrorPayload(error, fallbackEndpoint) {
|
|
67
|
+
if (error instanceof ToobitMcpError) {
|
|
68
|
+
return {
|
|
69
|
+
error: true,
|
|
70
|
+
type: error.type,
|
|
71
|
+
code: error.code,
|
|
72
|
+
message: error.message,
|
|
73
|
+
suggestion: error.suggestion,
|
|
74
|
+
endpoint: error.endpoint ?? fallbackEndpoint,
|
|
75
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
return {
|
|
80
|
+
error: true,
|
|
81
|
+
type: "InternalError",
|
|
82
|
+
message,
|
|
83
|
+
suggestion: "Unexpected server error. Check tool arguments and retry.",
|
|
84
|
+
endpoint: fallbackEndpoint,
|
|
85
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function sleep(ms) {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
setTimeout(resolve, ms);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
var RateLimiter = class {
|
|
94
|
+
buckets = /* @__PURE__ */ new Map();
|
|
95
|
+
maxWaitMs;
|
|
96
|
+
constructor(maxWaitMs = 3e4) {
|
|
97
|
+
this.maxWaitMs = maxWaitMs;
|
|
98
|
+
}
|
|
99
|
+
async consume(config, amount = 1) {
|
|
100
|
+
const bucket = this.getBucket(config);
|
|
101
|
+
this.refill(bucket);
|
|
102
|
+
if (bucket.tokens >= amount) {
|
|
103
|
+
bucket.tokens -= amount;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const missing = amount - bucket.tokens;
|
|
107
|
+
const secondsToWait = missing / bucket.refillPerSecond;
|
|
108
|
+
const waitMs = Math.ceil(secondsToWait * 1e3);
|
|
109
|
+
if (waitMs > this.maxWaitMs) {
|
|
110
|
+
throw new RateLimitError(
|
|
111
|
+
`Client-side rate limit reached for ${config.key}. Required wait ${waitMs}ms exceeds allowed max ${this.maxWaitMs}ms.`,
|
|
112
|
+
"Reduce tool call frequency or retry later."
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
await sleep(waitMs);
|
|
116
|
+
this.refill(bucket);
|
|
117
|
+
if (bucket.tokens < amount) {
|
|
118
|
+
throw new RateLimitError(
|
|
119
|
+
`Rate limiter failed to acquire enough tokens for ${config.key}.`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
bucket.tokens -= amount;
|
|
123
|
+
}
|
|
124
|
+
getBucket(config) {
|
|
125
|
+
const existing = this.buckets.get(config.key);
|
|
126
|
+
if (existing) {
|
|
127
|
+
if (existing.capacity !== config.capacity || existing.refillPerSecond !== config.refillPerSecond) {
|
|
128
|
+
existing.capacity = config.capacity;
|
|
129
|
+
existing.refillPerSecond = config.refillPerSecond;
|
|
130
|
+
existing.tokens = Math.min(existing.tokens, config.capacity);
|
|
131
|
+
}
|
|
132
|
+
return existing;
|
|
133
|
+
}
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const created = {
|
|
136
|
+
tokens: config.capacity,
|
|
137
|
+
lastRefillMs: now,
|
|
138
|
+
capacity: config.capacity,
|
|
139
|
+
refillPerSecond: config.refillPerSecond
|
|
140
|
+
};
|
|
141
|
+
this.buckets.set(config.key, created);
|
|
142
|
+
return created;
|
|
143
|
+
}
|
|
144
|
+
refill(bucket) {
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
const elapsedMs = now - bucket.lastRefillMs;
|
|
147
|
+
if (elapsedMs <= 0) return;
|
|
148
|
+
const refillTokens = elapsedMs / 1e3 * bucket.refillPerSecond;
|
|
149
|
+
bucket.tokens = Math.min(bucket.capacity, bucket.tokens + refillTokens);
|
|
150
|
+
bucket.lastRefillMs = now;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var TOOBIT_CODE_BEHAVIORS = {
|
|
154
|
+
"-1000": { retry: true, suggestion: "Unknown error. Retry after a delay." },
|
|
155
|
+
"-1001": { retry: true, suggestion: "Disconnected / internal error. Retry." },
|
|
156
|
+
"-1003": { retry: true, suggestion: "Too many requests. Back off and retry." },
|
|
157
|
+
"-1006": { retry: true, suggestion: "Unexpected response. Retry later." },
|
|
158
|
+
"-1007": { retry: true, suggestion: "Timeout. Retry after a delay." },
|
|
159
|
+
"-1016": { retry: true, suggestion: "Service shutting down. Retry later." },
|
|
160
|
+
"-1002": { retry: false, suggestion: "Unauthorized. Check API key permissions." },
|
|
161
|
+
"-1015": { retry: false, suggestion: "Too many orders. Reduce order frequency." },
|
|
162
|
+
"-1020": { retry: false, suggestion: "Unsupported operation." },
|
|
163
|
+
"-1021": { retry: false, suggestion: "Invalid timestamp. Check system clock sync." },
|
|
164
|
+
"-1022": { retry: false, suggestion: "Invalid signature. Check API key and secret." },
|
|
165
|
+
"-2010": { retry: false, suggestion: "New order rejected. Check order parameters." },
|
|
166
|
+
"-2011": { retry: false, suggestion: "Cancel rejected. Order may already be filled." },
|
|
167
|
+
"-2013": { retry: false, suggestion: "Order does not exist." },
|
|
168
|
+
"-2014": { retry: false, suggestion: "Bad API key format." },
|
|
169
|
+
"-2015": { retry: false, suggestion: "Invalid API key, IP, or permission." },
|
|
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." }
|
|
173
|
+
};
|
|
174
|
+
function isDefined(value) {
|
|
175
|
+
return value !== void 0 && value !== null;
|
|
176
|
+
}
|
|
177
|
+
function stringifyQueryValue(value) {
|
|
178
|
+
if (Array.isArray(value)) return value.map(String).join(",");
|
|
179
|
+
return String(value);
|
|
180
|
+
}
|
|
181
|
+
function buildQueryString(query) {
|
|
182
|
+
if (!query) return "";
|
|
183
|
+
const entries = Object.entries(query).filter(([, v]) => isDefined(v));
|
|
184
|
+
if (entries.length === 0) return "";
|
|
185
|
+
return entries.map(([k, v]) => `${k}=${stringifyQueryValue(v)}`).join("&");
|
|
186
|
+
}
|
|
187
|
+
var ToobitRestClient = class {
|
|
188
|
+
config;
|
|
189
|
+
rateLimiter = new RateLimiter();
|
|
190
|
+
constructor(config) {
|
|
191
|
+
this.config = config;
|
|
192
|
+
}
|
|
193
|
+
async publicGet(path4, query, rateLimit) {
|
|
194
|
+
return this.request({ method: "GET", path: path4, auth: "public", query, rateLimit });
|
|
195
|
+
}
|
|
196
|
+
async privateGet(path4, query, rateLimit) {
|
|
197
|
+
return this.request({ method: "GET", path: path4, auth: "private", query, rateLimit });
|
|
198
|
+
}
|
|
199
|
+
async privatePost(path4, body, rateLimit) {
|
|
200
|
+
return this.request({ method: "POST", path: path4, auth: "private", body, rateLimit });
|
|
201
|
+
}
|
|
202
|
+
async privateDelete(path4, query, rateLimit) {
|
|
203
|
+
return this.request({ method: "DELETE", path: path4, auth: "private", query, rateLimit });
|
|
204
|
+
}
|
|
205
|
+
async request(config) {
|
|
206
|
+
if (config.rateLimit) {
|
|
207
|
+
await this.rateLimiter.consume(config.rateLimit);
|
|
208
|
+
}
|
|
209
|
+
const timestamp = getTimestamp();
|
|
210
|
+
let allParams = { ...config.query ?? {} };
|
|
211
|
+
const isBodyMethod = config.method === "POST" || config.method === "PUT";
|
|
212
|
+
if (config.body) {
|
|
213
|
+
Object.assign(allParams, config.body);
|
|
214
|
+
}
|
|
215
|
+
if (config.auth === "private") {
|
|
216
|
+
if (!this.config.hasAuth) {
|
|
217
|
+
throw new ConfigError(
|
|
218
|
+
"Private endpoint requires API credentials.",
|
|
219
|
+
"Configure TOOBIT_API_KEY and TOOBIT_SECRET_KEY."
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
allParams.timestamp = String(timestamp);
|
|
223
|
+
const signPayload = buildQueryString(allParams);
|
|
224
|
+
const signature = signToobitPayload(signPayload, this.config.secretKey);
|
|
225
|
+
allParams.signature = signature;
|
|
226
|
+
}
|
|
227
|
+
const paramString = buildQueryString(allParams);
|
|
228
|
+
let url;
|
|
229
|
+
let fetchBody;
|
|
230
|
+
if (isBodyMethod && config.auth === "private") {
|
|
231
|
+
url = `${this.config.baseUrl}${config.path}`;
|
|
232
|
+
fetchBody = paramString || void 0;
|
|
233
|
+
} else {
|
|
234
|
+
const requestPath = paramString ? `${config.path}?${paramString}` : config.path;
|
|
235
|
+
url = `${this.config.baseUrl}${requestPath}`;
|
|
236
|
+
fetchBody = void 0;
|
|
237
|
+
}
|
|
238
|
+
const headers = new Headers({
|
|
239
|
+
Accept: "application/json"
|
|
240
|
+
});
|
|
241
|
+
if (isBodyMethod) {
|
|
242
|
+
headers.set("Content-Type", "application/x-www-form-urlencoded");
|
|
243
|
+
}
|
|
244
|
+
if (this.config.userAgent) {
|
|
245
|
+
headers.set("User-Agent", this.config.userAgent);
|
|
246
|
+
}
|
|
247
|
+
if (config.auth === "private" && this.config.apiKey) {
|
|
248
|
+
headers.set("X-BB-APIKEY", this.config.apiKey);
|
|
249
|
+
}
|
|
250
|
+
let response;
|
|
251
|
+
try {
|
|
252
|
+
response = await fetch(url, {
|
|
253
|
+
method: config.method,
|
|
254
|
+
headers,
|
|
255
|
+
body: fetchBody,
|
|
256
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
257
|
+
});
|
|
258
|
+
} catch (error) {
|
|
259
|
+
throw new NetworkError(
|
|
260
|
+
`Failed to call Toobit endpoint ${config.method} ${config.path}.`,
|
|
261
|
+
`${config.method} ${config.path}`,
|
|
262
|
+
error
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
const rawText = await response.text();
|
|
266
|
+
let parsed;
|
|
267
|
+
try {
|
|
268
|
+
parsed = rawText ? JSON.parse(rawText) : {};
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
const preview = rawText.slice(0, 160).replace(/\s+/g, " ").trim();
|
|
272
|
+
throw new ToobitApiError(
|
|
273
|
+
`HTTP ${response.status} from Toobit: ${preview || "Non-JSON response"}`,
|
|
274
|
+
{ code: String(response.status), endpoint: `${config.method} ${config.path}` }
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
throw new NetworkError(
|
|
278
|
+
`Toobit returned non-JSON response for ${config.method} ${config.path}.`,
|
|
279
|
+
`${config.method} ${config.path}`,
|
|
280
|
+
error
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (response.status === 429) {
|
|
284
|
+
throw new RateLimitError(
|
|
285
|
+
"Rate limited by Toobit. Back off and retry.",
|
|
286
|
+
"Reduce request frequency.",
|
|
287
|
+
`${config.method} ${config.path}`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const responseCode = parsed.code;
|
|
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) {
|
|
295
|
+
const codeStr = String(responseCode);
|
|
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") {
|
|
299
|
+
throw new AuthenticationError(
|
|
300
|
+
message,
|
|
301
|
+
behavior?.suggestion ?? "Check API key, secret key and permissions.",
|
|
302
|
+
endpoint
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
if (codeStr === "-1003") {
|
|
306
|
+
throw new RateLimitError(message, "Too many requests. Back off.", endpoint);
|
|
307
|
+
}
|
|
308
|
+
throw new ToobitApiError(message, {
|
|
309
|
+
code: codeStr,
|
|
310
|
+
endpoint,
|
|
311
|
+
suggestion: behavior?.suggestion
|
|
312
|
+
});
|
|
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
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
endpoint: `${config.method} ${config.path}`,
|
|
328
|
+
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
329
|
+
data: parsed,
|
|
330
|
+
raw: parsed
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var TOOBIT_API_BASE_URL = "https://api.toobit.com";
|
|
335
|
+
var DEFAULT_SOURCE_TAG = "MCP";
|
|
336
|
+
var MODULES = [
|
|
337
|
+
"market",
|
|
338
|
+
"spot",
|
|
339
|
+
"futures",
|
|
340
|
+
"account"
|
|
341
|
+
];
|
|
342
|
+
var DEFAULT_MODULES = ["spot", "futures", "account"];
|
|
343
|
+
function asRecord(value) {
|
|
344
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
345
|
+
return value;
|
|
346
|
+
}
|
|
347
|
+
function readString(args, key) {
|
|
348
|
+
const value = args[key];
|
|
349
|
+
if (value === void 0 || value === null) return void 0;
|
|
350
|
+
if (typeof value !== "string") throw new ValidationError(`Parameter "${key}" must be a string.`);
|
|
351
|
+
return value;
|
|
352
|
+
}
|
|
353
|
+
function readNumber(args, key) {
|
|
354
|
+
const value = args[key];
|
|
355
|
+
if (value === void 0 || value === null) return void 0;
|
|
356
|
+
if (typeof value !== "number" || Number.isNaN(value)) throw new ValidationError(`Parameter "${key}" must be a number.`);
|
|
357
|
+
return value;
|
|
358
|
+
}
|
|
359
|
+
function requireString(args, key) {
|
|
360
|
+
const value = readString(args, key);
|
|
361
|
+
if (!value || value.length === 0) throw new ValidationError(`Missing required parameter "${key}".`);
|
|
362
|
+
return value;
|
|
363
|
+
}
|
|
364
|
+
function compactObject(object) {
|
|
365
|
+
const next = {};
|
|
366
|
+
for (const [key, value] of Object.entries(object)) {
|
|
367
|
+
if (value !== void 0 && value !== null) next[key] = value;
|
|
368
|
+
}
|
|
369
|
+
return next;
|
|
370
|
+
}
|
|
371
|
+
var TOOBIT_CANDLE_BARS = [
|
|
372
|
+
"1m",
|
|
373
|
+
"3m",
|
|
374
|
+
"5m",
|
|
375
|
+
"15m",
|
|
376
|
+
"30m",
|
|
377
|
+
"1h",
|
|
378
|
+
"2h",
|
|
379
|
+
"4h",
|
|
380
|
+
"6h",
|
|
381
|
+
"8h",
|
|
382
|
+
"12h",
|
|
383
|
+
"1d",
|
|
384
|
+
"3d",
|
|
385
|
+
"1w",
|
|
386
|
+
"1M"
|
|
387
|
+
];
|
|
388
|
+
function publicRateLimit(key, rps = 20) {
|
|
389
|
+
return { key: `public:${key}`, capacity: rps, refillPerSecond: rps };
|
|
390
|
+
}
|
|
391
|
+
function privateRateLimit(key, rps = 10) {
|
|
392
|
+
return { key: `private:${key}`, capacity: rps, refillPerSecond: rps };
|
|
393
|
+
}
|
|
394
|
+
function normalize(response) {
|
|
395
|
+
return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
|
|
396
|
+
}
|
|
397
|
+
function registerAccountTools() {
|
|
398
|
+
return [
|
|
399
|
+
{
|
|
400
|
+
name: "account_get_info",
|
|
401
|
+
module: "account",
|
|
402
|
+
description: "Get spot account information (balances for all assets). Private endpoint. Rate limit: 20 req/s.",
|
|
403
|
+
isWrite: false,
|
|
404
|
+
inputSchema: { type: "object", properties: {} },
|
|
405
|
+
handler: async (_rawArgs, context) => {
|
|
406
|
+
const response = await context.client.privateGet(
|
|
407
|
+
"/api/v1/account",
|
|
408
|
+
{},
|
|
409
|
+
privateRateLimit("account_get_info", 20)
|
|
410
|
+
);
|
|
411
|
+
return normalize(response);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
name: "account_get_balance_flow",
|
|
416
|
+
module: "account",
|
|
417
|
+
description: "Get account balance flow (ledger). Private endpoint. Rate limit: 20 req/s.",
|
|
418
|
+
isWrite: false,
|
|
419
|
+
inputSchema: {
|
|
420
|
+
type: "object",
|
|
421
|
+
properties: {
|
|
422
|
+
accountType: { type: "number", description: "1=coin, 2=contract" },
|
|
423
|
+
tokenId: { type: "string" },
|
|
424
|
+
fromFlowId: { type: "string" },
|
|
425
|
+
endFlowId: { type: "string" },
|
|
426
|
+
startTime: { type: "number" },
|
|
427
|
+
endTime: { type: "number" },
|
|
428
|
+
limit: { type: "number" }
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
handler: async (rawArgs, context) => {
|
|
432
|
+
const args = asRecord(rawArgs);
|
|
433
|
+
const response = await context.client.privateGet(
|
|
434
|
+
"/api/v1/account/balanceFlow",
|
|
435
|
+
compactObject({
|
|
436
|
+
accountType: readNumber(args, "accountType"),
|
|
437
|
+
tokenId: readString(args, "tokenId"),
|
|
438
|
+
fromFlowId: readString(args, "fromFlowId"),
|
|
439
|
+
endFlowId: readString(args, "endFlowId"),
|
|
440
|
+
startTime: readNumber(args, "startTime"),
|
|
441
|
+
endTime: readNumber(args, "endTime"),
|
|
442
|
+
limit: readNumber(args, "limit")
|
|
443
|
+
}),
|
|
444
|
+
privateRateLimit("account_get_balance_flow", 20)
|
|
445
|
+
);
|
|
446
|
+
return normalize(response);
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "account_get_sub_accounts",
|
|
451
|
+
module: "account",
|
|
452
|
+
description: "Get sub-account list. Private endpoint. Rate limit: 10 req/s.",
|
|
453
|
+
isWrite: false,
|
|
454
|
+
inputSchema: { type: "object", properties: {} },
|
|
455
|
+
handler: async (_rawArgs, context) => {
|
|
456
|
+
const response = await context.client.privateGet(
|
|
457
|
+
"/api/v1/account/subAccount",
|
|
458
|
+
{},
|
|
459
|
+
privateRateLimit("account_get_sub_accounts", 10)
|
|
460
|
+
);
|
|
461
|
+
return normalize(response);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "account_sub_transfer",
|
|
466
|
+
module: "account",
|
|
467
|
+
description: "Transfer funds between main and sub accounts. [CAUTION] Private endpoint. Rate limit: 5 req/s.",
|
|
468
|
+
isWrite: true,
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: {
|
|
472
|
+
fromAccountType: { type: "number", description: "1=coin, 2=contract" },
|
|
473
|
+
toAccountType: { type: "number", description: "1=coin, 2=contract" },
|
|
474
|
+
tokenId: { type: "string", description: "e.g. USDT" },
|
|
475
|
+
amount: { type: "string" },
|
|
476
|
+
subAccountId: { type: "string" }
|
|
477
|
+
},
|
|
478
|
+
required: ["tokenId", "amount"]
|
|
479
|
+
},
|
|
480
|
+
handler: async (rawArgs, context) => {
|
|
481
|
+
const args = asRecord(rawArgs);
|
|
482
|
+
const response = await context.client.privatePost(
|
|
483
|
+
"/api/v1/subAccount/transfer",
|
|
484
|
+
compactObject({
|
|
485
|
+
fromAccountType: readNumber(args, "fromAccountType"),
|
|
486
|
+
toAccountType: readNumber(args, "toAccountType"),
|
|
487
|
+
tokenId: requireString(args, "tokenId"),
|
|
488
|
+
amount: requireString(args, "amount"),
|
|
489
|
+
subAccountId: readString(args, "subAccountId")
|
|
490
|
+
}),
|
|
491
|
+
privateRateLimit("account_sub_transfer", 5)
|
|
492
|
+
);
|
|
493
|
+
return normalize(response);
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: "account_check_api_key",
|
|
498
|
+
module: "account",
|
|
499
|
+
description: "Check API key type and permissions. Private endpoint. Rate limit: 20 req/s.",
|
|
500
|
+
isWrite: false,
|
|
501
|
+
inputSchema: { type: "object", properties: {} },
|
|
502
|
+
handler: async (_rawArgs, context) => {
|
|
503
|
+
const response = await context.client.privateGet(
|
|
504
|
+
"/api/v1/account/checkApiKey",
|
|
505
|
+
{},
|
|
506
|
+
privateRateLimit("account_check_api_key", 20)
|
|
507
|
+
);
|
|
508
|
+
return normalize(response);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: "account_withdraw",
|
|
513
|
+
module: "account",
|
|
514
|
+
description: "Submit a withdrawal request. [CAUTION] Moves real funds. Private endpoint. Rate limit: 5 req/s.",
|
|
515
|
+
isWrite: true,
|
|
516
|
+
inputSchema: {
|
|
517
|
+
type: "object",
|
|
518
|
+
properties: {
|
|
519
|
+
tokenId: { type: "string", description: "e.g. USDT" },
|
|
520
|
+
address: { type: "string" },
|
|
521
|
+
addressExt: { type: "string", description: "Memo/tag if required" },
|
|
522
|
+
chainType: { type: "string" },
|
|
523
|
+
withdrawQuantity: { type: "string" },
|
|
524
|
+
clientOrderId: { type: "string" }
|
|
525
|
+
},
|
|
526
|
+
required: ["tokenId", "address", "chainType", "withdrawQuantity"]
|
|
527
|
+
},
|
|
528
|
+
handler: async (rawArgs, context) => {
|
|
529
|
+
const args = asRecord(rawArgs);
|
|
530
|
+
const response = await context.client.privatePost(
|
|
531
|
+
"/api/v1/account/withdraw",
|
|
532
|
+
compactObject({
|
|
533
|
+
tokenId: requireString(args, "tokenId"),
|
|
534
|
+
address: requireString(args, "address"),
|
|
535
|
+
addressExt: readString(args, "addressExt"),
|
|
536
|
+
chainType: requireString(args, "chainType"),
|
|
537
|
+
withdrawQuantity: requireString(args, "withdrawQuantity"),
|
|
538
|
+
clientOrderId: readString(args, "clientOrderId")
|
|
539
|
+
}),
|
|
540
|
+
privateRateLimit("account_withdraw", 5)
|
|
541
|
+
);
|
|
542
|
+
return normalize(response);
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "account_get_withdraw_orders",
|
|
547
|
+
module: "account",
|
|
548
|
+
description: "Get withdrawal records. Private endpoint. Rate limit: 20 req/s.",
|
|
549
|
+
isWrite: false,
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: "object",
|
|
552
|
+
properties: {
|
|
553
|
+
tokenId: { type: "string" },
|
|
554
|
+
startTime: { type: "number" },
|
|
555
|
+
endTime: { type: "number" },
|
|
556
|
+
fromId: { type: "string" },
|
|
557
|
+
limit: { type: "number" }
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
handler: async (rawArgs, context) => {
|
|
561
|
+
const args = asRecord(rawArgs);
|
|
562
|
+
const response = await context.client.privateGet(
|
|
563
|
+
"/api/v1/account/withdrawOrders",
|
|
564
|
+
compactObject({
|
|
565
|
+
tokenId: readString(args, "tokenId"),
|
|
566
|
+
startTime: readNumber(args, "startTime"),
|
|
567
|
+
endTime: readNumber(args, "endTime"),
|
|
568
|
+
fromId: readString(args, "fromId"),
|
|
569
|
+
limit: readNumber(args, "limit")
|
|
570
|
+
}),
|
|
571
|
+
privateRateLimit("account_get_withdraw_orders", 20)
|
|
572
|
+
);
|
|
573
|
+
return normalize(response);
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
name: "account_get_deposit_address",
|
|
578
|
+
module: "account",
|
|
579
|
+
description: "Get deposit address for a token. Private endpoint. Rate limit: 20 req/s.",
|
|
580
|
+
isWrite: false,
|
|
581
|
+
inputSchema: {
|
|
582
|
+
type: "object",
|
|
583
|
+
properties: {
|
|
584
|
+
tokenId: { type: "string", description: "e.g. USDT" },
|
|
585
|
+
chainType: { type: "string" }
|
|
586
|
+
},
|
|
587
|
+
required: ["tokenId"]
|
|
588
|
+
},
|
|
589
|
+
handler: async (rawArgs, context) => {
|
|
590
|
+
const args = asRecord(rawArgs);
|
|
591
|
+
const response = await context.client.privateGet(
|
|
592
|
+
"/api/v1/account/deposit/address",
|
|
593
|
+
compactObject({
|
|
594
|
+
tokenId: requireString(args, "tokenId"),
|
|
595
|
+
chainType: readString(args, "chainType")
|
|
596
|
+
}),
|
|
597
|
+
privateRateLimit("account_get_deposit_address", 20)
|
|
598
|
+
);
|
|
599
|
+
return normalize(response);
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
name: "account_get_deposit_orders",
|
|
604
|
+
module: "account",
|
|
605
|
+
description: "Get deposit records. Private endpoint. Rate limit: 20 req/s.",
|
|
606
|
+
isWrite: false,
|
|
607
|
+
inputSchema: {
|
|
608
|
+
type: "object",
|
|
609
|
+
properties: {
|
|
610
|
+
tokenId: { type: "string" },
|
|
611
|
+
startTime: { type: "number" },
|
|
612
|
+
endTime: { type: "number" },
|
|
613
|
+
fromId: { type: "string" },
|
|
614
|
+
limit: { type: "number" }
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
handler: async (rawArgs, context) => {
|
|
618
|
+
const args = asRecord(rawArgs);
|
|
619
|
+
const response = await context.client.privateGet(
|
|
620
|
+
"/api/v1/account/depositOrders",
|
|
621
|
+
compactObject({
|
|
622
|
+
tokenId: readString(args, "tokenId"),
|
|
623
|
+
startTime: readNumber(args, "startTime"),
|
|
624
|
+
endTime: readNumber(args, "endTime"),
|
|
625
|
+
fromId: readString(args, "fromId"),
|
|
626
|
+
limit: readNumber(args, "limit")
|
|
627
|
+
}),
|
|
628
|
+
privateRateLimit("account_get_deposit_orders", 20)
|
|
629
|
+
);
|
|
630
|
+
return normalize(response);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
];
|
|
634
|
+
}
|
|
635
|
+
function registerAuditTools() {
|
|
636
|
+
return [
|
|
637
|
+
{
|
|
638
|
+
name: "trade_get_history",
|
|
639
|
+
module: "account",
|
|
640
|
+
description: "Read local audit log entries from ~/.toobit/logs/. Returns recent tool call records. No API call.",
|
|
641
|
+
isWrite: false,
|
|
642
|
+
inputSchema: {
|
|
643
|
+
type: "object",
|
|
644
|
+
properties: {
|
|
645
|
+
date: { type: "string", description: "Date in YYYY-MM-DD format. Defaults to today." },
|
|
646
|
+
limit: { type: "number", description: "Max entries to return (default 50)" }
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
handler: async (rawArgs) => {
|
|
650
|
+
const args = asRecord(rawArgs);
|
|
651
|
+
const dateStr = readString(args, "date") ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
652
|
+
const limit = readNumber(args, "limit") ?? 50;
|
|
653
|
+
const logPath = path.join(os.homedir(), ".toobit", "logs", `trade-${dateStr}.log`);
|
|
654
|
+
if (!fs.existsSync(logPath)) {
|
|
655
|
+
return { endpoint: "local", requestTime: (/* @__PURE__ */ new Date()).toISOString(), data: [] };
|
|
656
|
+
}
|
|
657
|
+
const lines = fs.readFileSync(logPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
658
|
+
const entries = lines.slice(-limit).map((line) => {
|
|
659
|
+
try {
|
|
660
|
+
return JSON.parse(line);
|
|
661
|
+
} catch {
|
|
662
|
+
return { raw: line };
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
return { endpoint: "local", requestTime: (/* @__PURE__ */ new Date()).toISOString(), data: entries };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
];
|
|
669
|
+
}
|
|
670
|
+
function normalize2(response) {
|
|
671
|
+
return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
|
|
672
|
+
}
|
|
673
|
+
function registerFuturesTools() {
|
|
674
|
+
return [
|
|
675
|
+
{
|
|
676
|
+
name: "futures_place_order",
|
|
677
|
+
module: "futures",
|
|
678
|
+
description: "Place a USDT-M futures order. [CAUTION] Executes real trades. Private endpoint. Rate limit: 20 req/s.",
|
|
679
|
+
isWrite: true,
|
|
680
|
+
inputSchema: {
|
|
681
|
+
type: "object",
|
|
682
|
+
properties: {
|
|
683
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
684
|
+
side: { type: "string", enum: ["BUY_OPEN", "SELL_OPEN", "BUY_CLOSE", "SELL_CLOSE"] },
|
|
685
|
+
orderType: { type: "string", enum: ["LIMIT", "MARKET"], description: "Order type" },
|
|
686
|
+
quantity: { type: "string", description: "Order quantity (contracts)" },
|
|
687
|
+
price: { type: "string", description: "Required for LIMIT" },
|
|
688
|
+
leverage: { type: "string", description: "Leverage, e.g. 10" },
|
|
689
|
+
clientOrderId: { type: "string" },
|
|
690
|
+
priceType: { type: "string", enum: ["INPUT", "OPPONENT", "QUEUE", "OVER", "MARKET"], description: "Price type for trigger orders" },
|
|
691
|
+
triggerPrice: { type: "string", description: "Trigger price for conditional orders" },
|
|
692
|
+
timeInForce: { type: "string", enum: ["GTC", "IOC", "FOK", "LIMIT_MAKER"] }
|
|
693
|
+
},
|
|
694
|
+
required: ["symbol", "side", "orderType", "quantity"]
|
|
695
|
+
},
|
|
696
|
+
handler: async (rawArgs, context) => {
|
|
697
|
+
const args = asRecord(rawArgs);
|
|
698
|
+
const response = await context.client.privatePost(
|
|
699
|
+
"/api/v1/futures/order",
|
|
700
|
+
compactObject({
|
|
701
|
+
symbol: requireString(args, "symbol"),
|
|
702
|
+
side: requireString(args, "side"),
|
|
703
|
+
orderType: requireString(args, "orderType"),
|
|
704
|
+
quantity: requireString(args, "quantity"),
|
|
705
|
+
price: readString(args, "price"),
|
|
706
|
+
leverage: readString(args, "leverage"),
|
|
707
|
+
clientOrderId: readString(args, "clientOrderId"),
|
|
708
|
+
priceType: readString(args, "priceType"),
|
|
709
|
+
triggerPrice: readString(args, "triggerPrice"),
|
|
710
|
+
timeInForce: readString(args, "timeInForce")
|
|
711
|
+
}),
|
|
712
|
+
privateRateLimit("futures_place_order", 20)
|
|
713
|
+
);
|
|
714
|
+
return normalize2(response);
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
name: "futures_batch_orders",
|
|
719
|
+
module: "futures",
|
|
720
|
+
description: "[CAUTION] Batch place futures orders. Private endpoint. Rate limit: 20 req/s.",
|
|
721
|
+
isWrite: true,
|
|
722
|
+
inputSchema: {
|
|
723
|
+
type: "object",
|
|
724
|
+
properties: {
|
|
725
|
+
orders: {
|
|
726
|
+
type: "array",
|
|
727
|
+
description: "Array of order objects",
|
|
728
|
+
items: { type: "object" }
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
required: ["orders"]
|
|
732
|
+
},
|
|
733
|
+
handler: async (rawArgs, context) => {
|
|
734
|
+
const args = asRecord(rawArgs);
|
|
735
|
+
const orders = args.orders;
|
|
736
|
+
if (!Array.isArray(orders) || orders.length === 0) throw new Error("orders must be a non-empty array.");
|
|
737
|
+
const response = await context.client.privatePost(
|
|
738
|
+
"/api/v1/futures/batchOrders",
|
|
739
|
+
orders,
|
|
740
|
+
privateRateLimit("futures_batch_orders", 20)
|
|
741
|
+
);
|
|
742
|
+
return normalize2(response);
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
name: "futures_cancel_order",
|
|
747
|
+
module: "futures",
|
|
748
|
+
description: "Cancel a futures order. Private endpoint. Rate limit: 20 req/s.",
|
|
749
|
+
isWrite: true,
|
|
750
|
+
inputSchema: {
|
|
751
|
+
type: "object",
|
|
752
|
+
properties: {
|
|
753
|
+
orderId: { type: "string" },
|
|
754
|
+
clientOrderId: { type: "string" },
|
|
755
|
+
orderType: { type: "string", description: "LIMIT or condition type" }
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
handler: async (rawArgs, context) => {
|
|
759
|
+
const args = asRecord(rawArgs);
|
|
760
|
+
const response = await context.client.privateDelete(
|
|
761
|
+
"/api/v1/futures/order",
|
|
762
|
+
compactObject({
|
|
763
|
+
orderId: readString(args, "orderId"),
|
|
764
|
+
clientOrderId: readString(args, "clientOrderId"),
|
|
765
|
+
orderType: readString(args, "orderType")
|
|
766
|
+
}),
|
|
767
|
+
privateRateLimit("futures_cancel_order", 20)
|
|
768
|
+
);
|
|
769
|
+
return normalize2(response);
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
name: "futures_cancel_all_orders",
|
|
774
|
+
module: "futures",
|
|
775
|
+
description: "Cancel all futures open orders for a symbol. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
776
|
+
isWrite: true,
|
|
777
|
+
inputSchema: {
|
|
778
|
+
type: "object",
|
|
779
|
+
properties: {
|
|
780
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" }
|
|
781
|
+
},
|
|
782
|
+
required: ["symbol"]
|
|
783
|
+
},
|
|
784
|
+
handler: async (rawArgs, context) => {
|
|
785
|
+
const args = asRecord(rawArgs);
|
|
786
|
+
const response = await context.client.privateDelete(
|
|
787
|
+
"/api/v1/futures/batchOrders",
|
|
788
|
+
{ symbol: requireString(args, "symbol") },
|
|
789
|
+
privateRateLimit("futures_cancel_all_orders", 10)
|
|
790
|
+
);
|
|
791
|
+
return normalize2(response);
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
name: "futures_cancel_order_by_ids",
|
|
796
|
+
module: "futures",
|
|
797
|
+
description: "Batch cancel futures orders by IDs. Private endpoint. Rate limit: 20 req/s.",
|
|
798
|
+
isWrite: true,
|
|
799
|
+
inputSchema: {
|
|
800
|
+
type: "object",
|
|
801
|
+
properties: {
|
|
802
|
+
orderIds: { type: "string", description: "Comma-separated order IDs" }
|
|
803
|
+
},
|
|
804
|
+
required: ["orderIds"]
|
|
805
|
+
},
|
|
806
|
+
handler: async (rawArgs, context) => {
|
|
807
|
+
const args = asRecord(rawArgs);
|
|
808
|
+
const response = await context.client.privateDelete(
|
|
809
|
+
"/api/v1/futures/cancelOrderByIds",
|
|
810
|
+
{ orderIds: requireString(args, "orderIds") },
|
|
811
|
+
privateRateLimit("futures_cancel_order_by_ids", 20)
|
|
812
|
+
);
|
|
813
|
+
return normalize2(response);
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
name: "futures_amend_order",
|
|
818
|
+
module: "futures",
|
|
819
|
+
description: "Modify a futures order (price/quantity). Private endpoint. Rate limit: 20 req/s.",
|
|
820
|
+
isWrite: true,
|
|
821
|
+
inputSchema: {
|
|
822
|
+
type: "object",
|
|
823
|
+
properties: {
|
|
824
|
+
orderId: { type: "string" },
|
|
825
|
+
quantity: { type: "string" },
|
|
826
|
+
price: { type: "string" }
|
|
827
|
+
},
|
|
828
|
+
required: ["orderId"]
|
|
829
|
+
},
|
|
830
|
+
handler: async (rawArgs, context) => {
|
|
831
|
+
const args = asRecord(rawArgs);
|
|
832
|
+
const response = await context.client.privatePost(
|
|
833
|
+
"/api/v1/futures/order/update",
|
|
834
|
+
compactObject({
|
|
835
|
+
orderId: requireString(args, "orderId"),
|
|
836
|
+
quantity: readString(args, "quantity"),
|
|
837
|
+
price: readString(args, "price")
|
|
838
|
+
}),
|
|
839
|
+
privateRateLimit("futures_amend_order", 20)
|
|
840
|
+
);
|
|
841
|
+
return normalize2(response);
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
name: "futures_get_order",
|
|
846
|
+
module: "futures",
|
|
847
|
+
description: "Get details of a single futures order. Private endpoint. Rate limit: 20 req/s.",
|
|
848
|
+
isWrite: false,
|
|
849
|
+
inputSchema: {
|
|
850
|
+
type: "object",
|
|
851
|
+
properties: {
|
|
852
|
+
orderId: { type: "string" },
|
|
853
|
+
clientOrderId: { type: "string" },
|
|
854
|
+
orderType: { type: "string" }
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
handler: async (rawArgs, context) => {
|
|
858
|
+
const args = asRecord(rawArgs);
|
|
859
|
+
const response = await context.client.privateGet(
|
|
860
|
+
"/api/v1/futures/order",
|
|
861
|
+
compactObject({
|
|
862
|
+
orderId: readString(args, "orderId"),
|
|
863
|
+
clientOrderId: readString(args, "clientOrderId"),
|
|
864
|
+
orderType: readString(args, "orderType")
|
|
865
|
+
}),
|
|
866
|
+
privateRateLimit("futures_get_order", 20)
|
|
867
|
+
);
|
|
868
|
+
return normalize2(response);
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
name: "futures_get_open_orders",
|
|
873
|
+
module: "futures",
|
|
874
|
+
description: "Get current open futures orders. Private endpoint. Rate limit: 20 req/s.",
|
|
875
|
+
isWrite: false,
|
|
876
|
+
inputSchema: {
|
|
877
|
+
type: "object",
|
|
878
|
+
properties: {
|
|
879
|
+
symbol: { type: "string" },
|
|
880
|
+
orderId: { type: "string" },
|
|
881
|
+
orderType: { type: "string" },
|
|
882
|
+
limit: { type: "number" }
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
handler: async (rawArgs, context) => {
|
|
886
|
+
const args = asRecord(rawArgs);
|
|
887
|
+
const response = await context.client.privateGet(
|
|
888
|
+
"/api/v1/futures/openOrders",
|
|
889
|
+
compactObject({
|
|
890
|
+
symbol: readString(args, "symbol"),
|
|
891
|
+
orderId: readString(args, "orderId"),
|
|
892
|
+
orderType: readString(args, "orderType"),
|
|
893
|
+
limit: readNumber(args, "limit")
|
|
894
|
+
}),
|
|
895
|
+
privateRateLimit("futures_get_open_orders", 20)
|
|
896
|
+
);
|
|
897
|
+
return normalize2(response);
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
name: "futures_get_history_orders",
|
|
902
|
+
module: "futures",
|
|
903
|
+
description: "Get futures order history. Private endpoint. Rate limit: 20 req/s.",
|
|
904
|
+
isWrite: false,
|
|
905
|
+
inputSchema: {
|
|
906
|
+
type: "object",
|
|
907
|
+
properties: {
|
|
908
|
+
symbol: { type: "string" },
|
|
909
|
+
orderId: { type: "string" },
|
|
910
|
+
orderType: { type: "string" },
|
|
911
|
+
startTime: { type: "number" },
|
|
912
|
+
endTime: { type: "number" },
|
|
913
|
+
limit: { type: "number" }
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
handler: async (rawArgs, context) => {
|
|
917
|
+
const args = asRecord(rawArgs);
|
|
918
|
+
const response = await context.client.privateGet(
|
|
919
|
+
"/api/v1/futures/historyOrders",
|
|
920
|
+
compactObject({
|
|
921
|
+
symbol: readString(args, "symbol"),
|
|
922
|
+
orderId: readString(args, "orderId"),
|
|
923
|
+
orderType: readString(args, "orderType"),
|
|
924
|
+
startTime: readNumber(args, "startTime"),
|
|
925
|
+
endTime: readNumber(args, "endTime"),
|
|
926
|
+
limit: readNumber(args, "limit")
|
|
927
|
+
}),
|
|
928
|
+
privateRateLimit("futures_get_history_orders", 20)
|
|
929
|
+
);
|
|
930
|
+
return normalize2(response);
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
name: "futures_get_positions",
|
|
935
|
+
module: "futures",
|
|
936
|
+
description: "Get current futures positions. Private endpoint. Rate limit: 20 req/s.",
|
|
937
|
+
isWrite: false,
|
|
938
|
+
inputSchema: {
|
|
939
|
+
type: "object",
|
|
940
|
+
properties: {
|
|
941
|
+
symbol: { type: "string", description: "Omit for all positions" }
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
handler: async (rawArgs, context) => {
|
|
945
|
+
const args = asRecord(rawArgs);
|
|
946
|
+
const response = await context.client.privateGet(
|
|
947
|
+
"/api/v1/futures/positions",
|
|
948
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
949
|
+
privateRateLimit("futures_get_positions", 20)
|
|
950
|
+
);
|
|
951
|
+
return normalize2(response);
|
|
952
|
+
}
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
name: "futures_get_history_positions",
|
|
956
|
+
module: "futures",
|
|
957
|
+
description: "Get futures closed position history. Private endpoint. Rate limit: 20 req/s.",
|
|
958
|
+
isWrite: false,
|
|
959
|
+
inputSchema: {
|
|
960
|
+
type: "object",
|
|
961
|
+
properties: {
|
|
962
|
+
symbol: { type: "string" },
|
|
963
|
+
startTime: { type: "number" },
|
|
964
|
+
endTime: { type: "number" },
|
|
965
|
+
limit: { type: "number" }
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
handler: async (rawArgs, context) => {
|
|
969
|
+
const args = asRecord(rawArgs);
|
|
970
|
+
const response = await context.client.privateGet(
|
|
971
|
+
"/api/v1/futures/historyPositions",
|
|
972
|
+
compactObject({
|
|
973
|
+
symbol: readString(args, "symbol"),
|
|
974
|
+
startTime: readNumber(args, "startTime"),
|
|
975
|
+
endTime: readNumber(args, "endTime"),
|
|
976
|
+
limit: readNumber(args, "limit")
|
|
977
|
+
}),
|
|
978
|
+
privateRateLimit("futures_get_history_positions", 20)
|
|
979
|
+
);
|
|
980
|
+
return normalize2(response);
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
name: "futures_set_leverage",
|
|
985
|
+
module: "futures",
|
|
986
|
+
description: "Set leverage for a futures symbol. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
987
|
+
isWrite: true,
|
|
988
|
+
inputSchema: {
|
|
989
|
+
type: "object",
|
|
990
|
+
properties: {
|
|
991
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
992
|
+
leverage: { type: "number", description: "Leverage value, e.g. 10" }
|
|
993
|
+
},
|
|
994
|
+
required: ["symbol", "leverage"]
|
|
995
|
+
},
|
|
996
|
+
handler: async (rawArgs, context) => {
|
|
997
|
+
const args = asRecord(rawArgs);
|
|
998
|
+
const response = await context.client.privatePost(
|
|
999
|
+
"/api/v1/futures/leverage",
|
|
1000
|
+
compactObject({
|
|
1001
|
+
symbol: requireString(args, "symbol"),
|
|
1002
|
+
leverage: readNumber(args, "leverage")
|
|
1003
|
+
}),
|
|
1004
|
+
privateRateLimit("futures_set_leverage", 10)
|
|
1005
|
+
);
|
|
1006
|
+
return normalize2(response);
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
name: "futures_get_leverage",
|
|
1011
|
+
module: "futures",
|
|
1012
|
+
description: "Get current leverage and position mode for a futures symbol. Private endpoint. Rate limit: 20 req/s.",
|
|
1013
|
+
isWrite: false,
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
type: "object",
|
|
1016
|
+
properties: {
|
|
1017
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" }
|
|
1018
|
+
},
|
|
1019
|
+
required: ["symbol"]
|
|
1020
|
+
},
|
|
1021
|
+
handler: async (rawArgs, context) => {
|
|
1022
|
+
const args = asRecord(rawArgs);
|
|
1023
|
+
const response = await context.client.privateGet(
|
|
1024
|
+
"/api/v1/futures/accountLeverage",
|
|
1025
|
+
{ symbol: requireString(args, "symbol") },
|
|
1026
|
+
privateRateLimit("futures_get_leverage", 20)
|
|
1027
|
+
);
|
|
1028
|
+
return normalize2(response);
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: "futures_set_margin_type",
|
|
1033
|
+
module: "futures",
|
|
1034
|
+
description: "Switch between cross and isolated margin mode. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1035
|
+
isWrite: true,
|
|
1036
|
+
inputSchema: {
|
|
1037
|
+
type: "object",
|
|
1038
|
+
properties: {
|
|
1039
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1040
|
+
marginType: { type: "string", enum: ["1", "2"], description: "1=cross, 2=isolated" }
|
|
1041
|
+
},
|
|
1042
|
+
required: ["symbol", "marginType"]
|
|
1043
|
+
},
|
|
1044
|
+
handler: async (rawArgs, context) => {
|
|
1045
|
+
const args = asRecord(rawArgs);
|
|
1046
|
+
const response = await context.client.privatePost(
|
|
1047
|
+
"/api/v1/futures/marginType",
|
|
1048
|
+
compactObject({
|
|
1049
|
+
symbol: requireString(args, "symbol"),
|
|
1050
|
+
marginType: requireString(args, "marginType")
|
|
1051
|
+
}),
|
|
1052
|
+
privateRateLimit("futures_set_margin_type", 10)
|
|
1053
|
+
);
|
|
1054
|
+
return normalize2(response);
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
name: "futures_set_trading_stop",
|
|
1059
|
+
module: "futures",
|
|
1060
|
+
description: "Set take-profit/stop-loss for a futures position. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1061
|
+
isWrite: true,
|
|
1062
|
+
inputSchema: {
|
|
1063
|
+
type: "object",
|
|
1064
|
+
properties: {
|
|
1065
|
+
symbol: { type: "string" },
|
|
1066
|
+
side: { type: "string", enum: ["LONG", "SHORT"] },
|
|
1067
|
+
stopLossPrice: { type: "string" },
|
|
1068
|
+
takeProfitPrice: { type: "string" }
|
|
1069
|
+
},
|
|
1070
|
+
required: ["symbol", "side"]
|
|
1071
|
+
},
|
|
1072
|
+
handler: async (rawArgs, context) => {
|
|
1073
|
+
const args = asRecord(rawArgs);
|
|
1074
|
+
const response = await context.client.privatePost(
|
|
1075
|
+
"/api/v1/futures/position/trading-stop",
|
|
1076
|
+
compactObject({
|
|
1077
|
+
symbol: requireString(args, "symbol"),
|
|
1078
|
+
side: requireString(args, "side"),
|
|
1079
|
+
stopLossPrice: readString(args, "stopLossPrice"),
|
|
1080
|
+
takeProfitPrice: readString(args, "takeProfitPrice")
|
|
1081
|
+
}),
|
|
1082
|
+
privateRateLimit("futures_set_trading_stop", 10)
|
|
1083
|
+
);
|
|
1084
|
+
return normalize2(response);
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
name: "futures_flash_close",
|
|
1089
|
+
module: "futures",
|
|
1090
|
+
description: "Flash close a futures position (market close). [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1091
|
+
isWrite: true,
|
|
1092
|
+
inputSchema: {
|
|
1093
|
+
type: "object",
|
|
1094
|
+
properties: {
|
|
1095
|
+
symbol: { type: "string" },
|
|
1096
|
+
side: { type: "string", enum: ["LONG", "SHORT"] }
|
|
1097
|
+
},
|
|
1098
|
+
required: ["symbol", "side"]
|
|
1099
|
+
},
|
|
1100
|
+
handler: async (rawArgs, context) => {
|
|
1101
|
+
const args = asRecord(rawArgs);
|
|
1102
|
+
const response = await context.client.privatePost(
|
|
1103
|
+
"/api/v1/futures/flashClose",
|
|
1104
|
+
compactObject({
|
|
1105
|
+
symbol: requireString(args, "symbol"),
|
|
1106
|
+
side: requireString(args, "side")
|
|
1107
|
+
}),
|
|
1108
|
+
privateRateLimit("futures_flash_close", 10)
|
|
1109
|
+
);
|
|
1110
|
+
return normalize2(response);
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
name: "futures_reverse_position",
|
|
1115
|
+
module: "futures",
|
|
1116
|
+
description: "Reverse a futures position (one-click reverse). [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1117
|
+
isWrite: true,
|
|
1118
|
+
inputSchema: {
|
|
1119
|
+
type: "object",
|
|
1120
|
+
properties: {
|
|
1121
|
+
symbol: { type: "string" },
|
|
1122
|
+
side: { type: "string", enum: ["LONG", "SHORT"], description: "Current position side to reverse" }
|
|
1123
|
+
},
|
|
1124
|
+
required: ["symbol", "side"]
|
|
1125
|
+
},
|
|
1126
|
+
handler: async (rawArgs, context) => {
|
|
1127
|
+
const args = asRecord(rawArgs);
|
|
1128
|
+
const response = await context.client.privatePost(
|
|
1129
|
+
"/api/v1/futures/reversePosition",
|
|
1130
|
+
compactObject({
|
|
1131
|
+
symbol: requireString(args, "symbol"),
|
|
1132
|
+
side: requireString(args, "side")
|
|
1133
|
+
}),
|
|
1134
|
+
privateRateLimit("futures_reverse_position", 10)
|
|
1135
|
+
);
|
|
1136
|
+
return normalize2(response);
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
name: "futures_adjust_margin",
|
|
1141
|
+
module: "futures",
|
|
1142
|
+
description: "Adjust isolated margin for a position. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1143
|
+
isWrite: true,
|
|
1144
|
+
inputSchema: {
|
|
1145
|
+
type: "object",
|
|
1146
|
+
properties: {
|
|
1147
|
+
symbol: { type: "string" },
|
|
1148
|
+
side: { type: "string", enum: ["LONG", "SHORT"] },
|
|
1149
|
+
amount: { type: "string", description: "Positive=add, negative=reduce" }
|
|
1150
|
+
},
|
|
1151
|
+
required: ["symbol", "side", "amount"]
|
|
1152
|
+
},
|
|
1153
|
+
handler: async (rawArgs, context) => {
|
|
1154
|
+
const args = asRecord(rawArgs);
|
|
1155
|
+
const response = await context.client.privatePost(
|
|
1156
|
+
"/api/v1/futures/positionMargin",
|
|
1157
|
+
compactObject({
|
|
1158
|
+
symbol: requireString(args, "symbol"),
|
|
1159
|
+
side: requireString(args, "side"),
|
|
1160
|
+
amount: requireString(args, "amount")
|
|
1161
|
+
}),
|
|
1162
|
+
privateRateLimit("futures_adjust_margin", 10)
|
|
1163
|
+
);
|
|
1164
|
+
return normalize2(response);
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
name: "futures_get_fills",
|
|
1169
|
+
module: "futures",
|
|
1170
|
+
description: "Get futures trade history (fills). Private endpoint. Rate limit: 20 req/s.",
|
|
1171
|
+
isWrite: false,
|
|
1172
|
+
inputSchema: {
|
|
1173
|
+
type: "object",
|
|
1174
|
+
properties: {
|
|
1175
|
+
symbol: { type: "string" },
|
|
1176
|
+
startTime: { type: "number" },
|
|
1177
|
+
endTime: { type: "number" },
|
|
1178
|
+
fromId: { type: "string" },
|
|
1179
|
+
limit: { type: "number" }
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
handler: async (rawArgs, context) => {
|
|
1183
|
+
const args = asRecord(rawArgs);
|
|
1184
|
+
const response = await context.client.privateGet(
|
|
1185
|
+
"/api/v1/futures/userTrades",
|
|
1186
|
+
compactObject({
|
|
1187
|
+
symbol: readString(args, "symbol"),
|
|
1188
|
+
startTime: readNumber(args, "startTime"),
|
|
1189
|
+
endTime: readNumber(args, "endTime"),
|
|
1190
|
+
fromId: readString(args, "fromId"),
|
|
1191
|
+
limit: readNumber(args, "limit")
|
|
1192
|
+
}),
|
|
1193
|
+
privateRateLimit("futures_get_fills", 20)
|
|
1194
|
+
);
|
|
1195
|
+
return normalize2(response);
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
name: "futures_get_balance",
|
|
1200
|
+
module: "futures",
|
|
1201
|
+
description: "Get futures account balance. Private endpoint. Rate limit: 20 req/s.",
|
|
1202
|
+
isWrite: false,
|
|
1203
|
+
inputSchema: { type: "object", properties: {} },
|
|
1204
|
+
handler: async (_rawArgs, context) => {
|
|
1205
|
+
const response = await context.client.privateGet(
|
|
1206
|
+
"/api/v1/futures/balance",
|
|
1207
|
+
{},
|
|
1208
|
+
privateRateLimit("futures_get_balance", 20)
|
|
1209
|
+
);
|
|
1210
|
+
return normalize2(response);
|
|
1211
|
+
}
|
|
1212
|
+
},
|
|
1213
|
+
{
|
|
1214
|
+
name: "futures_get_commission_rate",
|
|
1215
|
+
module: "futures",
|
|
1216
|
+
description: "Get futures commission rate for a symbol. Private endpoint. Rate limit: 20 req/s.",
|
|
1217
|
+
isWrite: false,
|
|
1218
|
+
inputSchema: {
|
|
1219
|
+
type: "object",
|
|
1220
|
+
properties: {
|
|
1221
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" }
|
|
1222
|
+
},
|
|
1223
|
+
required: ["symbol"]
|
|
1224
|
+
},
|
|
1225
|
+
handler: async (rawArgs, context) => {
|
|
1226
|
+
const args = asRecord(rawArgs);
|
|
1227
|
+
const response = await context.client.privateGet(
|
|
1228
|
+
"/api/v1/futures/commissionRate",
|
|
1229
|
+
{ symbol: requireString(args, "symbol") },
|
|
1230
|
+
privateRateLimit("futures_get_commission_rate", 20)
|
|
1231
|
+
);
|
|
1232
|
+
return normalize2(response);
|
|
1233
|
+
}
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
name: "futures_get_today_pnl",
|
|
1237
|
+
module: "futures",
|
|
1238
|
+
description: "Get today's realized PnL for futures. Private endpoint. Rate limit: 20 req/s.",
|
|
1239
|
+
isWrite: false,
|
|
1240
|
+
inputSchema: { type: "object", properties: {} },
|
|
1241
|
+
handler: async (_rawArgs, context) => {
|
|
1242
|
+
const response = await context.client.privateGet(
|
|
1243
|
+
"/api/v1/futures/todayPnl",
|
|
1244
|
+
{},
|
|
1245
|
+
privateRateLimit("futures_get_today_pnl", 20)
|
|
1246
|
+
);
|
|
1247
|
+
return normalize2(response);
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
name: "futures_get_balance_flow",
|
|
1252
|
+
module: "futures",
|
|
1253
|
+
description: "Get futures balance flow (ledger). Private endpoint. Rate limit: 20 req/s.",
|
|
1254
|
+
isWrite: false,
|
|
1255
|
+
inputSchema: {
|
|
1256
|
+
type: "object",
|
|
1257
|
+
properties: {
|
|
1258
|
+
symbol: { type: "string" },
|
|
1259
|
+
startTime: { type: "number" },
|
|
1260
|
+
endTime: { type: "number" },
|
|
1261
|
+
limit: { type: "number" },
|
|
1262
|
+
fromId: { type: "string" }
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
handler: async (rawArgs, context) => {
|
|
1266
|
+
const args = asRecord(rawArgs);
|
|
1267
|
+
const response = await context.client.privateGet(
|
|
1268
|
+
"/api/v1/futures/balanceFlow",
|
|
1269
|
+
compactObject({
|
|
1270
|
+
symbol: readString(args, "symbol"),
|
|
1271
|
+
startTime: readNumber(args, "startTime"),
|
|
1272
|
+
endTime: readNumber(args, "endTime"),
|
|
1273
|
+
limit: readNumber(args, "limit"),
|
|
1274
|
+
fromId: readString(args, "fromId")
|
|
1275
|
+
}),
|
|
1276
|
+
privateRateLimit("futures_get_balance_flow", 20)
|
|
1277
|
+
);
|
|
1278
|
+
return normalize2(response);
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
name: "futures_auto_add_margin",
|
|
1283
|
+
module: "futures",
|
|
1284
|
+
description: "Enable/disable auto add margin for isolated positions. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1285
|
+
isWrite: true,
|
|
1286
|
+
inputSchema: {
|
|
1287
|
+
type: "object",
|
|
1288
|
+
properties: {
|
|
1289
|
+
symbol: { type: "string" },
|
|
1290
|
+
side: { type: "string", enum: ["LONG", "SHORT"] },
|
|
1291
|
+
autoAddMargin: { type: "string", enum: ["true", "false"] }
|
|
1292
|
+
},
|
|
1293
|
+
required: ["symbol", "side", "autoAddMargin"]
|
|
1294
|
+
},
|
|
1295
|
+
handler: async (rawArgs, context) => {
|
|
1296
|
+
const args = asRecord(rawArgs);
|
|
1297
|
+
const response = await context.client.privatePost(
|
|
1298
|
+
"/api/v1/futures/autoAddMargin",
|
|
1299
|
+
compactObject({
|
|
1300
|
+
symbol: requireString(args, "symbol"),
|
|
1301
|
+
side: requireString(args, "side"),
|
|
1302
|
+
autoAddMargin: requireString(args, "autoAddMargin")
|
|
1303
|
+
}),
|
|
1304
|
+
privateRateLimit("futures_auto_add_margin", 10)
|
|
1305
|
+
);
|
|
1306
|
+
return normalize2(response);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
];
|
|
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
|
+
}
|
|
1318
|
+
function normalize3(response) {
|
|
1319
|
+
return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
|
|
1320
|
+
}
|
|
1321
|
+
function registerMarketTools() {
|
|
1322
|
+
return [
|
|
1323
|
+
{
|
|
1324
|
+
name: "market_get_server_time",
|
|
1325
|
+
module: "market",
|
|
1326
|
+
description: "Get Toobit server time. Public endpoint. Rate limit: 20 req/s.",
|
|
1327
|
+
isWrite: false,
|
|
1328
|
+
inputSchema: { type: "object", properties: {} },
|
|
1329
|
+
handler: async (_rawArgs, context) => {
|
|
1330
|
+
const response = await context.client.publicGet("/api/v1/time", {}, publicRateLimit("market_get_server_time", 20));
|
|
1331
|
+
return normalize3(response);
|
|
1332
|
+
}
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
name: "market_get_exchange_info",
|
|
1336
|
+
module: "market",
|
|
1337
|
+
description: "Get exchange info including trading rules, symbol list, rate limits. Public endpoint. Rate limit: 10 req/s.",
|
|
1338
|
+
isWrite: false,
|
|
1339
|
+
inputSchema: { type: "object", properties: {} },
|
|
1340
|
+
handler: async (_rawArgs, context) => {
|
|
1341
|
+
const response = await context.client.publicGet("/api/v1/exchangeInfo", {}, publicRateLimit("market_get_exchange_info", 10));
|
|
1342
|
+
return normalize3(response);
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
name: "market_get_depth",
|
|
1347
|
+
module: "market",
|
|
1348
|
+
description: "Get order book depth for a symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1349
|
+
isWrite: false,
|
|
1350
|
+
inputSchema: {
|
|
1351
|
+
type: "object",
|
|
1352
|
+
properties: {
|
|
1353
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1354
|
+
limit: { type: "number", description: "Depth per side, default 100, max 100" }
|
|
1355
|
+
},
|
|
1356
|
+
required: ["symbol"]
|
|
1357
|
+
},
|
|
1358
|
+
handler: async (rawArgs, context) => {
|
|
1359
|
+
const args = asRecord(rawArgs);
|
|
1360
|
+
const response = await context.client.publicGet(
|
|
1361
|
+
"/quote/v1/depth",
|
|
1362
|
+
compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
|
|
1363
|
+
publicRateLimit("market_get_depth", 20)
|
|
1364
|
+
);
|
|
1365
|
+
return normalize3(response);
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
{
|
|
1369
|
+
name: "market_get_merged_depth",
|
|
1370
|
+
module: "market",
|
|
1371
|
+
description: "Get merged order book depth for a symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1372
|
+
isWrite: false,
|
|
1373
|
+
inputSchema: {
|
|
1374
|
+
type: "object",
|
|
1375
|
+
properties: {
|
|
1376
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1377
|
+
scale: { type: "number", description: "Price merge precision" },
|
|
1378
|
+
limit: { type: "number", description: "Default 40, max 100" }
|
|
1379
|
+
},
|
|
1380
|
+
required: ["symbol"]
|
|
1381
|
+
},
|
|
1382
|
+
handler: async (rawArgs, context) => {
|
|
1383
|
+
const args = asRecord(rawArgs);
|
|
1384
|
+
const response = await context.client.publicGet(
|
|
1385
|
+
"/quote/v1/depth/merged",
|
|
1386
|
+
compactObject({ symbol: requireString(args, "symbol"), scale: readNumber(args, "scale"), limit: readPositiveInt(args, "limit") }),
|
|
1387
|
+
publicRateLimit("market_get_merged_depth", 20)
|
|
1388
|
+
);
|
|
1389
|
+
return normalize3(response);
|
|
1390
|
+
}
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
name: "market_get_trades",
|
|
1394
|
+
module: "market",
|
|
1395
|
+
description: "Get recent trades for a symbol. Default 60 records, max 60. Public endpoint. Rate limit: 20 req/s.",
|
|
1396
|
+
isWrite: false,
|
|
1397
|
+
inputSchema: {
|
|
1398
|
+
type: "object",
|
|
1399
|
+
properties: {
|
|
1400
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1401
|
+
limit: { type: "number", description: "Default 60, max 60" }
|
|
1402
|
+
},
|
|
1403
|
+
required: ["symbol"]
|
|
1404
|
+
},
|
|
1405
|
+
handler: async (rawArgs, context) => {
|
|
1406
|
+
const args = asRecord(rawArgs);
|
|
1407
|
+
const response = await context.client.publicGet(
|
|
1408
|
+
"/quote/v1/trades",
|
|
1409
|
+
compactObject({ symbol: requireString(args, "symbol"), limit: readPositiveInt(args, "limit") }),
|
|
1410
|
+
publicRateLimit("market_get_trades", 20)
|
|
1411
|
+
);
|
|
1412
|
+
return normalize3(response);
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
name: "market_get_klines",
|
|
1417
|
+
module: "market",
|
|
1418
|
+
description: "Get candlestick (OHLCV) data for a symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1419
|
+
isWrite: false,
|
|
1420
|
+
inputSchema: {
|
|
1421
|
+
type: "object",
|
|
1422
|
+
properties: {
|
|
1423
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1424
|
+
interval: { type: "string", enum: [...TOOBIT_CANDLE_BARS], description: "K-line interval, e.g. 1m, 1h, 1d" },
|
|
1425
|
+
startTime: { type: "number", description: "Start time in ms" },
|
|
1426
|
+
endTime: { type: "number", description: "End time in ms" },
|
|
1427
|
+
limit: { type: "number", description: "Default 500, max 1000" }
|
|
1428
|
+
},
|
|
1429
|
+
required: ["symbol", "interval"]
|
|
1430
|
+
},
|
|
1431
|
+
handler: async (rawArgs, context) => {
|
|
1432
|
+
const args = asRecord(rawArgs);
|
|
1433
|
+
const response = await context.client.publicGet(
|
|
1434
|
+
"/quote/v1/klines",
|
|
1435
|
+
compactObject({
|
|
1436
|
+
symbol: requireString(args, "symbol"),
|
|
1437
|
+
interval: requireString(args, "interval"),
|
|
1438
|
+
startTime: readNumber(args, "startTime"),
|
|
1439
|
+
endTime: readNumber(args, "endTime"),
|
|
1440
|
+
limit: readPositiveInt(args, "limit")
|
|
1441
|
+
}),
|
|
1442
|
+
publicRateLimit("market_get_klines", 20)
|
|
1443
|
+
);
|
|
1444
|
+
return normalize3(response);
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
name: "market_get_ticker_24hr",
|
|
1449
|
+
module: "market",
|
|
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.",
|
|
1451
|
+
isWrite: false,
|
|
1452
|
+
inputSchema: {
|
|
1453
|
+
type: "object",
|
|
1454
|
+
properties: {
|
|
1455
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1456
|
+
}
|
|
1457
|
+
},
|
|
1458
|
+
handler: async (rawArgs, context) => {
|
|
1459
|
+
const args = asRecord(rawArgs);
|
|
1460
|
+
const response = await context.client.publicGet(
|
|
1461
|
+
"/quote/v1/ticker/24hr",
|
|
1462
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1463
|
+
publicRateLimit("market_get_ticker_24hr", 20)
|
|
1464
|
+
);
|
|
1465
|
+
return normalize3(response);
|
|
1466
|
+
}
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
name: "market_get_ticker_price",
|
|
1470
|
+
module: "market",
|
|
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.",
|
|
1472
|
+
isWrite: false,
|
|
1473
|
+
inputSchema: {
|
|
1474
|
+
type: "object",
|
|
1475
|
+
properties: {
|
|
1476
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1477
|
+
}
|
|
1478
|
+
},
|
|
1479
|
+
handler: async (rawArgs, context) => {
|
|
1480
|
+
const args = asRecord(rawArgs);
|
|
1481
|
+
const response = await context.client.publicGet(
|
|
1482
|
+
"/quote/v1/ticker/price",
|
|
1483
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1484
|
+
publicRateLimit("market_get_ticker_price", 20)
|
|
1485
|
+
);
|
|
1486
|
+
return normalize3(response);
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1489
|
+
{
|
|
1490
|
+
name: "market_get_book_ticker",
|
|
1491
|
+
module: "market",
|
|
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.",
|
|
1493
|
+
isWrite: false,
|
|
1494
|
+
inputSchema: {
|
|
1495
|
+
type: "object",
|
|
1496
|
+
properties: {
|
|
1497
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols (warning: 900+ results)." }
|
|
1498
|
+
}
|
|
1499
|
+
},
|
|
1500
|
+
handler: async (rawArgs, context) => {
|
|
1501
|
+
const args = asRecord(rawArgs);
|
|
1502
|
+
const response = await context.client.publicGet(
|
|
1503
|
+
"/quote/v1/ticker/bookTicker",
|
|
1504
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1505
|
+
publicRateLimit("market_get_book_ticker", 20)
|
|
1506
|
+
);
|
|
1507
|
+
return normalize3(response);
|
|
1508
|
+
}
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
name: "market_get_index_klines",
|
|
1512
|
+
module: "market",
|
|
1513
|
+
description: "Get index K-line data. Public endpoint. Rate limit: 20 req/s.",
|
|
1514
|
+
isWrite: false,
|
|
1515
|
+
inputSchema: {
|
|
1516
|
+
type: "object",
|
|
1517
|
+
properties: {
|
|
1518
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1519
|
+
interval: { type: "string", enum: [...TOOBIT_CANDLE_BARS] },
|
|
1520
|
+
startTime: { type: "number" },
|
|
1521
|
+
endTime: { type: "number" },
|
|
1522
|
+
limit: { type: "number", description: "Default 500, max 1000" }
|
|
1523
|
+
},
|
|
1524
|
+
required: ["symbol", "interval"]
|
|
1525
|
+
},
|
|
1526
|
+
handler: async (rawArgs, context) => {
|
|
1527
|
+
const args = asRecord(rawArgs);
|
|
1528
|
+
const response = await context.client.publicGet(
|
|
1529
|
+
"/quote/v1/index/klines",
|
|
1530
|
+
compactObject({
|
|
1531
|
+
symbol: requireString(args, "symbol"),
|
|
1532
|
+
interval: requireString(args, "interval"),
|
|
1533
|
+
startTime: readNumber(args, "startTime"),
|
|
1534
|
+
endTime: readNumber(args, "endTime"),
|
|
1535
|
+
limit: readPositiveInt(args, "limit")
|
|
1536
|
+
}),
|
|
1537
|
+
publicRateLimit("market_get_index_klines", 20)
|
|
1538
|
+
);
|
|
1539
|
+
return normalize3(response);
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
name: "market_get_mark_price",
|
|
1544
|
+
module: "market",
|
|
1545
|
+
description: "Get latest mark price for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1546
|
+
isWrite: false,
|
|
1547
|
+
inputSchema: {
|
|
1548
|
+
type: "object",
|
|
1549
|
+
properties: {
|
|
1550
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
handler: async (rawArgs, context) => {
|
|
1554
|
+
const args = asRecord(rawArgs);
|
|
1555
|
+
const response = await context.client.publicGet(
|
|
1556
|
+
"/quote/v1/markPrice",
|
|
1557
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1558
|
+
publicRateLimit("market_get_mark_price", 20)
|
|
1559
|
+
);
|
|
1560
|
+
return normalize3(response);
|
|
1561
|
+
}
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
name: "market_get_mark_price_klines",
|
|
1565
|
+
module: "market",
|
|
1566
|
+
description: "Get mark price K-line data. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1567
|
+
isWrite: false,
|
|
1568
|
+
inputSchema: {
|
|
1569
|
+
type: "object",
|
|
1570
|
+
properties: {
|
|
1571
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1572
|
+
interval: { type: "string", enum: [...TOOBIT_CANDLE_BARS] },
|
|
1573
|
+
startTime: { type: "number" },
|
|
1574
|
+
endTime: { type: "number" },
|
|
1575
|
+
limit: { type: "number" }
|
|
1576
|
+
},
|
|
1577
|
+
required: ["symbol", "interval"]
|
|
1578
|
+
},
|
|
1579
|
+
handler: async (rawArgs, context) => {
|
|
1580
|
+
const args = asRecord(rawArgs);
|
|
1581
|
+
const response = await context.client.publicGet(
|
|
1582
|
+
"/quote/v1/markPrice/klines",
|
|
1583
|
+
compactObject({
|
|
1584
|
+
symbol: requireString(args, "symbol"),
|
|
1585
|
+
interval: requireString(args, "interval"),
|
|
1586
|
+
startTime: readNumber(args, "startTime"),
|
|
1587
|
+
endTime: readNumber(args, "endTime"),
|
|
1588
|
+
limit: readPositiveInt(args, "limit")
|
|
1589
|
+
}),
|
|
1590
|
+
publicRateLimit("market_get_mark_price_klines", 20)
|
|
1591
|
+
);
|
|
1592
|
+
return normalize3(response);
|
|
1593
|
+
}
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: "market_get_funding_rate",
|
|
1597
|
+
module: "market",
|
|
1598
|
+
description: "Get current funding rate for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1599
|
+
isWrite: false,
|
|
1600
|
+
inputSchema: {
|
|
1601
|
+
type: "object",
|
|
1602
|
+
properties: {
|
|
1603
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
handler: async (rawArgs, context) => {
|
|
1607
|
+
const args = asRecord(rawArgs);
|
|
1608
|
+
const response = await context.client.publicGet(
|
|
1609
|
+
"/api/v1/futures/fundingRate",
|
|
1610
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1611
|
+
publicRateLimit("market_get_funding_rate", 20)
|
|
1612
|
+
);
|
|
1613
|
+
return normalize3(response);
|
|
1614
|
+
}
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
name: "market_get_funding_rate_history",
|
|
1618
|
+
module: "market",
|
|
1619
|
+
description: "Get historical funding rates for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1620
|
+
isWrite: false,
|
|
1621
|
+
inputSchema: {
|
|
1622
|
+
type: "object",
|
|
1623
|
+
properties: {
|
|
1624
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1625
|
+
startTime: { type: "number" },
|
|
1626
|
+
endTime: { type: "number" },
|
|
1627
|
+
limit: { type: "number", description: "Default 100, max 1000" }
|
|
1628
|
+
},
|
|
1629
|
+
required: ["symbol"]
|
|
1630
|
+
},
|
|
1631
|
+
handler: async (rawArgs, context) => {
|
|
1632
|
+
const args = asRecord(rawArgs);
|
|
1633
|
+
const response = await context.client.publicGet(
|
|
1634
|
+
"/api/v1/futures/historyFundingRate",
|
|
1635
|
+
compactObject({
|
|
1636
|
+
symbol: requireString(args, "symbol"),
|
|
1637
|
+
startTime: readNumber(args, "startTime"),
|
|
1638
|
+
endTime: readNumber(args, "endTime"),
|
|
1639
|
+
limit: readPositiveInt(args, "limit")
|
|
1640
|
+
}),
|
|
1641
|
+
publicRateLimit("market_get_funding_rate_history", 20)
|
|
1642
|
+
);
|
|
1643
|
+
return normalize3(response);
|
|
1644
|
+
}
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
name: "market_get_open_interest",
|
|
1648
|
+
module: "market",
|
|
1649
|
+
description: "Get total open interest for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1650
|
+
isWrite: false,
|
|
1651
|
+
inputSchema: {
|
|
1652
|
+
type: "object",
|
|
1653
|
+
properties: {
|
|
1654
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1655
|
+
}
|
|
1656
|
+
},
|
|
1657
|
+
handler: async (rawArgs, context) => {
|
|
1658
|
+
const args = asRecord(rawArgs);
|
|
1659
|
+
const response = await context.client.publicGet(
|
|
1660
|
+
"/quote/v1/openInterest",
|
|
1661
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1662
|
+
publicRateLimit("market_get_open_interest", 20)
|
|
1663
|
+
);
|
|
1664
|
+
return normalize3(response);
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
name: "market_get_long_short_ratio",
|
|
1669
|
+
module: "market",
|
|
1670
|
+
description: "Get global long/short account ratio. Public endpoint. Rate limit: 20 req/s.",
|
|
1671
|
+
isWrite: false,
|
|
1672
|
+
inputSchema: {
|
|
1673
|
+
type: "object",
|
|
1674
|
+
properties: {
|
|
1675
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" },
|
|
1676
|
+
period: { type: "string", description: "e.g. 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d" },
|
|
1677
|
+
startTime: { type: "number" },
|
|
1678
|
+
endTime: { type: "number" },
|
|
1679
|
+
limit: { type: "number" }
|
|
1680
|
+
},
|
|
1681
|
+
required: ["symbol", "period"]
|
|
1682
|
+
},
|
|
1683
|
+
handler: async (rawArgs, context) => {
|
|
1684
|
+
const args = asRecord(rawArgs);
|
|
1685
|
+
const response = await context.client.publicGet(
|
|
1686
|
+
"/quote/v1/globalLongShortAccountRatio",
|
|
1687
|
+
compactObject({
|
|
1688
|
+
symbol: requireString(args, "symbol"),
|
|
1689
|
+
period: requireString(args, "period"),
|
|
1690
|
+
startTime: readNumber(args, "startTime"),
|
|
1691
|
+
endTime: readNumber(args, "endTime"),
|
|
1692
|
+
limit: readPositiveInt(args, "limit")
|
|
1693
|
+
}),
|
|
1694
|
+
publicRateLimit("market_get_long_short_ratio", 20)
|
|
1695
|
+
);
|
|
1696
|
+
return normalize3(response);
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
name: "market_get_contract_ticker_24hr",
|
|
1701
|
+
module: "market",
|
|
1702
|
+
description: "Get 24h price change statistics for a futures contract. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1703
|
+
isWrite: false,
|
|
1704
|
+
inputSchema: {
|
|
1705
|
+
type: "object",
|
|
1706
|
+
properties: {
|
|
1707
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT. Omit for all." }
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
handler: async (rawArgs, context) => {
|
|
1711
|
+
const args = asRecord(rawArgs);
|
|
1712
|
+
const response = await context.client.publicGet(
|
|
1713
|
+
"/quote/v1/contract/ticker/24hr",
|
|
1714
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1715
|
+
publicRateLimit("market_get_contract_ticker_24hr", 20)
|
|
1716
|
+
);
|
|
1717
|
+
return normalize3(response);
|
|
1718
|
+
}
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
name: "market_get_contract_ticker_price",
|
|
1722
|
+
module: "market",
|
|
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.",
|
|
1724
|
+
isWrite: false,
|
|
1725
|
+
inputSchema: {
|
|
1726
|
+
type: "object",
|
|
1727
|
+
properties: {
|
|
1728
|
+
symbol: { type: "string", description: "e.g. BTC-SWAP-USDT or BTCUSDT. Omit for all." }
|
|
1729
|
+
}
|
|
1730
|
+
},
|
|
1731
|
+
handler: async (rawArgs, context) => {
|
|
1732
|
+
const args = asRecord(rawArgs);
|
|
1733
|
+
const response = await context.client.publicGet(
|
|
1734
|
+
"/quote/v1/contract/ticker/price",
|
|
1735
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1736
|
+
publicRateLimit("market_get_contract_ticker_price", 20)
|
|
1737
|
+
);
|
|
1738
|
+
return normalize3(response);
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
name: "market_get_index_price",
|
|
1743
|
+
module: "market",
|
|
1744
|
+
description: "Get index price for a futures symbol. Public endpoint. Rate limit: 20 req/s.",
|
|
1745
|
+
isWrite: false,
|
|
1746
|
+
inputSchema: {
|
|
1747
|
+
type: "object",
|
|
1748
|
+
properties: {
|
|
1749
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all." }
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
handler: async (rawArgs, context) => {
|
|
1753
|
+
const args = asRecord(rawArgs);
|
|
1754
|
+
const response = await context.client.publicGet(
|
|
1755
|
+
"/quote/v1/index",
|
|
1756
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1757
|
+
publicRateLimit("market_get_index_price", 20)
|
|
1758
|
+
);
|
|
1759
|
+
return normalize3(response);
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1762
|
+
{
|
|
1763
|
+
name: "market_get_insurance_fund",
|
|
1764
|
+
module: "market",
|
|
1765
|
+
description: "Get insurance fund balance for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1766
|
+
isWrite: false,
|
|
1767
|
+
inputSchema: {
|
|
1768
|
+
type: "object",
|
|
1769
|
+
properties: {
|
|
1770
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
|
|
1771
|
+
},
|
|
1772
|
+
required: ["symbol"]
|
|
1773
|
+
},
|
|
1774
|
+
handler: async (rawArgs, context) => {
|
|
1775
|
+
const args = asRecord(rawArgs);
|
|
1776
|
+
const response = await context.client.publicGet(
|
|
1777
|
+
"/api/v1/futures/insuranceBySymbol",
|
|
1778
|
+
{ symbol: requireString(args, "symbol") },
|
|
1779
|
+
publicRateLimit("market_get_insurance_fund", 20)
|
|
1780
|
+
);
|
|
1781
|
+
return normalize3(response);
|
|
1782
|
+
}
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
name: "market_get_risk_limits",
|
|
1786
|
+
module: "market",
|
|
1787
|
+
description: "Get risk limits configuration for a futures symbol. Requires contract symbol format. Public endpoint. Rate limit: 20 req/s.",
|
|
1788
|
+
isWrite: false,
|
|
1789
|
+
inputSchema: {
|
|
1790
|
+
type: "object",
|
|
1791
|
+
properties: {
|
|
1792
|
+
symbol: { type: "string", description: "Futures contract symbol, e.g. BTC-SWAP-USDT" }
|
|
1793
|
+
},
|
|
1794
|
+
required: ["symbol"]
|
|
1795
|
+
},
|
|
1796
|
+
handler: async (rawArgs, context) => {
|
|
1797
|
+
const args = asRecord(rawArgs);
|
|
1798
|
+
const response = await context.client.publicGet(
|
|
1799
|
+
"/api/v1/futures/riskLimits",
|
|
1800
|
+
{ symbol: requireString(args, "symbol") },
|
|
1801
|
+
publicRateLimit("market_get_risk_limits", 20)
|
|
1802
|
+
);
|
|
1803
|
+
return normalize3(response);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
];
|
|
1807
|
+
}
|
|
1808
|
+
function normalize4(response) {
|
|
1809
|
+
return { endpoint: response.endpoint, requestTime: response.requestTime, data: response.data };
|
|
1810
|
+
}
|
|
1811
|
+
function registerSpotTradeTools() {
|
|
1812
|
+
return [
|
|
1813
|
+
{
|
|
1814
|
+
name: "spot_place_order",
|
|
1815
|
+
module: "spot",
|
|
1816
|
+
description: "Place a spot order (LIMIT, MARKET, LIMIT_MAKER). [CAUTION] Executes real trades. Private endpoint. Rate limit: 50 req/s.",
|
|
1817
|
+
isWrite: true,
|
|
1818
|
+
inputSchema: {
|
|
1819
|
+
type: "object",
|
|
1820
|
+
properties: {
|
|
1821
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1822
|
+
side: { type: "string", enum: ["BUY", "SELL"] },
|
|
1823
|
+
type: { type: "string", enum: ["LIMIT", "MARKET", "LIMIT_MAKER"], description: "Order type" },
|
|
1824
|
+
quantity: { type: "string", description: "Order quantity" },
|
|
1825
|
+
price: { type: "string", description: "Order price (required for LIMIT)" },
|
|
1826
|
+
newClientOrderId: { type: "string", description: "Client order ID" },
|
|
1827
|
+
timeInForce: { type: "string", enum: ["GTC", "IOC", "FOK"], description: "Default GTC" }
|
|
1828
|
+
},
|
|
1829
|
+
required: ["symbol", "side", "type", "quantity"]
|
|
1830
|
+
},
|
|
1831
|
+
handler: async (rawArgs, context) => {
|
|
1832
|
+
const args = asRecord(rawArgs);
|
|
1833
|
+
const response = await context.client.privatePost(
|
|
1834
|
+
"/api/v1/spot/order",
|
|
1835
|
+
compactObject({
|
|
1836
|
+
symbol: requireString(args, "symbol"),
|
|
1837
|
+
side: requireString(args, "side"),
|
|
1838
|
+
type: requireString(args, "type"),
|
|
1839
|
+
quantity: requireString(args, "quantity"),
|
|
1840
|
+
price: readString(args, "price"),
|
|
1841
|
+
newClientOrderId: readString(args, "newClientOrderId"),
|
|
1842
|
+
timeInForce: readString(args, "timeInForce")
|
|
1843
|
+
}),
|
|
1844
|
+
privateRateLimit("spot_place_order", 50)
|
|
1845
|
+
);
|
|
1846
|
+
return normalize4(response);
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
name: "spot_place_order_test",
|
|
1851
|
+
module: "spot",
|
|
1852
|
+
description: "Test spot order placement without actually submitting. Private endpoint. Rate limit: 50 req/s.",
|
|
1853
|
+
isWrite: false,
|
|
1854
|
+
inputSchema: {
|
|
1855
|
+
type: "object",
|
|
1856
|
+
properties: {
|
|
1857
|
+
symbol: { type: "string", description: "e.g. BTCUSDT" },
|
|
1858
|
+
side: { type: "string", enum: ["BUY", "SELL"] },
|
|
1859
|
+
type: { type: "string", enum: ["LIMIT", "MARKET", "LIMIT_MAKER"] },
|
|
1860
|
+
quantity: { type: "string" },
|
|
1861
|
+
price: { type: "string" }
|
|
1862
|
+
},
|
|
1863
|
+
required: ["symbol", "side", "type", "quantity"]
|
|
1864
|
+
},
|
|
1865
|
+
handler: async (rawArgs, context) => {
|
|
1866
|
+
const args = asRecord(rawArgs);
|
|
1867
|
+
const response = await context.client.privatePost(
|
|
1868
|
+
"/api/v1/spot/orderTest",
|
|
1869
|
+
compactObject({
|
|
1870
|
+
symbol: requireString(args, "symbol"),
|
|
1871
|
+
side: requireString(args, "side"),
|
|
1872
|
+
type: requireString(args, "type"),
|
|
1873
|
+
quantity: requireString(args, "quantity"),
|
|
1874
|
+
price: readString(args, "price")
|
|
1875
|
+
}),
|
|
1876
|
+
privateRateLimit("spot_place_order_test", 50)
|
|
1877
|
+
);
|
|
1878
|
+
return normalize4(response);
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
{
|
|
1882
|
+
name: "spot_batch_orders",
|
|
1883
|
+
module: "spot",
|
|
1884
|
+
description: "[CAUTION] Batch place spot orders. Private endpoint. Rate limit: 50 req/s.",
|
|
1885
|
+
isWrite: true,
|
|
1886
|
+
inputSchema: {
|
|
1887
|
+
type: "object",
|
|
1888
|
+
properties: {
|
|
1889
|
+
orders: {
|
|
1890
|
+
type: "array",
|
|
1891
|
+
description: "Array of orders: [{symbol, side, type, quantity, price?, newClientOrderId?, timeInForce?}]",
|
|
1892
|
+
items: { type: "object" }
|
|
1893
|
+
}
|
|
1894
|
+
},
|
|
1895
|
+
required: ["orders"]
|
|
1896
|
+
},
|
|
1897
|
+
handler: async (rawArgs, context) => {
|
|
1898
|
+
const args = asRecord(rawArgs);
|
|
1899
|
+
const orders = args.orders;
|
|
1900
|
+
if (!Array.isArray(orders) || orders.length === 0) throw new Error("orders must be a non-empty array.");
|
|
1901
|
+
const response = await context.client.privatePost(
|
|
1902
|
+
"/api/v1/spot/batchOrders",
|
|
1903
|
+
orders,
|
|
1904
|
+
privateRateLimit("spot_batch_orders", 50)
|
|
1905
|
+
);
|
|
1906
|
+
return normalize4(response);
|
|
1907
|
+
}
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
name: "spot_cancel_order",
|
|
1911
|
+
module: "spot",
|
|
1912
|
+
description: "Cancel a spot order by orderId or clientOrderId. Private endpoint. Rate limit: 50 req/s.",
|
|
1913
|
+
isWrite: true,
|
|
1914
|
+
inputSchema: {
|
|
1915
|
+
type: "object",
|
|
1916
|
+
properties: {
|
|
1917
|
+
orderId: { type: "string", description: "Order ID" },
|
|
1918
|
+
clientOrderId: { type: "string", description: "Client order ID" }
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
handler: async (rawArgs, context) => {
|
|
1922
|
+
const args = asRecord(rawArgs);
|
|
1923
|
+
const response = await context.client.privateDelete(
|
|
1924
|
+
"/api/v1/spot/order",
|
|
1925
|
+
compactObject({
|
|
1926
|
+
orderId: readString(args, "orderId"),
|
|
1927
|
+
clientOrderId: readString(args, "clientOrderId")
|
|
1928
|
+
}),
|
|
1929
|
+
privateRateLimit("spot_cancel_order", 50)
|
|
1930
|
+
);
|
|
1931
|
+
return normalize4(response);
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
name: "spot_cancel_open_orders",
|
|
1936
|
+
module: "spot",
|
|
1937
|
+
description: "Cancel all open spot orders for a symbol. [CAUTION] Private endpoint. Rate limit: 10 req/s.",
|
|
1938
|
+
isWrite: true,
|
|
1939
|
+
inputSchema: {
|
|
1940
|
+
type: "object",
|
|
1941
|
+
properties: {
|
|
1942
|
+
symbol: { type: "string", description: "e.g. BTCUSDT. Omit for all symbols." }
|
|
1943
|
+
}
|
|
1944
|
+
},
|
|
1945
|
+
handler: async (rawArgs, context) => {
|
|
1946
|
+
const args = asRecord(rawArgs);
|
|
1947
|
+
const response = await context.client.privateDelete(
|
|
1948
|
+
"/api/v1/spot/openOrders",
|
|
1949
|
+
compactObject({ symbol: readString(args, "symbol") }),
|
|
1950
|
+
privateRateLimit("spot_cancel_open_orders", 10)
|
|
1951
|
+
);
|
|
1952
|
+
return normalize4(response);
|
|
1953
|
+
}
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
name: "spot_cancel_order_by_ids",
|
|
1957
|
+
module: "spot",
|
|
1958
|
+
description: "Batch cancel spot orders by IDs. [CAUTION] Private endpoint. Rate limit: 50 req/s.",
|
|
1959
|
+
isWrite: true,
|
|
1960
|
+
inputSchema: {
|
|
1961
|
+
type: "object",
|
|
1962
|
+
properties: {
|
|
1963
|
+
orderIds: { type: "string", description: "Comma-separated order IDs" }
|
|
1964
|
+
},
|
|
1965
|
+
required: ["orderIds"]
|
|
1966
|
+
},
|
|
1967
|
+
handler: async (rawArgs, context) => {
|
|
1968
|
+
const args = asRecord(rawArgs);
|
|
1969
|
+
const response = await context.client.privateDelete(
|
|
1970
|
+
"/api/v1/spot/cancelOrderByIds",
|
|
1971
|
+
{ orderIds: requireString(args, "orderIds") },
|
|
1972
|
+
privateRateLimit("spot_cancel_order_by_ids", 50)
|
|
1973
|
+
);
|
|
1974
|
+
return normalize4(response);
|
|
1975
|
+
}
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
name: "spot_get_order",
|
|
1979
|
+
module: "spot",
|
|
1980
|
+
description: "Get details of a single spot order. Private endpoint. Rate limit: 20 req/s.",
|
|
1981
|
+
isWrite: false,
|
|
1982
|
+
inputSchema: {
|
|
1983
|
+
type: "object",
|
|
1984
|
+
properties: {
|
|
1985
|
+
orderId: { type: "string" },
|
|
1986
|
+
clientOrderId: { type: "string" }
|
|
1987
|
+
}
|
|
1988
|
+
},
|
|
1989
|
+
handler: async (rawArgs, context) => {
|
|
1990
|
+
const args = asRecord(rawArgs);
|
|
1991
|
+
const response = await context.client.privateGet(
|
|
1992
|
+
"/api/v1/spot/order",
|
|
1993
|
+
compactObject({ orderId: readString(args, "orderId"), clientOrderId: readString(args, "clientOrderId") }),
|
|
1994
|
+
privateRateLimit("spot_get_order", 20)
|
|
1995
|
+
);
|
|
1996
|
+
return normalize4(response);
|
|
1997
|
+
}
|
|
1998
|
+
},
|
|
1999
|
+
{
|
|
2000
|
+
name: "spot_get_open_orders",
|
|
2001
|
+
module: "spot",
|
|
2002
|
+
description: "Get current open spot orders. Private endpoint. Rate limit: 20 req/s.",
|
|
2003
|
+
isWrite: false,
|
|
2004
|
+
inputSchema: {
|
|
2005
|
+
type: "object",
|
|
2006
|
+
properties: {
|
|
2007
|
+
symbol: { type: "string" },
|
|
2008
|
+
orderId: { type: "string", description: "Filter orders >= this ID" },
|
|
2009
|
+
limit: { type: "number", description: "Default 500, max 1000" }
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
handler: async (rawArgs, context) => {
|
|
2013
|
+
const args = asRecord(rawArgs);
|
|
2014
|
+
const response = await context.client.privateGet(
|
|
2015
|
+
"/api/v1/spot/openOrders",
|
|
2016
|
+
compactObject({ symbol: readString(args, "symbol"), orderId: readString(args, "orderId"), limit: readNumber(args, "limit") }),
|
|
2017
|
+
privateRateLimit("spot_get_open_orders", 20)
|
|
2018
|
+
);
|
|
2019
|
+
return normalize4(response);
|
|
2020
|
+
}
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
name: "spot_get_trade_orders",
|
|
2024
|
+
module: "spot",
|
|
2025
|
+
description: "Get all spot orders (history). Private endpoint. Rate limit: 20 req/s.",
|
|
2026
|
+
isWrite: false,
|
|
2027
|
+
inputSchema: {
|
|
2028
|
+
type: "object",
|
|
2029
|
+
properties: {
|
|
2030
|
+
symbol: { type: "string" },
|
|
2031
|
+
orderId: { type: "string" },
|
|
2032
|
+
startTime: { type: "number" },
|
|
2033
|
+
endTime: { type: "number" },
|
|
2034
|
+
limit: { type: "number", description: "Default 500, max 1000" }
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
handler: async (rawArgs, context) => {
|
|
2038
|
+
const args = asRecord(rawArgs);
|
|
2039
|
+
const response = await context.client.privateGet(
|
|
2040
|
+
"/api/v1/spot/tradeOrders",
|
|
2041
|
+
compactObject({
|
|
2042
|
+
symbol: readString(args, "symbol"),
|
|
2043
|
+
orderId: readString(args, "orderId"),
|
|
2044
|
+
startTime: readNumber(args, "startTime"),
|
|
2045
|
+
endTime: readNumber(args, "endTime"),
|
|
2046
|
+
limit: readNumber(args, "limit")
|
|
2047
|
+
}),
|
|
2048
|
+
privateRateLimit("spot_get_trade_orders", 20)
|
|
2049
|
+
);
|
|
2050
|
+
return normalize4(response);
|
|
2051
|
+
}
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
name: "spot_get_fills",
|
|
2055
|
+
module: "spot",
|
|
2056
|
+
description: "Get spot trade history (fills). Private endpoint. Rate limit: 20 req/s.",
|
|
2057
|
+
isWrite: false,
|
|
2058
|
+
inputSchema: {
|
|
2059
|
+
type: "object",
|
|
2060
|
+
properties: {
|
|
2061
|
+
symbol: { type: "string" },
|
|
2062
|
+
fromId: { type: "string" },
|
|
2063
|
+
toId: { type: "string" },
|
|
2064
|
+
startTime: { type: "number" },
|
|
2065
|
+
endTime: { type: "number" },
|
|
2066
|
+
limit: { type: "number", description: "Default 500, max 1000" }
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
handler: async (rawArgs, context) => {
|
|
2070
|
+
const args = asRecord(rawArgs);
|
|
2071
|
+
const response = await context.client.privateGet(
|
|
2072
|
+
"/api/v1/account/trades",
|
|
2073
|
+
compactObject({
|
|
2074
|
+
symbol: readString(args, "symbol"),
|
|
2075
|
+
fromId: readString(args, "fromId"),
|
|
2076
|
+
toId: readString(args, "toId"),
|
|
2077
|
+
startTime: readNumber(args, "startTime"),
|
|
2078
|
+
endTime: readNumber(args, "endTime"),
|
|
2079
|
+
limit: readNumber(args, "limit")
|
|
2080
|
+
}),
|
|
2081
|
+
privateRateLimit("spot_get_fills", 20)
|
|
2082
|
+
);
|
|
2083
|
+
return normalize4(response);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
];
|
|
2087
|
+
}
|
|
2088
|
+
function allToolSpecs() {
|
|
2089
|
+
return [
|
|
2090
|
+
...registerMarketTools(),
|
|
2091
|
+
...registerSpotTradeTools(),
|
|
2092
|
+
...registerFuturesTools(),
|
|
2093
|
+
...registerAccountTools(),
|
|
2094
|
+
...registerAuditTools()
|
|
2095
|
+
];
|
|
2096
|
+
}
|
|
2097
|
+
function createToolRunner(client, config) {
|
|
2098
|
+
const fullConfig = { ...config, modules: [...MODULES] };
|
|
2099
|
+
const tools = allToolSpecs();
|
|
2100
|
+
const toolMap = new Map(tools.map((t) => [t.name, t]));
|
|
2101
|
+
return async (toolName, args) => {
|
|
2102
|
+
const tool = toolMap.get(toolName);
|
|
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
|
+
}
|
|
2107
|
+
const result = await tool.handler(args, { config: fullConfig, client });
|
|
2108
|
+
return result;
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
function configFilePath() {
|
|
2112
|
+
return join2(homedir2(), ".toobit", "config.toml");
|
|
2113
|
+
}
|
|
2114
|
+
function readFullConfig() {
|
|
2115
|
+
const path4 = configFilePath();
|
|
2116
|
+
if (!existsSync2(path4)) return { profiles: {} };
|
|
2117
|
+
const raw = readFileSync2(path4, "utf-8");
|
|
2118
|
+
return parse(raw);
|
|
2119
|
+
}
|
|
2120
|
+
function readTomlProfile(profileName) {
|
|
2121
|
+
const config = readFullConfig();
|
|
2122
|
+
const name = profileName ?? config.default_profile ?? "default";
|
|
2123
|
+
return config.profiles?.[name] ?? {};
|
|
2124
|
+
}
|
|
2125
|
+
function writeFullConfig(config) {
|
|
2126
|
+
const path4 = configFilePath();
|
|
2127
|
+
const dir = dirname(path4);
|
|
2128
|
+
if (!existsSync2(dir)) {
|
|
2129
|
+
mkdirSync(dir, { recursive: true });
|
|
2130
|
+
}
|
|
2131
|
+
writeFileSync(path4, stringify(config), "utf-8");
|
|
2132
|
+
}
|
|
2133
|
+
function parseModuleList(rawModules) {
|
|
2134
|
+
if (!rawModules || rawModules.trim().length === 0) {
|
|
2135
|
+
return [...DEFAULT_MODULES];
|
|
2136
|
+
}
|
|
2137
|
+
const trimmed = rawModules.trim().toLowerCase();
|
|
2138
|
+
if (trimmed === "all") {
|
|
2139
|
+
return [...MODULES];
|
|
2140
|
+
}
|
|
2141
|
+
const requested = trimmed.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
2142
|
+
if (requested.length === 0) {
|
|
2143
|
+
return [...DEFAULT_MODULES];
|
|
2144
|
+
}
|
|
2145
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
2146
|
+
for (const moduleId of requested) {
|
|
2147
|
+
if (!MODULES.includes(moduleId)) {
|
|
2148
|
+
throw new ConfigError(
|
|
2149
|
+
`Unknown module "${moduleId}".`,
|
|
2150
|
+
`Use one of: ${MODULES.join(", ")}, or "all".`
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
deduped.add(moduleId);
|
|
2154
|
+
}
|
|
2155
|
+
return Array.from(deduped);
|
|
2156
|
+
}
|
|
2157
|
+
function loadConfig(cli) {
|
|
2158
|
+
const toml = readTomlProfile(cli.profile);
|
|
2159
|
+
const apiKey = process.env.TOOBIT_API_KEY?.trim() ?? toml.api_key;
|
|
2160
|
+
const secretKey = process.env.TOOBIT_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
2161
|
+
const hasAuth = Boolean(apiKey && secretKey);
|
|
2162
|
+
const partialAuth = Boolean(apiKey) || Boolean(secretKey);
|
|
2163
|
+
if (partialAuth && !hasAuth) {
|
|
2164
|
+
throw new ConfigError(
|
|
2165
|
+
"Partial API credentials detected.",
|
|
2166
|
+
"Set TOOBIT_API_KEY and TOOBIT_SECRET_KEY together (env vars or config.toml profile)."
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
const rawBaseUrl = process.env.TOOBIT_API_BASE_URL?.trim() ?? toml.base_url ?? TOOBIT_API_BASE_URL;
|
|
2170
|
+
if (!rawBaseUrl.startsWith("http://") && !rawBaseUrl.startsWith("https://")) {
|
|
2171
|
+
throw new ConfigError(
|
|
2172
|
+
`Invalid base URL "${rawBaseUrl}".`,
|
|
2173
|
+
"TOOBIT_API_BASE_URL must start with http:// or https://"
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
2177
|
+
const rawTimeout = process.env.TOOBIT_TIMEOUT_MS ? Number(process.env.TOOBIT_TIMEOUT_MS) : toml.timeout_ms ?? 15e3;
|
|
2178
|
+
if (!Number.isFinite(rawTimeout) || rawTimeout <= 0) {
|
|
2179
|
+
throw new ConfigError(
|
|
2180
|
+
`Invalid timeout value "${rawTimeout}".`,
|
|
2181
|
+
"Set TOOBIT_TIMEOUT_MS as a positive integer in milliseconds."
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
apiKey,
|
|
2186
|
+
secretKey,
|
|
2187
|
+
hasAuth,
|
|
2188
|
+
baseUrl,
|
|
2189
|
+
timeoutMs: Math.floor(rawTimeout),
|
|
2190
|
+
modules: parseModuleList(cli.modules),
|
|
2191
|
+
readOnly: cli.readOnly,
|
|
2192
|
+
userAgent: cli.userAgent,
|
|
2193
|
+
sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
var CLIENT_NAMES = {
|
|
2197
|
+
"claude-desktop": "Claude Desktop",
|
|
2198
|
+
cursor: "Cursor",
|
|
2199
|
+
windsurf: "Windsurf",
|
|
2200
|
+
vscode: "VS Code",
|
|
2201
|
+
"claude-code": "Claude Code CLI"
|
|
2202
|
+
};
|
|
2203
|
+
var SUPPORTED_CLIENTS = Object.keys(CLIENT_NAMES);
|
|
11
2204
|
|
|
12
2205
|
// src/parser.ts
|
|
13
2206
|
import { parseArgs } from "util";
|
|
@@ -91,11 +2284,58 @@ function parseCli(argv = process.argv.slice(2)) {
|
|
|
91
2284
|
}
|
|
92
2285
|
|
|
93
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
|
+
}
|
|
94
2297
|
function formatJson(data, json) {
|
|
95
2298
|
if (json) return JSON.stringify(data, null, 2);
|
|
96
2299
|
if (data === null || data === void 0) return "No data.";
|
|
97
2300
|
if (typeof data !== "object") return String(data);
|
|
98
|
-
|
|
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}`;
|
|
99
2339
|
}
|
|
100
2340
|
|
|
101
2341
|
// src/commands/market.ts
|
|
@@ -158,9 +2398,18 @@ async function handleMarketCommand(cli, run) {
|
|
|
158
2398
|
case "long-short-ratio":
|
|
159
2399
|
result = await run("market_get_long_short_ratio", { symbol: f.symbol, period: f.period ?? "1h" });
|
|
160
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;
|
|
161
2410
|
default:
|
|
162
2411
|
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
|
|
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
|
|
164
2413
|
`);
|
|
165
2414
|
return;
|
|
166
2415
|
}
|
|
@@ -345,11 +2594,6 @@ Available: balance, info, balance-flow, sub-accounts, check-api-key, deposit-add
|
|
|
345
2594
|
|
|
346
2595
|
// src/commands/config.ts
|
|
347
2596
|
import * as readline from "readline";
|
|
348
|
-
import {
|
|
349
|
-
configFilePath,
|
|
350
|
-
readFullConfig,
|
|
351
|
-
writeFullConfig
|
|
352
|
-
} from "@toobit_agent/agent-toobitkit-core";
|
|
353
2597
|
function prompt(question) {
|
|
354
2598
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
355
2599
|
return new Promise((resolve) => {
|
|
@@ -429,7 +2673,7 @@ Commands:
|
|
|
429
2673
|
config Configuration management (init, show, list)
|
|
430
2674
|
|
|
431
2675
|
Global Options:
|
|
432
|
-
--profile <name> Profile from ${
|
|
2676
|
+
--profile <name> Profile from ${configFilePath()}
|
|
433
2677
|
--json Output raw JSON
|
|
434
2678
|
--read-only Disable write operations
|
|
435
2679
|
--help Show this help
|