@pionex/pionex-ai-kit 0.2.42 → 0.2.44
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/chunk-NDTSX4HU.js +2900 -0
- package/dist/chunk-NDTSX4HU.js.map +1 -0
- package/dist/index.js +10 -3561
- package/dist/index.js.map +1 -1
- package/dist/kit-7X7UVP4Q.js +101 -0
- package/dist/kit-7X7UVP4Q.js.map +1 -0
- package/dist/trade-A4O6JDLZ.js +786 -0
- package/dist/trade-A4O6JDLZ.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,2900 @@
|
|
|
1
|
+
// ../core/dist/index.js
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
|
|
6
|
+
// ../../node_modules/smol-toml/dist/error.js
|
|
7
|
+
function getLineColFromPtr(string, ptr) {
|
|
8
|
+
let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
9
|
+
return [lines.length, lines.pop().length + 1];
|
|
10
|
+
}
|
|
11
|
+
function makeCodeBlock(string, line, column) {
|
|
12
|
+
let lines = string.split(/\r\n|\n|\r/g);
|
|
13
|
+
let codeblock = "";
|
|
14
|
+
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
15
|
+
for (let i = line - 1; i <= line + 1; i++) {
|
|
16
|
+
let l = lines[i - 1];
|
|
17
|
+
if (!l)
|
|
18
|
+
continue;
|
|
19
|
+
codeblock += i.toString().padEnd(numberLen, " ");
|
|
20
|
+
codeblock += ": ";
|
|
21
|
+
codeblock += l;
|
|
22
|
+
codeblock += "\n";
|
|
23
|
+
if (i === line) {
|
|
24
|
+
codeblock += " ".repeat(numberLen + column + 2);
|
|
25
|
+
codeblock += "^\n";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return codeblock;
|
|
29
|
+
}
|
|
30
|
+
var TomlError = class extends Error {
|
|
31
|
+
line;
|
|
32
|
+
column;
|
|
33
|
+
codeblock;
|
|
34
|
+
constructor(message, options) {
|
|
35
|
+
const [line, column] = getLineColFromPtr(options.toml, options.ptr);
|
|
36
|
+
const codeblock = makeCodeBlock(options.toml, line, column);
|
|
37
|
+
super(`Invalid TOML document: ${message}
|
|
38
|
+
|
|
39
|
+
${codeblock}`, options);
|
|
40
|
+
this.line = line;
|
|
41
|
+
this.column = column;
|
|
42
|
+
this.codeblock = codeblock;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ../../node_modules/smol-toml/dist/util.js
|
|
47
|
+
function isEscaped(str, ptr) {
|
|
48
|
+
let i = 0;
|
|
49
|
+
while (str[ptr - ++i] === "\\")
|
|
50
|
+
;
|
|
51
|
+
return --i && i % 2;
|
|
52
|
+
}
|
|
53
|
+
function indexOfNewline(str, start = 0, end = str.length) {
|
|
54
|
+
let idx = str.indexOf("\n", start);
|
|
55
|
+
if (str[idx - 1] === "\r")
|
|
56
|
+
idx--;
|
|
57
|
+
return idx <= end ? idx : -1;
|
|
58
|
+
}
|
|
59
|
+
function skipComment(str, ptr) {
|
|
60
|
+
for (let i = ptr; i < str.length; i++) {
|
|
61
|
+
let c = str[i];
|
|
62
|
+
if (c === "\n")
|
|
63
|
+
return i;
|
|
64
|
+
if (c === "\r" && str[i + 1] === "\n")
|
|
65
|
+
return i + 1;
|
|
66
|
+
if (c < " " && c !== " " || c === "\x7F") {
|
|
67
|
+
throw new TomlError("control characters are not allowed in comments", {
|
|
68
|
+
toml: str,
|
|
69
|
+
ptr
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return str.length;
|
|
74
|
+
}
|
|
75
|
+
function skipVoid(str, ptr, banNewLines, banComments) {
|
|
76
|
+
let c;
|
|
77
|
+
while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
|
|
78
|
+
ptr++;
|
|
79
|
+
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
80
|
+
}
|
|
81
|
+
function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
82
|
+
if (!end) {
|
|
83
|
+
ptr = indexOfNewline(str, ptr);
|
|
84
|
+
return ptr < 0 ? str.length : ptr;
|
|
85
|
+
}
|
|
86
|
+
for (let i = ptr; i < str.length; i++) {
|
|
87
|
+
let c = str[i];
|
|
88
|
+
if (c === "#") {
|
|
89
|
+
i = indexOfNewline(str, i);
|
|
90
|
+
} else if (c === sep) {
|
|
91
|
+
return i + 1;
|
|
92
|
+
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
93
|
+
return i;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
throw new TomlError("cannot find end of structure", {
|
|
97
|
+
toml: str,
|
|
98
|
+
ptr
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function getStringEnd(str, seek) {
|
|
102
|
+
let first = str[seek];
|
|
103
|
+
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first;
|
|
104
|
+
seek += target.length - 1;
|
|
105
|
+
do
|
|
106
|
+
seek = str.indexOf(target, ++seek);
|
|
107
|
+
while (seek > -1 && first !== "'" && isEscaped(str, seek));
|
|
108
|
+
if (seek > -1) {
|
|
109
|
+
seek += target.length;
|
|
110
|
+
if (target.length > 1) {
|
|
111
|
+
if (str[seek] === first)
|
|
112
|
+
seek++;
|
|
113
|
+
if (str[seek] === first)
|
|
114
|
+
seek++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return seek;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ../../node_modules/smol-toml/dist/date.js
|
|
121
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
122
|
+
var TomlDate = class _TomlDate extends Date {
|
|
123
|
+
#hasDate = false;
|
|
124
|
+
#hasTime = false;
|
|
125
|
+
#offset = null;
|
|
126
|
+
constructor(date) {
|
|
127
|
+
let hasDate = true;
|
|
128
|
+
let hasTime = true;
|
|
129
|
+
let offset = "Z";
|
|
130
|
+
if (typeof date === "string") {
|
|
131
|
+
let match = date.match(DATE_TIME_RE);
|
|
132
|
+
if (match) {
|
|
133
|
+
if (!match[1]) {
|
|
134
|
+
hasDate = false;
|
|
135
|
+
date = `0000-01-01T${date}`;
|
|
136
|
+
}
|
|
137
|
+
hasTime = !!match[2];
|
|
138
|
+
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
139
|
+
if (match[2] && +match[2] > 23) {
|
|
140
|
+
date = "";
|
|
141
|
+
} else {
|
|
142
|
+
offset = match[3] || null;
|
|
143
|
+
date = date.toUpperCase();
|
|
144
|
+
if (!offset && hasTime)
|
|
145
|
+
date += "Z";
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
date = "";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
super(date);
|
|
152
|
+
if (!isNaN(this.getTime())) {
|
|
153
|
+
this.#hasDate = hasDate;
|
|
154
|
+
this.#hasTime = hasTime;
|
|
155
|
+
this.#offset = offset;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
isDateTime() {
|
|
159
|
+
return this.#hasDate && this.#hasTime;
|
|
160
|
+
}
|
|
161
|
+
isLocal() {
|
|
162
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
163
|
+
}
|
|
164
|
+
isDate() {
|
|
165
|
+
return this.#hasDate && !this.#hasTime;
|
|
166
|
+
}
|
|
167
|
+
isTime() {
|
|
168
|
+
return this.#hasTime && !this.#hasDate;
|
|
169
|
+
}
|
|
170
|
+
isValid() {
|
|
171
|
+
return this.#hasDate || this.#hasTime;
|
|
172
|
+
}
|
|
173
|
+
toISOString() {
|
|
174
|
+
let iso = super.toISOString();
|
|
175
|
+
if (this.isDate())
|
|
176
|
+
return iso.slice(0, 10);
|
|
177
|
+
if (this.isTime())
|
|
178
|
+
return iso.slice(11, 23);
|
|
179
|
+
if (this.#offset === null)
|
|
180
|
+
return iso.slice(0, -1);
|
|
181
|
+
if (this.#offset === "Z")
|
|
182
|
+
return iso;
|
|
183
|
+
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
184
|
+
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
185
|
+
let offsetDate = new Date(this.getTime() - offset * 6e4);
|
|
186
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
187
|
+
}
|
|
188
|
+
static wrapAsOffsetDateTime(jsDate, offset = "Z") {
|
|
189
|
+
let date = new _TomlDate(jsDate);
|
|
190
|
+
date.#offset = offset;
|
|
191
|
+
return date;
|
|
192
|
+
}
|
|
193
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
194
|
+
let date = new _TomlDate(jsDate);
|
|
195
|
+
date.#offset = null;
|
|
196
|
+
return date;
|
|
197
|
+
}
|
|
198
|
+
static wrapAsLocalDate(jsDate) {
|
|
199
|
+
let date = new _TomlDate(jsDate);
|
|
200
|
+
date.#hasTime = false;
|
|
201
|
+
date.#offset = null;
|
|
202
|
+
return date;
|
|
203
|
+
}
|
|
204
|
+
static wrapAsLocalTime(jsDate) {
|
|
205
|
+
let date = new _TomlDate(jsDate);
|
|
206
|
+
date.#hasDate = false;
|
|
207
|
+
date.#offset = null;
|
|
208
|
+
return date;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// ../../node_modules/smol-toml/dist/primitive.js
|
|
213
|
+
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
214
|
+
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
215
|
+
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
216
|
+
var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
|
|
217
|
+
var ESC_MAP = {
|
|
218
|
+
b: "\b",
|
|
219
|
+
t: " ",
|
|
220
|
+
n: "\n",
|
|
221
|
+
f: "\f",
|
|
222
|
+
r: "\r",
|
|
223
|
+
e: "\x1B",
|
|
224
|
+
'"': '"',
|
|
225
|
+
"\\": "\\"
|
|
226
|
+
};
|
|
227
|
+
function parseString(str, ptr = 0, endPtr = str.length) {
|
|
228
|
+
let isLiteral = str[ptr] === "'";
|
|
229
|
+
let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
|
|
230
|
+
if (isMultiline) {
|
|
231
|
+
endPtr -= 2;
|
|
232
|
+
if (str[ptr += 2] === "\r")
|
|
233
|
+
ptr++;
|
|
234
|
+
if (str[ptr] === "\n")
|
|
235
|
+
ptr++;
|
|
236
|
+
}
|
|
237
|
+
let tmp = 0;
|
|
238
|
+
let isEscape;
|
|
239
|
+
let parsed = "";
|
|
240
|
+
let sliceStart = ptr;
|
|
241
|
+
while (ptr < endPtr - 1) {
|
|
242
|
+
let c = str[ptr++];
|
|
243
|
+
if (c === "\n" || c === "\r" && str[ptr] === "\n") {
|
|
244
|
+
if (!isMultiline) {
|
|
245
|
+
throw new TomlError("newlines are not allowed in strings", {
|
|
246
|
+
toml: str,
|
|
247
|
+
ptr: ptr - 1
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
} else if (c < " " && c !== " " || c === "\x7F") {
|
|
251
|
+
throw new TomlError("control characters are not allowed in strings", {
|
|
252
|
+
toml: str,
|
|
253
|
+
ptr: ptr - 1
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (isEscape) {
|
|
257
|
+
isEscape = false;
|
|
258
|
+
if (c === "x" || c === "u" || c === "U") {
|
|
259
|
+
let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
|
|
260
|
+
if (!ESCAPE_REGEX.test(code)) {
|
|
261
|
+
throw new TomlError("invalid unicode escape", {
|
|
262
|
+
toml: str,
|
|
263
|
+
ptr: tmp
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
parsed += String.fromCodePoint(parseInt(code, 16));
|
|
268
|
+
} catch {
|
|
269
|
+
throw new TomlError("invalid unicode escape", {
|
|
270
|
+
toml: str,
|
|
271
|
+
ptr: tmp
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
} else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) {
|
|
275
|
+
ptr = skipVoid(str, ptr - 1, true);
|
|
276
|
+
if (str[ptr] !== "\n" && str[ptr] !== "\r") {
|
|
277
|
+
throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
|
|
278
|
+
toml: str,
|
|
279
|
+
ptr: tmp
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
ptr = skipVoid(str, ptr);
|
|
283
|
+
} else if (c in ESC_MAP) {
|
|
284
|
+
parsed += ESC_MAP[c];
|
|
285
|
+
} else {
|
|
286
|
+
throw new TomlError("unrecognized escape sequence", {
|
|
287
|
+
toml: str,
|
|
288
|
+
ptr: tmp
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
sliceStart = ptr;
|
|
292
|
+
} else if (!isLiteral && c === "\\") {
|
|
293
|
+
tmp = ptr - 1;
|
|
294
|
+
isEscape = true;
|
|
295
|
+
parsed += str.slice(sliceStart, tmp);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return parsed + str.slice(sliceStart, endPtr - 1);
|
|
299
|
+
}
|
|
300
|
+
function parseValue(value, toml, ptr, integersAsBigInt) {
|
|
301
|
+
if (value === "true")
|
|
302
|
+
return true;
|
|
303
|
+
if (value === "false")
|
|
304
|
+
return false;
|
|
305
|
+
if (value === "-inf")
|
|
306
|
+
return -Infinity;
|
|
307
|
+
if (value === "inf" || value === "+inf")
|
|
308
|
+
return Infinity;
|
|
309
|
+
if (value === "nan" || value === "+nan" || value === "-nan")
|
|
310
|
+
return NaN;
|
|
311
|
+
if (value === "-0")
|
|
312
|
+
return integersAsBigInt ? 0n : 0;
|
|
313
|
+
let isInt = INT_REGEX.test(value);
|
|
314
|
+
if (isInt || FLOAT_REGEX.test(value)) {
|
|
315
|
+
if (LEADING_ZERO.test(value)) {
|
|
316
|
+
throw new TomlError("leading zeroes are not allowed", {
|
|
317
|
+
toml,
|
|
318
|
+
ptr
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
value = value.replace(/_/g, "");
|
|
322
|
+
let numeric = +value;
|
|
323
|
+
if (isNaN(numeric)) {
|
|
324
|
+
throw new TomlError("invalid number", {
|
|
325
|
+
toml,
|
|
326
|
+
ptr
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
if (isInt) {
|
|
330
|
+
if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
|
|
331
|
+
throw new TomlError("integer value cannot be represented losslessly", {
|
|
332
|
+
toml,
|
|
333
|
+
ptr
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (isInt || integersAsBigInt === true)
|
|
337
|
+
numeric = BigInt(value);
|
|
338
|
+
}
|
|
339
|
+
return numeric;
|
|
340
|
+
}
|
|
341
|
+
const date = new TomlDate(value);
|
|
342
|
+
if (!date.isValid()) {
|
|
343
|
+
throw new TomlError("invalid value", {
|
|
344
|
+
toml,
|
|
345
|
+
ptr
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return date;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ../../node_modules/smol-toml/dist/extract.js
|
|
352
|
+
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
353
|
+
let value = str.slice(startPtr, endPtr);
|
|
354
|
+
let commentIdx = value.indexOf("#");
|
|
355
|
+
if (commentIdx > -1) {
|
|
356
|
+
skipComment(str, commentIdx);
|
|
357
|
+
value = value.slice(0, commentIdx);
|
|
358
|
+
}
|
|
359
|
+
return [value.trimEnd(), commentIdx];
|
|
360
|
+
}
|
|
361
|
+
function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
362
|
+
if (depth === 0) {
|
|
363
|
+
throw new TomlError("document contains excessively nested structures. aborting.", {
|
|
364
|
+
toml: str,
|
|
365
|
+
ptr
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
let c = str[ptr];
|
|
369
|
+
if (c === "[" || c === "{") {
|
|
370
|
+
let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
|
|
371
|
+
if (end) {
|
|
372
|
+
endPtr2 = skipVoid(str, endPtr2);
|
|
373
|
+
if (str[endPtr2] === ",")
|
|
374
|
+
endPtr2++;
|
|
375
|
+
else if (str[endPtr2] !== end) {
|
|
376
|
+
throw new TomlError("expected comma or end of structure", {
|
|
377
|
+
toml: str,
|
|
378
|
+
ptr: endPtr2
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return [value, endPtr2];
|
|
383
|
+
}
|
|
384
|
+
let endPtr;
|
|
385
|
+
if (c === '"' || c === "'") {
|
|
386
|
+
endPtr = getStringEnd(str, ptr);
|
|
387
|
+
let parsed = parseString(str, ptr, endPtr);
|
|
388
|
+
if (end) {
|
|
389
|
+
endPtr = skipVoid(str, endPtr);
|
|
390
|
+
if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") {
|
|
391
|
+
throw new TomlError("unexpected character encountered", {
|
|
392
|
+
toml: str,
|
|
393
|
+
ptr: endPtr
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
endPtr += +(str[endPtr] === ",");
|
|
397
|
+
}
|
|
398
|
+
return [parsed, endPtr];
|
|
399
|
+
}
|
|
400
|
+
endPtr = skipUntil(str, ptr, ",", end);
|
|
401
|
+
let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
|
|
402
|
+
if (!slice[0]) {
|
|
403
|
+
throw new TomlError("incomplete key-value declaration: no value specified", {
|
|
404
|
+
toml: str,
|
|
405
|
+
ptr
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
if (end && slice[1] > -1) {
|
|
409
|
+
endPtr = skipVoid(str, ptr + slice[1]);
|
|
410
|
+
endPtr += +(str[endPtr] === ",");
|
|
411
|
+
}
|
|
412
|
+
return [
|
|
413
|
+
parseValue(slice[0], str, ptr, integersAsBigInt),
|
|
414
|
+
endPtr
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ../../node_modules/smol-toml/dist/struct.js
|
|
419
|
+
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
420
|
+
function parseKey(str, ptr, end = "=") {
|
|
421
|
+
let dot = ptr - 1;
|
|
422
|
+
let parsed = [];
|
|
423
|
+
let endPtr = str.indexOf(end, ptr);
|
|
424
|
+
if (endPtr < 0) {
|
|
425
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
426
|
+
toml: str,
|
|
427
|
+
ptr
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
do {
|
|
431
|
+
let c = str[ptr = ++dot];
|
|
432
|
+
if (c !== " " && c !== " ") {
|
|
433
|
+
if (c === '"' || c === "'") {
|
|
434
|
+
if (c === str[ptr + 1] && c === str[ptr + 2]) {
|
|
435
|
+
throw new TomlError("multiline strings are not allowed in keys", {
|
|
436
|
+
toml: str,
|
|
437
|
+
ptr
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
let eos = getStringEnd(str, ptr);
|
|
441
|
+
if (eos < 0) {
|
|
442
|
+
throw new TomlError("unfinished string encountered", {
|
|
443
|
+
toml: str,
|
|
444
|
+
ptr
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
dot = str.indexOf(".", eos);
|
|
448
|
+
let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
449
|
+
let newLine = indexOfNewline(strEnd);
|
|
450
|
+
if (newLine > -1) {
|
|
451
|
+
throw new TomlError("newlines are not allowed in keys", {
|
|
452
|
+
toml: str,
|
|
453
|
+
ptr: ptr + dot + newLine
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (strEnd.trimStart()) {
|
|
457
|
+
throw new TomlError("found extra tokens after the string part", {
|
|
458
|
+
toml: str,
|
|
459
|
+
ptr: eos
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (endPtr < eos) {
|
|
463
|
+
endPtr = str.indexOf(end, eos);
|
|
464
|
+
if (endPtr < 0) {
|
|
465
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
466
|
+
toml: str,
|
|
467
|
+
ptr
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
parsed.push(parseString(str, ptr, eos));
|
|
472
|
+
} else {
|
|
473
|
+
dot = str.indexOf(".", ptr);
|
|
474
|
+
let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
475
|
+
if (!KEY_PART_RE.test(part)) {
|
|
476
|
+
throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
|
|
477
|
+
toml: str,
|
|
478
|
+
ptr
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
parsed.push(part.trimEnd());
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} while (dot + 1 && dot < endPtr);
|
|
485
|
+
return [parsed, skipVoid(str, endPtr + 1, true, true)];
|
|
486
|
+
}
|
|
487
|
+
function parseInlineTable(str, ptr, depth, integersAsBigInt) {
|
|
488
|
+
let res = {};
|
|
489
|
+
let seen = /* @__PURE__ */ new Set();
|
|
490
|
+
let c;
|
|
491
|
+
ptr++;
|
|
492
|
+
while ((c = str[ptr++]) !== "}" && c) {
|
|
493
|
+
if (c === ",") {
|
|
494
|
+
throw new TomlError("expected value, found comma", {
|
|
495
|
+
toml: str,
|
|
496
|
+
ptr: ptr - 1
|
|
497
|
+
});
|
|
498
|
+
} else if (c === "#")
|
|
499
|
+
ptr = skipComment(str, ptr);
|
|
500
|
+
else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
|
|
501
|
+
let k;
|
|
502
|
+
let t = res;
|
|
503
|
+
let hasOwn = false;
|
|
504
|
+
let [key, keyEndPtr] = parseKey(str, ptr - 1);
|
|
505
|
+
for (let i = 0; i < key.length; i++) {
|
|
506
|
+
if (i)
|
|
507
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
508
|
+
k = key[i];
|
|
509
|
+
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
|
|
510
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
511
|
+
toml: str,
|
|
512
|
+
ptr
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
if (!hasOwn && k === "__proto__") {
|
|
516
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (hasOwn) {
|
|
520
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
521
|
+
toml: str,
|
|
522
|
+
ptr
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
|
|
526
|
+
seen.add(value);
|
|
527
|
+
t[k] = value;
|
|
528
|
+
ptr = valueEndPtr;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (!c) {
|
|
532
|
+
throw new TomlError("unfinished table encountered", {
|
|
533
|
+
toml: str,
|
|
534
|
+
ptr
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return [res, ptr];
|
|
538
|
+
}
|
|
539
|
+
function parseArray(str, ptr, depth, integersAsBigInt) {
|
|
540
|
+
let res = [];
|
|
541
|
+
let c;
|
|
542
|
+
ptr++;
|
|
543
|
+
while ((c = str[ptr++]) !== "]" && c) {
|
|
544
|
+
if (c === ",") {
|
|
545
|
+
throw new TomlError("expected value, found comma", {
|
|
546
|
+
toml: str,
|
|
547
|
+
ptr: ptr - 1
|
|
548
|
+
});
|
|
549
|
+
} else if (c === "#")
|
|
550
|
+
ptr = skipComment(str, ptr);
|
|
551
|
+
else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
|
|
552
|
+
let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
|
|
553
|
+
res.push(e[0]);
|
|
554
|
+
ptr = e[1];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (!c) {
|
|
558
|
+
throw new TomlError("unfinished array encountered", {
|
|
559
|
+
toml: str,
|
|
560
|
+
ptr
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
return [res, ptr];
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ../../node_modules/smol-toml/dist/parse.js
|
|
567
|
+
function peekTable(key, table, meta, type) {
|
|
568
|
+
let t = table;
|
|
569
|
+
let m = meta;
|
|
570
|
+
let k;
|
|
571
|
+
let hasOwn = false;
|
|
572
|
+
let state;
|
|
573
|
+
for (let i = 0; i < key.length; i++) {
|
|
574
|
+
if (i) {
|
|
575
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
576
|
+
m = (state = m[k]).c;
|
|
577
|
+
if (type === 0 && (state.t === 1 || state.t === 2)) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
if (state.t === 2) {
|
|
581
|
+
let l = t.length - 1;
|
|
582
|
+
t = t[l];
|
|
583
|
+
m = m[l].c;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
k = key[i];
|
|
587
|
+
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
if (!hasOwn) {
|
|
591
|
+
if (k === "__proto__") {
|
|
592
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
593
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
594
|
+
}
|
|
595
|
+
m[k] = {
|
|
596
|
+
t: i < key.length - 1 && type === 2 ? 3 : type,
|
|
597
|
+
d: false,
|
|
598
|
+
i: 0,
|
|
599
|
+
c: {}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
state = m[k];
|
|
604
|
+
if (state.t !== type && !(type === 1 && state.t === 3)) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
if (type === 2) {
|
|
608
|
+
if (!state.d) {
|
|
609
|
+
state.d = true;
|
|
610
|
+
t[k] = [];
|
|
611
|
+
}
|
|
612
|
+
t[k].push(t = {});
|
|
613
|
+
state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
|
|
614
|
+
}
|
|
615
|
+
if (state.d) {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
state.d = true;
|
|
619
|
+
if (type === 1) {
|
|
620
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
621
|
+
} else if (type === 0 && hasOwn) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
return [k, t, state.c];
|
|
625
|
+
}
|
|
626
|
+
function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
627
|
+
let res = {};
|
|
628
|
+
let meta = {};
|
|
629
|
+
let tbl = res;
|
|
630
|
+
let m = meta;
|
|
631
|
+
for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) {
|
|
632
|
+
if (toml[ptr] === "[") {
|
|
633
|
+
let isTableArray = toml[++ptr] === "[";
|
|
634
|
+
let k = parseKey(toml, ptr += +isTableArray, "]");
|
|
635
|
+
if (isTableArray) {
|
|
636
|
+
if (toml[k[1] - 1] !== "]") {
|
|
637
|
+
throw new TomlError("expected end of table declaration", {
|
|
638
|
+
toml,
|
|
639
|
+
ptr: k[1] - 1
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
k[1]++;
|
|
643
|
+
}
|
|
644
|
+
let p = peekTable(
|
|
645
|
+
k[0],
|
|
646
|
+
res,
|
|
647
|
+
meta,
|
|
648
|
+
isTableArray ? 2 : 1
|
|
649
|
+
/* Type.EXPLICIT */
|
|
650
|
+
);
|
|
651
|
+
if (!p) {
|
|
652
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
653
|
+
toml,
|
|
654
|
+
ptr
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
m = p[2];
|
|
658
|
+
tbl = p[1];
|
|
659
|
+
ptr = k[1];
|
|
660
|
+
} else {
|
|
661
|
+
let k = parseKey(toml, ptr);
|
|
662
|
+
let p = peekTable(
|
|
663
|
+
k[0],
|
|
664
|
+
tbl,
|
|
665
|
+
m,
|
|
666
|
+
0
|
|
667
|
+
/* Type.DOTTED */
|
|
668
|
+
);
|
|
669
|
+
if (!p) {
|
|
670
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
671
|
+
toml,
|
|
672
|
+
ptr
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt);
|
|
676
|
+
p[1][p[0]] = v[0];
|
|
677
|
+
ptr = v[1];
|
|
678
|
+
}
|
|
679
|
+
ptr = skipVoid(toml, ptr, true);
|
|
680
|
+
if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") {
|
|
681
|
+
throw new TomlError("each key-value declaration must be followed by an end-of-line", {
|
|
682
|
+
toml,
|
|
683
|
+
ptr
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
ptr = skipVoid(toml, ptr);
|
|
687
|
+
}
|
|
688
|
+
return res;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ../../node_modules/smol-toml/dist/stringify.js
|
|
692
|
+
var BARE_KEY = /^[a-z0-9-_]+$/i;
|
|
693
|
+
function extendedTypeOf(obj) {
|
|
694
|
+
let type = typeof obj;
|
|
695
|
+
if (type === "object") {
|
|
696
|
+
if (Array.isArray(obj))
|
|
697
|
+
return "array";
|
|
698
|
+
if (obj instanceof Date)
|
|
699
|
+
return "date";
|
|
700
|
+
}
|
|
701
|
+
return type;
|
|
702
|
+
}
|
|
703
|
+
function isArrayOfTables(obj) {
|
|
704
|
+
for (let i = 0; i < obj.length; i++) {
|
|
705
|
+
if (extendedTypeOf(obj[i]) !== "object")
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
return obj.length != 0;
|
|
709
|
+
}
|
|
710
|
+
function formatString(s) {
|
|
711
|
+
return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
|
|
712
|
+
}
|
|
713
|
+
function stringifyValue(val, type, depth, numberAsFloat) {
|
|
714
|
+
if (depth === 0) {
|
|
715
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
716
|
+
}
|
|
717
|
+
if (type === "number") {
|
|
718
|
+
if (isNaN(val))
|
|
719
|
+
return "nan";
|
|
720
|
+
if (val === Infinity)
|
|
721
|
+
return "inf";
|
|
722
|
+
if (val === -Infinity)
|
|
723
|
+
return "-inf";
|
|
724
|
+
if (numberAsFloat && Number.isInteger(val))
|
|
725
|
+
return val.toFixed(1);
|
|
726
|
+
return val.toString();
|
|
727
|
+
}
|
|
728
|
+
if (type === "bigint" || type === "boolean") {
|
|
729
|
+
return val.toString();
|
|
730
|
+
}
|
|
731
|
+
if (type === "string") {
|
|
732
|
+
return formatString(val);
|
|
733
|
+
}
|
|
734
|
+
if (type === "date") {
|
|
735
|
+
if (isNaN(val.getTime())) {
|
|
736
|
+
throw new TypeError("cannot serialize invalid date");
|
|
737
|
+
}
|
|
738
|
+
return val.toISOString();
|
|
739
|
+
}
|
|
740
|
+
if (type === "object") {
|
|
741
|
+
return stringifyInlineTable(val, depth, numberAsFloat);
|
|
742
|
+
}
|
|
743
|
+
if (type === "array") {
|
|
744
|
+
return stringifyArray(val, depth, numberAsFloat);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function stringifyInlineTable(obj, depth, numberAsFloat) {
|
|
748
|
+
let keys = Object.keys(obj);
|
|
749
|
+
if (keys.length === 0)
|
|
750
|
+
return "{}";
|
|
751
|
+
let res = "{ ";
|
|
752
|
+
for (let i = 0; i < keys.length; i++) {
|
|
753
|
+
let k = keys[i];
|
|
754
|
+
if (i)
|
|
755
|
+
res += ", ";
|
|
756
|
+
res += BARE_KEY.test(k) ? k : formatString(k);
|
|
757
|
+
res += " = ";
|
|
758
|
+
res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
|
|
759
|
+
}
|
|
760
|
+
return res + " }";
|
|
761
|
+
}
|
|
762
|
+
function stringifyArray(array, depth, numberAsFloat) {
|
|
763
|
+
if (array.length === 0)
|
|
764
|
+
return "[]";
|
|
765
|
+
let res = "[ ";
|
|
766
|
+
for (let i = 0; i < array.length; i++) {
|
|
767
|
+
if (i)
|
|
768
|
+
res += ", ";
|
|
769
|
+
if (array[i] === null || array[i] === void 0) {
|
|
770
|
+
throw new TypeError("arrays cannot contain null or undefined values");
|
|
771
|
+
}
|
|
772
|
+
res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat);
|
|
773
|
+
}
|
|
774
|
+
return res + " ]";
|
|
775
|
+
}
|
|
776
|
+
function stringifyArrayTable(array, key, depth, numberAsFloat) {
|
|
777
|
+
if (depth === 0) {
|
|
778
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
779
|
+
}
|
|
780
|
+
let res = "";
|
|
781
|
+
for (let i = 0; i < array.length; i++) {
|
|
782
|
+
res += `${res && "\n"}[[${key}]]
|
|
783
|
+
`;
|
|
784
|
+
res += stringifyTable(0, array[i], key, depth, numberAsFloat);
|
|
785
|
+
}
|
|
786
|
+
return res;
|
|
787
|
+
}
|
|
788
|
+
function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
|
|
789
|
+
if (depth === 0) {
|
|
790
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
791
|
+
}
|
|
792
|
+
let preamble = "";
|
|
793
|
+
let tables = "";
|
|
794
|
+
let keys = Object.keys(obj);
|
|
795
|
+
for (let i = 0; i < keys.length; i++) {
|
|
796
|
+
let k = keys[i];
|
|
797
|
+
if (obj[k] !== null && obj[k] !== void 0) {
|
|
798
|
+
let type = extendedTypeOf(obj[k]);
|
|
799
|
+
if (type === "symbol" || type === "function") {
|
|
800
|
+
throw new TypeError(`cannot serialize values of type '${type}'`);
|
|
801
|
+
}
|
|
802
|
+
let key = BARE_KEY.test(k) ? k : formatString(k);
|
|
803
|
+
if (type === "array" && isArrayOfTables(obj[k])) {
|
|
804
|
+
tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
|
|
805
|
+
} else if (type === "object") {
|
|
806
|
+
let tblKey = prefix ? `${prefix}.${key}` : key;
|
|
807
|
+
tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
|
|
808
|
+
} else {
|
|
809
|
+
preamble += key;
|
|
810
|
+
preamble += " = ";
|
|
811
|
+
preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
|
|
812
|
+
preamble += "\n";
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (tableKey && (preamble || !tables))
|
|
817
|
+
preamble = preamble ? `[${tableKey}]
|
|
818
|
+
${preamble}` : `[${tableKey}]`;
|
|
819
|
+
return preamble && tables ? `${preamble}
|
|
820
|
+
${tables}` : preamble || tables;
|
|
821
|
+
}
|
|
822
|
+
function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
823
|
+
if (extendedTypeOf(obj) !== "object") {
|
|
824
|
+
throw new TypeError("stringify can only be called with an object");
|
|
825
|
+
}
|
|
826
|
+
let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
|
|
827
|
+
if (str[str.length - 1] !== "\n")
|
|
828
|
+
return str + "\n";
|
|
829
|
+
return str;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// ../core/dist/index.js
|
|
833
|
+
import * as fs from "fs";
|
|
834
|
+
import * as path from "path";
|
|
835
|
+
import * as os from "os";
|
|
836
|
+
import { execFileSync } from "child_process";
|
|
837
|
+
import crypto from "crypto";
|
|
838
|
+
function configFilePath() {
|
|
839
|
+
return join(homedir(), ".pionex", "config.toml");
|
|
840
|
+
}
|
|
841
|
+
function readFullConfig() {
|
|
842
|
+
const path2 = configFilePath();
|
|
843
|
+
if (!existsSync(path2)) return { profiles: {} };
|
|
844
|
+
const raw = readFileSync(path2, "utf-8");
|
|
845
|
+
return parse(raw);
|
|
846
|
+
}
|
|
847
|
+
function readTomlProfile(profileName) {
|
|
848
|
+
const config = readFullConfig();
|
|
849
|
+
const name = profileName ?? config.default_profile ?? "default";
|
|
850
|
+
return config.profiles?.[name] ?? {};
|
|
851
|
+
}
|
|
852
|
+
function writeFullConfig(config) {
|
|
853
|
+
const path2 = configFilePath();
|
|
854
|
+
const dir = dirname(path2);
|
|
855
|
+
if (!existsSync(dir)) {
|
|
856
|
+
mkdirSync(dir, { recursive: true });
|
|
857
|
+
}
|
|
858
|
+
writeFileSync(path2, stringify(config), "utf-8");
|
|
859
|
+
}
|
|
860
|
+
var CLIENT_NAMES = {
|
|
861
|
+
"claude-desktop": "Claude Desktop",
|
|
862
|
+
cursor: "Cursor",
|
|
863
|
+
windsurf: "Windsurf",
|
|
864
|
+
vscode: "VS Code",
|
|
865
|
+
"claude-code": "Claude Code CLI",
|
|
866
|
+
openclaw: "OpenClaw (mcporter)"
|
|
867
|
+
};
|
|
868
|
+
var SUPPORTED_CLIENTS = Object.keys(CLIENT_NAMES);
|
|
869
|
+
function appData() {
|
|
870
|
+
return process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
871
|
+
}
|
|
872
|
+
var CLAUDE_CONFIG_FILE = "claude_desktop_config.json";
|
|
873
|
+
function findMsStoreClaudePath() {
|
|
874
|
+
const localAppData = process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
|
|
875
|
+
const packagesDir = path.join(localAppData, "Packages");
|
|
876
|
+
try {
|
|
877
|
+
const entries = fs.readdirSync(packagesDir);
|
|
878
|
+
const claudePkg = entries.find((e) => e.startsWith("Claude_"));
|
|
879
|
+
if (claudePkg) {
|
|
880
|
+
const configPath = path.join(
|
|
881
|
+
packagesDir,
|
|
882
|
+
claudePkg,
|
|
883
|
+
"LocalCache",
|
|
884
|
+
"Roaming",
|
|
885
|
+
"Claude",
|
|
886
|
+
CLAUDE_CONFIG_FILE
|
|
887
|
+
);
|
|
888
|
+
if (fs.existsSync(configPath) || fs.existsSync(path.dirname(configPath))) {
|
|
889
|
+
return configPath;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
} catch {
|
|
893
|
+
}
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
function getConfigPath(client) {
|
|
897
|
+
const home = os.homedir();
|
|
898
|
+
const platform = process.platform;
|
|
899
|
+
switch (client) {
|
|
900
|
+
case "claude-desktop":
|
|
901
|
+
if (platform === "win32") {
|
|
902
|
+
return findMsStoreClaudePath() ?? path.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
|
|
903
|
+
}
|
|
904
|
+
if (platform === "darwin") {
|
|
905
|
+
return path.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
|
|
906
|
+
}
|
|
907
|
+
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
|
|
908
|
+
case "cursor":
|
|
909
|
+
return path.join(home, ".cursor", "mcp.json");
|
|
910
|
+
case "windsurf":
|
|
911
|
+
return path.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
912
|
+
case "vscode":
|
|
913
|
+
return path.join(process.cwd(), ".mcp.json");
|
|
914
|
+
case "claude-code":
|
|
915
|
+
return null;
|
|
916
|
+
case "openclaw":
|
|
917
|
+
return path.join(home, ".openclaw", "workspace", "config", "mcporter.json");
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
var NPX_PACKAGE = "@pionex/pionex-trade-mcp";
|
|
921
|
+
function buildEntry(client) {
|
|
922
|
+
if (client === "vscode") {
|
|
923
|
+
return { type: "stdio", command: "npx", args: ["-y", NPX_PACKAGE] };
|
|
924
|
+
}
|
|
925
|
+
return { command: "npx", args: ["-y", NPX_PACKAGE] };
|
|
926
|
+
}
|
|
927
|
+
function mergeJsonConfig(configPath, serverName, entry) {
|
|
928
|
+
const dir = path.dirname(configPath);
|
|
929
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
930
|
+
let data = {};
|
|
931
|
+
if (fs.existsSync(configPath)) {
|
|
932
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
933
|
+
try {
|
|
934
|
+
data = JSON.parse(raw);
|
|
935
|
+
} catch {
|
|
936
|
+
throw new Error(`Failed to parse existing config at ${configPath}`);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (typeof data.mcpServers !== "object" || data.mcpServers === null) {
|
|
940
|
+
data.mcpServers = {};
|
|
941
|
+
}
|
|
942
|
+
data.mcpServers[serverName] = entry;
|
|
943
|
+
fs.writeFileSync(configPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
944
|
+
}
|
|
945
|
+
function runSetup(options) {
|
|
946
|
+
const { client } = options;
|
|
947
|
+
const name = CLIENT_NAMES[client];
|
|
948
|
+
const serverName = "pionex-trade-mcp";
|
|
949
|
+
if (client === "claude-code") {
|
|
950
|
+
const claudeArgs = [
|
|
951
|
+
"mcp",
|
|
952
|
+
"add",
|
|
953
|
+
"--scope",
|
|
954
|
+
"user",
|
|
955
|
+
"--transport",
|
|
956
|
+
"stdio",
|
|
957
|
+
serverName,
|
|
958
|
+
"--",
|
|
959
|
+
"npx",
|
|
960
|
+
"-y",
|
|
961
|
+
NPX_PACKAGE
|
|
962
|
+
];
|
|
963
|
+
process.stdout.write(`Running: claude ${claudeArgs.join(" ")}
|
|
964
|
+
`);
|
|
965
|
+
execFileSync("claude", claudeArgs, { stdio: "inherit" });
|
|
966
|
+
process.stdout.write(`\u2713 Configured ${name}
|
|
967
|
+
`);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
const configPath = getConfigPath(client);
|
|
971
|
+
if (!configPath) {
|
|
972
|
+
throw new Error(`${name} is not supported on this platform`);
|
|
973
|
+
}
|
|
974
|
+
const entry = buildEntry(client);
|
|
975
|
+
mergeJsonConfig(configPath, serverName, entry);
|
|
976
|
+
process.stdout.write(
|
|
977
|
+
`\u2713 Configured ${name}
|
|
978
|
+
${configPath}
|
|
979
|
+
Restart ${name} to apply changes.
|
|
980
|
+
`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
var PIONEX_API_DEFAULT_BASE_URL = "https://api.pionex.com";
|
|
984
|
+
var MODULES = ["market", "account", "orders", "bot", "earn_dual"];
|
|
985
|
+
var DEFAULT_MODULES = ["market", "account", "orders", "bot", "earn_dual"];
|
|
986
|
+
var ConfigError = class extends Error {
|
|
987
|
+
suggestion;
|
|
988
|
+
constructor(message, suggestion) {
|
|
989
|
+
super(message);
|
|
990
|
+
this.name = "ConfigError";
|
|
991
|
+
this.suggestion = suggestion;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
var PionexApiError = class extends Error {
|
|
995
|
+
status;
|
|
996
|
+
endpoint;
|
|
997
|
+
responseText;
|
|
998
|
+
constructor(message, opts) {
|
|
999
|
+
super(message);
|
|
1000
|
+
this.name = "PionexApiError";
|
|
1001
|
+
this.status = opts?.status;
|
|
1002
|
+
this.endpoint = opts?.endpoint;
|
|
1003
|
+
this.responseText = opts?.responseText;
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
function toToolErrorPayload(error) {
|
|
1007
|
+
if (error instanceof ConfigError) {
|
|
1008
|
+
return {
|
|
1009
|
+
error: true,
|
|
1010
|
+
type: "ConfigError",
|
|
1011
|
+
message: error.message,
|
|
1012
|
+
suggestion: error.suggestion
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
if (error instanceof PionexApiError) {
|
|
1016
|
+
return {
|
|
1017
|
+
error: true,
|
|
1018
|
+
type: "PionexApiError",
|
|
1019
|
+
message: error.message,
|
|
1020
|
+
status: error.status,
|
|
1021
|
+
endpoint: error.endpoint,
|
|
1022
|
+
responseText: error.responseText
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1026
|
+
return { error: true, type: "Error", message };
|
|
1027
|
+
}
|
|
1028
|
+
function parseModuleList(rawModules) {
|
|
1029
|
+
if (!rawModules || rawModules.trim().length === 0) return [...DEFAULT_MODULES];
|
|
1030
|
+
const trimmed = rawModules.trim().toLowerCase();
|
|
1031
|
+
if (trimmed === "all") return [...MODULES];
|
|
1032
|
+
const requested = trimmed.split(",").map((x) => x.trim()).filter(Boolean);
|
|
1033
|
+
if (requested.length === 0) return [...DEFAULT_MODULES];
|
|
1034
|
+
const out = [];
|
|
1035
|
+
for (const m of requested) {
|
|
1036
|
+
if (!MODULES.includes(m)) {
|
|
1037
|
+
throw new ConfigError(`Unknown module "${m}".`, `Use one of: ${MODULES.join(", ")} or "all".`);
|
|
1038
|
+
}
|
|
1039
|
+
out.push(m);
|
|
1040
|
+
}
|
|
1041
|
+
return Array.from(new Set(out));
|
|
1042
|
+
}
|
|
1043
|
+
function loadConfig(cli) {
|
|
1044
|
+
const toml = readTomlProfile(cli.profile);
|
|
1045
|
+
const apiKey = process.env.PIONEX_API_KEY?.trim() || toml.api_key;
|
|
1046
|
+
const apiSecret = process.env.PIONEX_API_SECRET?.trim() || toml.secret_key;
|
|
1047
|
+
const hasAuth = Boolean(apiKey && apiSecret);
|
|
1048
|
+
const partialAuth = Boolean(apiKey) || Boolean(apiSecret);
|
|
1049
|
+
if (partialAuth && !hasAuth) {
|
|
1050
|
+
throw new ConfigError(
|
|
1051
|
+
"Partial Pionex API credentials detected.",
|
|
1052
|
+
"Set both PIONEX_API_KEY and PIONEX_API_SECRET (env vars or config.toml profile)."
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
const baseUrl = (cli.baseUrl?.trim() || process.env.PIONEX_BASE_URL?.trim() || toml.base_url || PIONEX_API_DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
1056
|
+
if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) {
|
|
1057
|
+
throw new ConfigError(`Invalid base URL "${baseUrl}".`, "PIONEX_BASE_URL must start with http:// or https://");
|
|
1058
|
+
}
|
|
1059
|
+
return {
|
|
1060
|
+
apiKey,
|
|
1061
|
+
apiSecret,
|
|
1062
|
+
hasAuth,
|
|
1063
|
+
baseUrl,
|
|
1064
|
+
modules: parseModuleList(cli.modules),
|
|
1065
|
+
readOnly: cli.readOnly
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
function requireAuth(config) {
|
|
1069
|
+
if (!config.apiKey || !config.apiSecret) {
|
|
1070
|
+
throw new ConfigError(
|
|
1071
|
+
"This operation requires authentication, but no Pionex API credentials were found.",
|
|
1072
|
+
"Run 'pionex-ai-kit onboard' to create ~/.pionex/config.toml, or set PIONEX_API_KEY and PIONEX_API_SECRET."
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
return { apiKey: config.apiKey, apiSecret: config.apiSecret };
|
|
1076
|
+
}
|
|
1077
|
+
function buildQueryString(query) {
|
|
1078
|
+
if (!query) return "";
|
|
1079
|
+
const entries = Object.entries(query).filter(([, v]) => v !== void 0 && v !== null);
|
|
1080
|
+
if (entries.length === 0) return "";
|
|
1081
|
+
const params = new URLSearchParams();
|
|
1082
|
+
for (const [k, v] of entries) params.set(k, String(v));
|
|
1083
|
+
return params.toString();
|
|
1084
|
+
}
|
|
1085
|
+
function buildSignedRequest(config, method, path2, query, bodyJson) {
|
|
1086
|
+
const { apiKey, apiSecret } = requireAuth(config);
|
|
1087
|
+
const timestamp = Date.now().toString();
|
|
1088
|
+
const params = { ...query, timestamp };
|
|
1089
|
+
const sortedKeys = Object.keys(params).sort();
|
|
1090
|
+
const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join("&");
|
|
1091
|
+
const pathUrl = `${path2}?${queryString}`;
|
|
1092
|
+
let payload = `${method}${pathUrl}`;
|
|
1093
|
+
if (bodyJson != null) payload += bodyJson;
|
|
1094
|
+
const signature = crypto.createHmac("sha256", apiSecret).update(payload).digest("hex");
|
|
1095
|
+
const url = `${config.baseUrl}${pathUrl}`;
|
|
1096
|
+
const headers = {
|
|
1097
|
+
"PIONEX-KEY": apiKey,
|
|
1098
|
+
"PIONEX-SIGNATURE": signature,
|
|
1099
|
+
"Content-Type": "application/json"
|
|
1100
|
+
};
|
|
1101
|
+
return { url, headers, bodyJson };
|
|
1102
|
+
}
|
|
1103
|
+
async function readTextSafe(res) {
|
|
1104
|
+
try {
|
|
1105
|
+
return await res.text();
|
|
1106
|
+
} catch {
|
|
1107
|
+
return "";
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
var PionexRestClient = class {
|
|
1111
|
+
config;
|
|
1112
|
+
constructor(config) {
|
|
1113
|
+
this.config = config;
|
|
1114
|
+
}
|
|
1115
|
+
async publicGet(path2, query = {}) {
|
|
1116
|
+
const qs = buildQueryString(query);
|
|
1117
|
+
const endpoint = qs ? `${path2}?${qs}` : path2;
|
|
1118
|
+
const url = `${this.config.baseUrl}${endpoint}`;
|
|
1119
|
+
const res = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json" } });
|
|
1120
|
+
if (!res.ok) {
|
|
1121
|
+
const txt = await readTextSafe(res);
|
|
1122
|
+
throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint, responseText: txt });
|
|
1123
|
+
}
|
|
1124
|
+
const data = await res.json();
|
|
1125
|
+
return { endpoint, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1126
|
+
}
|
|
1127
|
+
async signedGet(path2, query = {}) {
|
|
1128
|
+
const { url, headers } = buildSignedRequest(this.config, "GET", path2, query, null);
|
|
1129
|
+
const endpoint = `${path2}?${buildQueryString({ ...query, timestamp: "..." })}`;
|
|
1130
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
1131
|
+
if (!res.ok) {
|
|
1132
|
+
const txt = await readTextSafe(res);
|
|
1133
|
+
throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint: path2, responseText: txt });
|
|
1134
|
+
}
|
|
1135
|
+
const data = await res.json();
|
|
1136
|
+
return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1137
|
+
}
|
|
1138
|
+
async signedPost(path2, body) {
|
|
1139
|
+
const bodyJson = JSON.stringify(body);
|
|
1140
|
+
const { url, headers, bodyJson: bj } = buildSignedRequest(this.config, "POST", path2, {}, bodyJson);
|
|
1141
|
+
const res = await fetch(url, { method: "POST", headers, body: bj ?? void 0 });
|
|
1142
|
+
if (!res.ok) {
|
|
1143
|
+
const txt = await readTextSafe(res);
|
|
1144
|
+
throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint: path2, responseText: txt });
|
|
1145
|
+
}
|
|
1146
|
+
const data = await res.json();
|
|
1147
|
+
return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1148
|
+
}
|
|
1149
|
+
async signedDelete(path2, body) {
|
|
1150
|
+
const bodyJson = JSON.stringify(body);
|
|
1151
|
+
const { url, headers, bodyJson: bj } = buildSignedRequest(this.config, "DELETE", path2, {}, bodyJson);
|
|
1152
|
+
const res = await fetch(url, { method: "DELETE", headers, body: bj ?? void 0 });
|
|
1153
|
+
if (!res.ok) {
|
|
1154
|
+
const txt = await readTextSafe(res);
|
|
1155
|
+
throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint: path2, responseText: txt });
|
|
1156
|
+
}
|
|
1157
|
+
const data = await res.json();
|
|
1158
|
+
return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1159
|
+
}
|
|
1160
|
+
async signedDeleteQuery(path2, query = {}) {
|
|
1161
|
+
const { url, headers } = buildSignedRequest(this.config, "DELETE", path2, query, null);
|
|
1162
|
+
const res = await fetch(url, { method: "DELETE", headers });
|
|
1163
|
+
if (!res.ok) {
|
|
1164
|
+
const txt = await readTextSafe(res);
|
|
1165
|
+
throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint: path2, responseText: txt });
|
|
1166
|
+
}
|
|
1167
|
+
const data = await res.json();
|
|
1168
|
+
return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
function registerMarketTools() {
|
|
1172
|
+
return [
|
|
1173
|
+
{
|
|
1174
|
+
name: "pionex_market_get_depth",
|
|
1175
|
+
module: "market",
|
|
1176
|
+
isWrite: false,
|
|
1177
|
+
description: "Get order book depth (bids and asks) for a symbol. Use for spread, liquidity, or best bid/ask.",
|
|
1178
|
+
inputSchema: {
|
|
1179
|
+
type: "object",
|
|
1180
|
+
additionalProperties: false,
|
|
1181
|
+
properties: {
|
|
1182
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1183
|
+
limit: { type: "integer", description: "Price levels (1-100), default 5" }
|
|
1184
|
+
},
|
|
1185
|
+
required: ["symbol"]
|
|
1186
|
+
},
|
|
1187
|
+
async handler(args, { client }) {
|
|
1188
|
+
const symbol = String(args.symbol);
|
|
1189
|
+
const limit = args.limit == null ? void 0 : Number(args.limit);
|
|
1190
|
+
const q = { symbol };
|
|
1191
|
+
if (limit != null && Number.isFinite(limit)) q.limit = limit;
|
|
1192
|
+
return (await client.publicGet("/api/v1/market/depth", q)).data;
|
|
1193
|
+
}
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
name: "pionex_market_get_trades",
|
|
1197
|
+
module: "market",
|
|
1198
|
+
isWrite: false,
|
|
1199
|
+
description: "Get recent trades for a symbol. Use for latest price and volume.",
|
|
1200
|
+
inputSchema: {
|
|
1201
|
+
type: "object",
|
|
1202
|
+
additionalProperties: false,
|
|
1203
|
+
properties: {
|
|
1204
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1205
|
+
limit: { type: "integer", description: "Default 5 (1-100)" }
|
|
1206
|
+
},
|
|
1207
|
+
required: ["symbol"]
|
|
1208
|
+
},
|
|
1209
|
+
async handler(args, { client }) {
|
|
1210
|
+
const symbol = String(args.symbol);
|
|
1211
|
+
const limit = args.limit == null ? void 0 : Number(args.limit);
|
|
1212
|
+
const q = { symbol };
|
|
1213
|
+
if (limit != null && Number.isFinite(limit)) q.limit = limit;
|
|
1214
|
+
return (await client.publicGet("/api/v1/market/trades", q)).data;
|
|
1215
|
+
}
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
name: "pionex_market_get_symbol_info",
|
|
1219
|
+
module: "market",
|
|
1220
|
+
isWrite: false,
|
|
1221
|
+
description: "Get symbol metadata (precision, min size, price limits). Call before placing orders to avoid amount/size filter errors.",
|
|
1222
|
+
inputSchema: {
|
|
1223
|
+
type: "object",
|
|
1224
|
+
additionalProperties: false,
|
|
1225
|
+
properties: {
|
|
1226
|
+
symbols: {
|
|
1227
|
+
type: "string",
|
|
1228
|
+
description: 'Optional. One or more symbols, comma-separated, e.g. "BTC_USDT" or "BTC_USDT,ADA_USDT".'
|
|
1229
|
+
},
|
|
1230
|
+
type: {
|
|
1231
|
+
type: "string",
|
|
1232
|
+
enum: ["SPOT", "PERP"],
|
|
1233
|
+
description: "Optional. If no symbols are specified, filter by type (default SPOT)."
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
async handler(args, { client }) {
|
|
1238
|
+
const q = {};
|
|
1239
|
+
if (args.symbols) q.symbols = String(args.symbols);
|
|
1240
|
+
if (!args.symbols && args.type) q.type = String(args.type);
|
|
1241
|
+
return (await client.publicGet("/api/v1/common/symbols", q)).data;
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
name: "pionex_market_get_tickers",
|
|
1246
|
+
module: "market",
|
|
1247
|
+
isWrite: false,
|
|
1248
|
+
description: "Get 24-hour ticker(s): open, close, high, low, volume, amount, count. Optional symbol or type (SPOT/PERP).",
|
|
1249
|
+
inputSchema: {
|
|
1250
|
+
type: "object",
|
|
1251
|
+
additionalProperties: false,
|
|
1252
|
+
properties: {
|
|
1253
|
+
symbol: { type: "string", description: "e.g. BTC_USDT; if omitted, returns all tickers filtered by type" },
|
|
1254
|
+
type: { type: "string", enum: ["SPOT", "PERP"], description: "If symbol is not specified, filter by type." }
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
async handler(args, { client }) {
|
|
1258
|
+
const q = {};
|
|
1259
|
+
if (args.symbol) q.symbol = String(args.symbol);
|
|
1260
|
+
if (args.type) q.type = String(args.type);
|
|
1261
|
+
return (await client.publicGet("/api/v1/market/tickers", q)).data;
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
name: "pionex_market_get_book_tickers",
|
|
1266
|
+
module: "market",
|
|
1267
|
+
isWrite: false,
|
|
1268
|
+
description: "Get best bid/ask ticker(s). Optional symbol or type (SPOT/PERP).",
|
|
1269
|
+
inputSchema: {
|
|
1270
|
+
type: "object",
|
|
1271
|
+
additionalProperties: false,
|
|
1272
|
+
properties: {
|
|
1273
|
+
symbol: { type: "string", description: "e.g. BTC_USDT; if omitted, returns all book tickers filtered by type" },
|
|
1274
|
+
type: { type: "string", enum: ["SPOT", "PERP"], description: "If symbol is not specified, filter by type." }
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
async handler(args, { client }) {
|
|
1278
|
+
const q = {};
|
|
1279
|
+
if (args.symbol) q.symbol = String(args.symbol);
|
|
1280
|
+
if (args.type) q.type = String(args.type);
|
|
1281
|
+
return (await client.publicGet("/api/v1/market/bookTickers", q)).data;
|
|
1282
|
+
}
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
name: "pionex_market_get_klines",
|
|
1286
|
+
module: "market",
|
|
1287
|
+
isWrite: false,
|
|
1288
|
+
description: "Get OHLCV klines (candlestick) for a symbol. Use for charts or historical price/volume.",
|
|
1289
|
+
inputSchema: {
|
|
1290
|
+
type: "object",
|
|
1291
|
+
additionalProperties: false,
|
|
1292
|
+
properties: {
|
|
1293
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1294
|
+
interval: { type: "string", enum: ["1M", "5M", "15M", "30M", "60M", "4H", "8H", "12H", "1D"], description: "Kline interval." },
|
|
1295
|
+
endTime: { type: "integer", description: "End time in milliseconds." },
|
|
1296
|
+
limit: { type: "integer", description: "Default 100 (1-500)." }
|
|
1297
|
+
},
|
|
1298
|
+
required: ["symbol", "interval"]
|
|
1299
|
+
},
|
|
1300
|
+
async handler(args, { client }) {
|
|
1301
|
+
const symbol = String(args.symbol);
|
|
1302
|
+
const interval = String(args.interval);
|
|
1303
|
+
const q = { symbol, interval };
|
|
1304
|
+
if (args.endTime != null) q.endTime = Number(args.endTime);
|
|
1305
|
+
if (args.limit != null) q.limit = Number(args.limit);
|
|
1306
|
+
return (await client.publicGet("/api/v1/market/klines", q)).data;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
];
|
|
1310
|
+
}
|
|
1311
|
+
function registerAccountTools() {
|
|
1312
|
+
return [
|
|
1313
|
+
{
|
|
1314
|
+
name: "pionex_account_get_balance",
|
|
1315
|
+
module: "account",
|
|
1316
|
+
isWrite: false,
|
|
1317
|
+
description: "Query spot account balances for all currencies. Requires API key and secret in ~/.pionex/config.toml or env.",
|
|
1318
|
+
inputSchema: { type: "object", additionalProperties: false, properties: {} },
|
|
1319
|
+
async handler(_args, { client }) {
|
|
1320
|
+
return (await client.signedGet("/api/v1/account/balances")).data;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
];
|
|
1324
|
+
}
|
|
1325
|
+
function registerOrdersTools() {
|
|
1326
|
+
return [
|
|
1327
|
+
{
|
|
1328
|
+
name: "pionex_orders_new_order",
|
|
1329
|
+
module: "orders",
|
|
1330
|
+
isWrite: true,
|
|
1331
|
+
description: "Create a spot order on Pionex. LIMIT requires symbol/side/type=LIMIT/price/size. MARKET BUY requires amount (quote). MARKET SELL requires size (base).",
|
|
1332
|
+
inputSchema: {
|
|
1333
|
+
type: "object",
|
|
1334
|
+
additionalProperties: false,
|
|
1335
|
+
properties: {
|
|
1336
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1337
|
+
side: { type: "string", enum: ["BUY", "SELL"] },
|
|
1338
|
+
type: { type: "string", enum: ["LIMIT", "MARKET"] },
|
|
1339
|
+
clientOrderId: { type: "string", description: "Optional client order id (max 64 chars)" },
|
|
1340
|
+
size: { type: "string", description: "Quantity; required for limit and market sell" },
|
|
1341
|
+
price: { type: "string", description: "Required for limit order" },
|
|
1342
|
+
amount: { type: "string", description: "Quote amount; required for market buy" },
|
|
1343
|
+
IOC: { type: "boolean", description: "Immediate-or-cancel, default false" }
|
|
1344
|
+
},
|
|
1345
|
+
required: ["symbol", "side", "type"]
|
|
1346
|
+
},
|
|
1347
|
+
async handler(args, { client, config }) {
|
|
1348
|
+
if (config.readOnly) {
|
|
1349
|
+
throw new Error("Server is running in --read-only mode; order placement is disabled.");
|
|
1350
|
+
}
|
|
1351
|
+
const body = {};
|
|
1352
|
+
if (args.symbol != null) body.symbol = String(args.symbol);
|
|
1353
|
+
if (args.side != null) body.side = String(args.side);
|
|
1354
|
+
if (args.type != null) body.type = String(args.type);
|
|
1355
|
+
if (args.clientOrderId != null) body.clientOrderId = String(args.clientOrderId);
|
|
1356
|
+
if (args.size != null) body.size = String(args.size);
|
|
1357
|
+
if (args.price != null) body.price = String(args.price);
|
|
1358
|
+
if (args.amount != null) body.amount = String(args.amount);
|
|
1359
|
+
if (args.IOC != null) body.IOC = Boolean(args.IOC);
|
|
1360
|
+
return (await client.signedPost("/api/v1/trade/order", body)).data;
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
name: "pionex_orders_get_order",
|
|
1365
|
+
module: "orders",
|
|
1366
|
+
isWrite: false,
|
|
1367
|
+
description: "Get a single order by order ID.",
|
|
1368
|
+
inputSchema: {
|
|
1369
|
+
type: "object",
|
|
1370
|
+
additionalProperties: false,
|
|
1371
|
+
properties: {
|
|
1372
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1373
|
+
orderId: { type: "integer", description: "Order id" }
|
|
1374
|
+
},
|
|
1375
|
+
required: ["symbol", "orderId"]
|
|
1376
|
+
},
|
|
1377
|
+
async handler(args, { client }) {
|
|
1378
|
+
const symbol = String(args.symbol);
|
|
1379
|
+
const orderId = Number(args.orderId);
|
|
1380
|
+
return (await client.signedGet("/api/v1/trade/order", { symbol, orderId })).data;
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
name: "pionex_orders_get_order_by_client_order_id",
|
|
1385
|
+
module: "orders",
|
|
1386
|
+
isWrite: false,
|
|
1387
|
+
description: "Get a single order by client order ID.",
|
|
1388
|
+
inputSchema: {
|
|
1389
|
+
type: "object",
|
|
1390
|
+
additionalProperties: false,
|
|
1391
|
+
properties: {
|
|
1392
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1393
|
+
clientOrderId: { type: "string", description: "Client order id" }
|
|
1394
|
+
},
|
|
1395
|
+
required: ["symbol", "clientOrderId"]
|
|
1396
|
+
},
|
|
1397
|
+
async handler(args, { client }) {
|
|
1398
|
+
const symbol = String(args.symbol);
|
|
1399
|
+
const clientOrderId = String(args.clientOrderId);
|
|
1400
|
+
return (await client.signedGet("/api/v1/trade/orderByClientOrderId", { symbol, clientOrderId })).data;
|
|
1401
|
+
}
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
name: "pionex_orders_get_open_orders",
|
|
1405
|
+
module: "orders",
|
|
1406
|
+
isWrite: false,
|
|
1407
|
+
description: "List open (unfilled) orders for a symbol.",
|
|
1408
|
+
inputSchema: {
|
|
1409
|
+
type: "object",
|
|
1410
|
+
additionalProperties: false,
|
|
1411
|
+
properties: { symbol: { type: "string", description: "e.g. BTC_USDT" } },
|
|
1412
|
+
required: ["symbol"]
|
|
1413
|
+
},
|
|
1414
|
+
async handler(args, { client }) {
|
|
1415
|
+
const symbol = String(args.symbol);
|
|
1416
|
+
return (await client.signedGet("/api/v1/trade/openOrders", { symbol })).data;
|
|
1417
|
+
}
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
name: "pionex_orders_get_all_orders",
|
|
1421
|
+
module: "orders",
|
|
1422
|
+
isWrite: false,
|
|
1423
|
+
description: "List order history for a symbol (filled and cancelled), with optional limit.",
|
|
1424
|
+
inputSchema: {
|
|
1425
|
+
type: "object",
|
|
1426
|
+
additionalProperties: false,
|
|
1427
|
+
properties: {
|
|
1428
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1429
|
+
limit: { type: "integer", description: "Default 1 (1-100)" }
|
|
1430
|
+
},
|
|
1431
|
+
required: ["symbol"]
|
|
1432
|
+
},
|
|
1433
|
+
async handler(args, { client }) {
|
|
1434
|
+
const symbol = String(args.symbol);
|
|
1435
|
+
const q = { symbol };
|
|
1436
|
+
if (args.limit != null) q.limit = Number(args.limit);
|
|
1437
|
+
return (await client.signedGet("/api/v1/trade/allOrders", q)).data;
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
name: "pionex_orders_cancel_order",
|
|
1442
|
+
module: "orders",
|
|
1443
|
+
isWrite: true,
|
|
1444
|
+
description: "Cancel an open order by order ID.",
|
|
1445
|
+
inputSchema: {
|
|
1446
|
+
type: "object",
|
|
1447
|
+
additionalProperties: false,
|
|
1448
|
+
properties: {
|
|
1449
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1450
|
+
orderId: { type: "integer", description: "Order id" }
|
|
1451
|
+
},
|
|
1452
|
+
required: ["symbol", "orderId"]
|
|
1453
|
+
},
|
|
1454
|
+
async handler(args, { client, config }) {
|
|
1455
|
+
if (config.readOnly) {
|
|
1456
|
+
throw new Error("Server is running in --read-only mode; order cancellation is disabled.");
|
|
1457
|
+
}
|
|
1458
|
+
const symbol = String(args.symbol);
|
|
1459
|
+
const orderId = Number(args.orderId);
|
|
1460
|
+
return (await client.signedDelete("/api/v1/trade/order", { symbol, orderId })).data;
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "pionex_orders_get_fills",
|
|
1465
|
+
module: "orders",
|
|
1466
|
+
isWrite: false,
|
|
1467
|
+
description: "Get filled trades (fills) for a symbol in a time range. Requires API key. Returns up to 100 latest fills.",
|
|
1468
|
+
inputSchema: {
|
|
1469
|
+
type: "object",
|
|
1470
|
+
additionalProperties: false,
|
|
1471
|
+
properties: {
|
|
1472
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1473
|
+
startTime: { type: "integer", description: "Start time in milliseconds." },
|
|
1474
|
+
endTime: { type: "integer", description: "End time in milliseconds." }
|
|
1475
|
+
},
|
|
1476
|
+
required: ["symbol"]
|
|
1477
|
+
},
|
|
1478
|
+
async handler(args, { client }) {
|
|
1479
|
+
const symbol = String(args.symbol);
|
|
1480
|
+
const q = { symbol };
|
|
1481
|
+
if (args.startTime != null) q.startTime = Number(args.startTime);
|
|
1482
|
+
if (args.endTime != null) q.endTime = Number(args.endTime);
|
|
1483
|
+
return (await client.signedGet("/api/v1/trade/fills", q)).data;
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "pionex_orders_get_fills_by_order_id",
|
|
1488
|
+
module: "orders",
|
|
1489
|
+
isWrite: false,
|
|
1490
|
+
description: "Get fills for a specific order by symbol and orderId.",
|
|
1491
|
+
inputSchema: {
|
|
1492
|
+
type: "object",
|
|
1493
|
+
additionalProperties: false,
|
|
1494
|
+
properties: {
|
|
1495
|
+
symbol: { type: "string", description: "e.g. BTC_USDT" },
|
|
1496
|
+
orderId: { type: "integer", description: "Order id" }
|
|
1497
|
+
},
|
|
1498
|
+
required: ["symbol", "orderId"]
|
|
1499
|
+
},
|
|
1500
|
+
async handler(args, { client }) {
|
|
1501
|
+
const symbol = String(args.symbol);
|
|
1502
|
+
const orderId = Number(args.orderId);
|
|
1503
|
+
return (await client.signedGet("/api/v1/trade/fillsByOrderId", { symbol, orderId })).data;
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
name: "pionex_orders_cancel_all_orders",
|
|
1508
|
+
module: "orders",
|
|
1509
|
+
isWrite: true,
|
|
1510
|
+
description: "Cancel all open orders for a symbol.",
|
|
1511
|
+
inputSchema: {
|
|
1512
|
+
type: "object",
|
|
1513
|
+
additionalProperties: false,
|
|
1514
|
+
properties: { symbol: { type: "string", description: "e.g. BTC_USDT" } },
|
|
1515
|
+
required: ["symbol"]
|
|
1516
|
+
},
|
|
1517
|
+
async handler(args, { client, config }) {
|
|
1518
|
+
if (config.readOnly) {
|
|
1519
|
+
throw new Error("Server is running in --read-only mode; cancel_all is disabled.");
|
|
1520
|
+
}
|
|
1521
|
+
const symbol = String(args.symbol);
|
|
1522
|
+
return (await client.signedDelete("/api/v1/trade/allOrders", { symbol })).data;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
];
|
|
1526
|
+
}
|
|
1527
|
+
var CREATE_FUTURES_GRID_ORDER_DATA_KEYS = [
|
|
1528
|
+
"top",
|
|
1529
|
+
"bottom",
|
|
1530
|
+
"row",
|
|
1531
|
+
"grid_type",
|
|
1532
|
+
"trend",
|
|
1533
|
+
"leverage",
|
|
1534
|
+
"extraMargin",
|
|
1535
|
+
"quoteInvestment",
|
|
1536
|
+
"condition",
|
|
1537
|
+
"conditionDirection",
|
|
1538
|
+
"lossStopType",
|
|
1539
|
+
"lossStop",
|
|
1540
|
+
"lossStopDelay",
|
|
1541
|
+
"profitStopType",
|
|
1542
|
+
"profitStop",
|
|
1543
|
+
"profitStopDelay",
|
|
1544
|
+
"lossStopHigh",
|
|
1545
|
+
"shareRatio",
|
|
1546
|
+
"investCoin",
|
|
1547
|
+
"investmentFrom",
|
|
1548
|
+
"uiInvestCoin",
|
|
1549
|
+
"lossStopLimitPrice",
|
|
1550
|
+
"lossStopLimitHighPrice",
|
|
1551
|
+
"profitStopLimitPrice",
|
|
1552
|
+
"slippage",
|
|
1553
|
+
"bonusId",
|
|
1554
|
+
"uiExtraData",
|
|
1555
|
+
"movingIndicatorType",
|
|
1556
|
+
"movingIndicatorInterval",
|
|
1557
|
+
"movingIndicatorParam",
|
|
1558
|
+
"movingTrailingUpParam",
|
|
1559
|
+
"cateType",
|
|
1560
|
+
"movingTop",
|
|
1561
|
+
"movingBottom",
|
|
1562
|
+
"enableFollowClosed"
|
|
1563
|
+
];
|
|
1564
|
+
var ORDER_DATA_KEY_SET = new Set(CREATE_FUTURES_GRID_ORDER_DATA_KEYS);
|
|
1565
|
+
function asNonEmptyString(value, field) {
|
|
1566
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1567
|
+
throw new Error(`Invalid "${field}": expected non-empty string.`);
|
|
1568
|
+
}
|
|
1569
|
+
return value.trim();
|
|
1570
|
+
}
|
|
1571
|
+
function asFiniteNumber(value, field) {
|
|
1572
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1573
|
+
throw new Error(`Invalid "${field}": expected finite number.`);
|
|
1574
|
+
}
|
|
1575
|
+
return value;
|
|
1576
|
+
}
|
|
1577
|
+
function asPositiveNumber(value, field) {
|
|
1578
|
+
const n = asFiniteNumber(value, field);
|
|
1579
|
+
if (n <= 0) throw new Error(`Invalid "${field}": expected number > 0.`);
|
|
1580
|
+
return n;
|
|
1581
|
+
}
|
|
1582
|
+
function asPositiveInteger(value, field) {
|
|
1583
|
+
const n = asPositiveNumber(value, field);
|
|
1584
|
+
if (!Number.isInteger(n)) {
|
|
1585
|
+
throw new Error(`Invalid "${field}": expected positive integer.`);
|
|
1586
|
+
}
|
|
1587
|
+
return n;
|
|
1588
|
+
}
|
|
1589
|
+
function asBoolean(value, field) {
|
|
1590
|
+
if (typeof value !== "boolean") {
|
|
1591
|
+
throw new Error(`Invalid "${field}": expected boolean.`);
|
|
1592
|
+
}
|
|
1593
|
+
return value;
|
|
1594
|
+
}
|
|
1595
|
+
function assertEnum(value, field, allowed) {
|
|
1596
|
+
if (!allowed.includes(value)) {
|
|
1597
|
+
throw new Error(`Invalid "${field}": expected one of ${allowed.join(", ")}.`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
function asPositiveDecimalString(value, field) {
|
|
1601
|
+
const s = asNonEmptyString(value, field);
|
|
1602
|
+
if (!/^\d+(\.\d+)?$/.test(s)) {
|
|
1603
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
1604
|
+
}
|
|
1605
|
+
const n = Number(s);
|
|
1606
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
1607
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
1608
|
+
}
|
|
1609
|
+
return s;
|
|
1610
|
+
}
|
|
1611
|
+
function asPositiveDecimalStringLoose(value, field) {
|
|
1612
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
1613
|
+
return String(value);
|
|
1614
|
+
}
|
|
1615
|
+
return asPositiveDecimalString(value, field);
|
|
1616
|
+
}
|
|
1617
|
+
function asNonNegativeDecimalString(value, field) {
|
|
1618
|
+
const s = asNonEmptyString(value, field);
|
|
1619
|
+
if (!/^\d+(\.\d+)?$/.test(s)) {
|
|
1620
|
+
throw new Error(`Invalid "${field}": expected non-negative decimal string.`);
|
|
1621
|
+
}
|
|
1622
|
+
const n = Number(s);
|
|
1623
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
1624
|
+
throw new Error(`Invalid "${field}": expected non-negative decimal string.`);
|
|
1625
|
+
}
|
|
1626
|
+
return s;
|
|
1627
|
+
}
|
|
1628
|
+
function asOptionalString(value, field) {
|
|
1629
|
+
if (typeof value !== "string") {
|
|
1630
|
+
throw new Error(`Invalid "${field}": expected string.`);
|
|
1631
|
+
}
|
|
1632
|
+
return value;
|
|
1633
|
+
}
|
|
1634
|
+
function asOptionalNonNegativeNumber(value, field) {
|
|
1635
|
+
const n = asFiniteNumber(value, field);
|
|
1636
|
+
if (n < 0) throw new Error(`Invalid "${field}": expected number >= 0.`);
|
|
1637
|
+
return n;
|
|
1638
|
+
}
|
|
1639
|
+
function parseAndValidateCreateFuturesGridBuOrderData(raw) {
|
|
1640
|
+
const data = { ...raw };
|
|
1641
|
+
for (const k of Object.keys(data)) {
|
|
1642
|
+
if (!ORDER_DATA_KEY_SET.has(k)) {
|
|
1643
|
+
throw new Error(`Unknown buOrderData property "${k}". Allowed keys: ${CREATE_FUTURES_GRID_ORDER_DATA_KEYS.join(", ")}.`);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
const top = asPositiveDecimalStringLoose(data.top, "buOrderData.top");
|
|
1647
|
+
const bottom = asPositiveDecimalStringLoose(data.bottom, "buOrderData.bottom");
|
|
1648
|
+
if (Number(top) <= Number(bottom)) {
|
|
1649
|
+
throw new Error('Invalid "buOrderData.top": expected top > bottom.');
|
|
1650
|
+
}
|
|
1651
|
+
const row = asPositiveInteger(data.row, "buOrderData.row");
|
|
1652
|
+
const gridType = asNonEmptyString(data.grid_type, "buOrderData.grid_type");
|
|
1653
|
+
assertEnum(gridType, "buOrderData.grid_type", ["arithmetic", "geometric"]);
|
|
1654
|
+
const trend = asNonEmptyString(data.trend, "buOrderData.trend");
|
|
1655
|
+
assertEnum(trend, "buOrderData.trend", ["long", "short", "no_trend"]);
|
|
1656
|
+
const leverage = asPositiveNumber(data.leverage, "buOrderData.leverage");
|
|
1657
|
+
const quoteInvestment = asPositiveDecimalStringLoose(data.quoteInvestment, "buOrderData.quoteInvestment");
|
|
1658
|
+
const out = {
|
|
1659
|
+
top,
|
|
1660
|
+
bottom,
|
|
1661
|
+
row,
|
|
1662
|
+
grid_type: gridType,
|
|
1663
|
+
trend,
|
|
1664
|
+
leverage,
|
|
1665
|
+
quoteInvestment
|
|
1666
|
+
};
|
|
1667
|
+
if (data.extraMargin != null) {
|
|
1668
|
+
out.extraMargin = asNonNegativeDecimalString(data.extraMargin, "buOrderData.extraMargin");
|
|
1669
|
+
}
|
|
1670
|
+
if (data.condition != null) out.condition = asOptionalString(data.condition, "buOrderData.condition");
|
|
1671
|
+
if (data.conditionDirection != null) {
|
|
1672
|
+
const v = asNonEmptyString(data.conditionDirection, "buOrderData.conditionDirection");
|
|
1673
|
+
assertEnum(v, "buOrderData.conditionDirection", ["-1", "1"]);
|
|
1674
|
+
out.conditionDirection = v;
|
|
1675
|
+
}
|
|
1676
|
+
if (data.lossStopType != null) {
|
|
1677
|
+
const v = asNonEmptyString(data.lossStopType, "buOrderData.lossStopType");
|
|
1678
|
+
assertEnum(v, "buOrderData.lossStopType", ["price", "profit_amount", "profit_ratio", "price_limit"]);
|
|
1679
|
+
out.lossStopType = v;
|
|
1680
|
+
}
|
|
1681
|
+
if (data.lossStop != null) out.lossStop = asOptionalString(data.lossStop, "buOrderData.lossStop");
|
|
1682
|
+
if (data.lossStopDelay != null) out.lossStopDelay = asOptionalNonNegativeNumber(data.lossStopDelay, "buOrderData.lossStopDelay");
|
|
1683
|
+
if (data.profitStopType != null) {
|
|
1684
|
+
const v = asNonEmptyString(data.profitStopType, "buOrderData.profitStopType");
|
|
1685
|
+
assertEnum(v, "buOrderData.profitStopType", ["price", "profit_amount", "profit_ratio", "price_limit"]);
|
|
1686
|
+
out.profitStopType = v;
|
|
1687
|
+
}
|
|
1688
|
+
if (data.profitStop != null) out.profitStop = asOptionalString(data.profitStop, "buOrderData.profitStop");
|
|
1689
|
+
if (data.profitStopDelay != null) out.profitStopDelay = asOptionalNonNegativeNumber(data.profitStopDelay, "buOrderData.profitStopDelay");
|
|
1690
|
+
if (data.lossStopHigh != null) out.lossStopHigh = asOptionalString(data.lossStopHigh, "buOrderData.lossStopHigh");
|
|
1691
|
+
if (data.shareRatio != null) out.shareRatio = asOptionalString(data.shareRatio, "buOrderData.shareRatio");
|
|
1692
|
+
if (data.investCoin != null) out.investCoin = asOptionalString(data.investCoin, "buOrderData.investCoin");
|
|
1693
|
+
if (data.investmentFrom != null) {
|
|
1694
|
+
const v = asNonEmptyString(data.investmentFrom, "buOrderData.investmentFrom");
|
|
1695
|
+
assertEnum(v, "buOrderData.investmentFrom", ["USER", "LOCK_ACTIVITY", "FUTURE_GRID_BONUS"]);
|
|
1696
|
+
out.investmentFrom = v;
|
|
1697
|
+
}
|
|
1698
|
+
if (data.uiInvestCoin != null) out.uiInvestCoin = asOptionalString(data.uiInvestCoin, "buOrderData.uiInvestCoin");
|
|
1699
|
+
if (data.lossStopLimitPrice != null) out.lossStopLimitPrice = asOptionalString(data.lossStopLimitPrice, "buOrderData.lossStopLimitPrice");
|
|
1700
|
+
if (data.lossStopLimitHighPrice != null) out.lossStopLimitHighPrice = asOptionalString(data.lossStopLimitHighPrice, "buOrderData.lossStopLimitHighPrice");
|
|
1701
|
+
if (data.profitStopLimitPrice != null) out.profitStopLimitPrice = asOptionalString(data.profitStopLimitPrice, "buOrderData.profitStopLimitPrice");
|
|
1702
|
+
if (data.slippage != null) out.slippage = asOptionalString(data.slippage, "buOrderData.slippage");
|
|
1703
|
+
if (data.bonusId != null) out.bonusId = asOptionalString(data.bonusId, "buOrderData.bonusId");
|
|
1704
|
+
if (data.uiExtraData != null) out.uiExtraData = asOptionalString(data.uiExtraData, "buOrderData.uiExtraData");
|
|
1705
|
+
if (data.movingIndicatorType != null) out.movingIndicatorType = asOptionalString(data.movingIndicatorType, "buOrderData.movingIndicatorType");
|
|
1706
|
+
if (data.movingIndicatorInterval != null) out.movingIndicatorInterval = asOptionalString(data.movingIndicatorInterval, "buOrderData.movingIndicatorInterval");
|
|
1707
|
+
if (data.movingIndicatorParam != null) out.movingIndicatorParam = asOptionalString(data.movingIndicatorParam, "buOrderData.movingIndicatorParam");
|
|
1708
|
+
if (data.movingTrailingUpParam != null) out.movingTrailingUpParam = asOptionalString(data.movingTrailingUpParam, "buOrderData.movingTrailingUpParam");
|
|
1709
|
+
if (data.cateType != null) {
|
|
1710
|
+
const v = asNonEmptyString(data.cateType, "buOrderData.cateType");
|
|
1711
|
+
assertEnum(v, "buOrderData.cateType", ["FULLY_HEDGING", "LOAN_GRID", "LEVERAGE_GRID", "FUTURE_GRID_COIN_MARGINED"]);
|
|
1712
|
+
out.cateType = v;
|
|
1713
|
+
}
|
|
1714
|
+
if (data.movingTop != null) out.movingTop = asOptionalString(data.movingTop, "buOrderData.movingTop");
|
|
1715
|
+
if (data.movingBottom != null) out.movingBottom = asOptionalString(data.movingBottom, "buOrderData.movingBottom");
|
|
1716
|
+
if (data.enableFollowClosed != null) out.enableFollowClosed = asBoolean(data.enableFollowClosed, "buOrderData.enableFollowClosed");
|
|
1717
|
+
return out;
|
|
1718
|
+
}
|
|
1719
|
+
var createFuturesGridOrderDataJsonSchema = {
|
|
1720
|
+
type: "object",
|
|
1721
|
+
additionalProperties: false,
|
|
1722
|
+
description: "CreateFuturesGridOrderData (openapi_bot.yaml). Required: top, bottom, row, grid_type, trend, leverage, quoteInvestment.",
|
|
1723
|
+
required: ["top", "bottom", "row", "grid_type", "trend", "leverage", "quoteInvestment"],
|
|
1724
|
+
properties: {
|
|
1725
|
+
top: { type: "string", description: "Grid upper price" },
|
|
1726
|
+
bottom: { type: "string", description: "Grid lower price" },
|
|
1727
|
+
row: { type: "number", description: "Number of grid levels" },
|
|
1728
|
+
grid_type: {
|
|
1729
|
+
type: "string",
|
|
1730
|
+
enum: ["arithmetic", "geometric"],
|
|
1731
|
+
description: "Grid spacing: arithmetic (equal difference) or geometric (equal ratio)"
|
|
1732
|
+
},
|
|
1733
|
+
trend: {
|
|
1734
|
+
type: "string",
|
|
1735
|
+
enum: ["long", "short", "no_trend"],
|
|
1736
|
+
description: "Grid direction"
|
|
1737
|
+
},
|
|
1738
|
+
leverage: { type: "number", description: "Leverage multiplier" },
|
|
1739
|
+
extraMargin: { type: "string", description: "Extra margin amount (optional)" },
|
|
1740
|
+
quoteInvestment: { type: "string", description: "Investment amount" },
|
|
1741
|
+
condition: { type: "string", description: "Trigger price (conditional orders)" },
|
|
1742
|
+
conditionDirection: { type: "string", enum: ["-1", "1"], description: "Trigger direction" },
|
|
1743
|
+
lossStopType: {
|
|
1744
|
+
type: "string",
|
|
1745
|
+
enum: ["price", "profit_amount", "profit_ratio", "price_limit"],
|
|
1746
|
+
description: "Stop loss type"
|
|
1747
|
+
},
|
|
1748
|
+
lossStop: { type: "string", description: "Stop loss value" },
|
|
1749
|
+
lossStopDelay: { type: "number", description: "Stop loss delay (seconds)" },
|
|
1750
|
+
profitStopType: {
|
|
1751
|
+
type: "string",
|
|
1752
|
+
enum: ["price", "profit_amount", "profit_ratio", "price_limit"],
|
|
1753
|
+
description: "Take profit type"
|
|
1754
|
+
},
|
|
1755
|
+
profitStop: { type: "string", description: "Take profit value" },
|
|
1756
|
+
profitStopDelay: { type: "number", description: "Take profit delay (seconds)" },
|
|
1757
|
+
lossStopHigh: { type: "string", description: "Upper stop loss price for neutral grid" },
|
|
1758
|
+
shareRatio: { type: "string", description: "Profit sharing ratio" },
|
|
1759
|
+
investCoin: { type: "string", description: "Investment currency" },
|
|
1760
|
+
investmentFrom: {
|
|
1761
|
+
type: "string",
|
|
1762
|
+
enum: ["USER", "LOCK_ACTIVITY", "FUTURE_GRID_BONUS"],
|
|
1763
|
+
description: "Funding source"
|
|
1764
|
+
},
|
|
1765
|
+
uiInvestCoin: { type: "string", description: "Frontend-recorded investment currency" },
|
|
1766
|
+
lossStopLimitPrice: { type: "string", description: "Limit SL price (lossStopType=price_limit)" },
|
|
1767
|
+
lossStopLimitHighPrice: { type: "string", description: "Upper limit SL for neutral grid" },
|
|
1768
|
+
profitStopLimitPrice: { type: "string", description: "Limit TP price (profitStopType=price_limit)" },
|
|
1769
|
+
slippage: { type: "string", description: "Open slippage e.g. 0.01 = 1%" },
|
|
1770
|
+
bonusId: { type: "string", description: "Bonus UUID" },
|
|
1771
|
+
uiExtraData: { type: "string", description: "Frontend extra (coin-margined)" },
|
|
1772
|
+
movingIndicatorType: { type: "string", description: "e.g. sma" },
|
|
1773
|
+
movingIndicatorInterval: { type: "string", description: "e.g. 1m, 15m" },
|
|
1774
|
+
movingIndicatorParam: { type: "string", description: "JSON params e.g. length" },
|
|
1775
|
+
movingTrailingUpParam: { type: "string", description: "SMA trailing up ratio" },
|
|
1776
|
+
cateType: {
|
|
1777
|
+
type: "string",
|
|
1778
|
+
enum: ["FULLY_HEDGING", "LOAN_GRID", "LEVERAGE_GRID", "FUTURE_GRID_COIN_MARGINED"],
|
|
1779
|
+
description: "Category type"
|
|
1780
|
+
},
|
|
1781
|
+
movingTop: { type: "string", description: "Moving grid upper limit" },
|
|
1782
|
+
movingBottom: { type: "string", description: "Moving grid lower limit" },
|
|
1783
|
+
enableFollowClosed: { type: "boolean", description: "Follow close" }
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
var createFuturesGridCreateToolInputSchema = {
|
|
1787
|
+
type: "object",
|
|
1788
|
+
additionalProperties: false,
|
|
1789
|
+
required: ["base", "quote", "buOrderData"],
|
|
1790
|
+
properties: {
|
|
1791
|
+
base: { type: "string", description: "Base currency (e.g. BTC); *.PERP normalized in handler" },
|
|
1792
|
+
quote: { type: "string", description: "Quote currency (e.g. USDT)" },
|
|
1793
|
+
copyFrom: { type: "string", description: "Optional. Copy source order ID" },
|
|
1794
|
+
copyType: { type: "string", description: "Optional. Copy type" },
|
|
1795
|
+
copyBotOrderId: { type: "string", description: "Optional. Copy bot order ID" },
|
|
1796
|
+
buOrderData: createFuturesGridOrderDataJsonSchema,
|
|
1797
|
+
__dryRun: { type: "boolean", description: "Internal: when true, return resolved body without POST" }
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
var CREATE_SPOT_GRID_ORDER_DATA_KEYS = [
|
|
1801
|
+
"top",
|
|
1802
|
+
"bottom",
|
|
1803
|
+
"row",
|
|
1804
|
+
"gridType",
|
|
1805
|
+
"quoteTotalInvestment",
|
|
1806
|
+
"lossStopType",
|
|
1807
|
+
"lossStop",
|
|
1808
|
+
"lossStopDelay",
|
|
1809
|
+
"profitStopType",
|
|
1810
|
+
"profitStop",
|
|
1811
|
+
"profitStopDelay",
|
|
1812
|
+
"condition",
|
|
1813
|
+
"conditionDirection",
|
|
1814
|
+
"slippage",
|
|
1815
|
+
"closeSellModel"
|
|
1816
|
+
];
|
|
1817
|
+
var ORDER_DATA_KEY_SET2 = new Set(CREATE_SPOT_GRID_ORDER_DATA_KEYS);
|
|
1818
|
+
function asNonEmptyString2(value, field) {
|
|
1819
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1820
|
+
throw new Error(`Invalid "${field}": expected non-empty string.`);
|
|
1821
|
+
}
|
|
1822
|
+
return value.trim();
|
|
1823
|
+
}
|
|
1824
|
+
function asFiniteNumber2(value, field) {
|
|
1825
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1826
|
+
throw new Error(`Invalid "${field}": expected finite number.`);
|
|
1827
|
+
}
|
|
1828
|
+
return value;
|
|
1829
|
+
}
|
|
1830
|
+
function asPositiveNumber2(value, field) {
|
|
1831
|
+
const n = asFiniteNumber2(value, field);
|
|
1832
|
+
if (n <= 0) throw new Error(`Invalid "${field}": expected number > 0.`);
|
|
1833
|
+
return n;
|
|
1834
|
+
}
|
|
1835
|
+
function asPositiveInteger2(value, field) {
|
|
1836
|
+
const n = asPositiveNumber2(value, field);
|
|
1837
|
+
if (!Number.isInteger(n)) {
|
|
1838
|
+
throw new Error(`Invalid "${field}": expected positive integer.`);
|
|
1839
|
+
}
|
|
1840
|
+
return n;
|
|
1841
|
+
}
|
|
1842
|
+
function asNonNegativeInteger(value, field) {
|
|
1843
|
+
const n = asFiniteNumber2(value, field);
|
|
1844
|
+
if (n < 0 || !Number.isInteger(n)) {
|
|
1845
|
+
throw new Error(`Invalid "${field}": expected non-negative integer.`);
|
|
1846
|
+
}
|
|
1847
|
+
return n;
|
|
1848
|
+
}
|
|
1849
|
+
function assertEnum2(value, field, allowed) {
|
|
1850
|
+
if (!allowed.includes(value)) {
|
|
1851
|
+
throw new Error(`Invalid "${field}": expected one of ${allowed.join(", ")}.`);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
function asPositiveDecimalStringLoose2(value, field) {
|
|
1855
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
1856
|
+
return String(value);
|
|
1857
|
+
}
|
|
1858
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1859
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
1860
|
+
}
|
|
1861
|
+
const s = value.trim();
|
|
1862
|
+
if (!/^\d+(\.\d+)?$/.test(s)) {
|
|
1863
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
1864
|
+
}
|
|
1865
|
+
const n = Number(s);
|
|
1866
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
1867
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
1868
|
+
}
|
|
1869
|
+
return s;
|
|
1870
|
+
}
|
|
1871
|
+
function asOptionalString2(value, field) {
|
|
1872
|
+
if (typeof value !== "string") {
|
|
1873
|
+
throw new Error(`Invalid "${field}": expected string.`);
|
|
1874
|
+
}
|
|
1875
|
+
return value;
|
|
1876
|
+
}
|
|
1877
|
+
function parseAndValidateCreateSpotGridBuOrderData(raw) {
|
|
1878
|
+
const data = { ...raw };
|
|
1879
|
+
for (const k of Object.keys(data)) {
|
|
1880
|
+
if (!ORDER_DATA_KEY_SET2.has(k)) {
|
|
1881
|
+
throw new Error(`Unknown buOrderData property "${k}". Allowed keys: ${CREATE_SPOT_GRID_ORDER_DATA_KEYS.join(", ")}.`);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
const top = asPositiveDecimalStringLoose2(data.top, "buOrderData.top");
|
|
1885
|
+
const bottom = asPositiveDecimalStringLoose2(data.bottom, "buOrderData.bottom");
|
|
1886
|
+
if (Number(top) <= Number(bottom)) {
|
|
1887
|
+
throw new Error('Invalid "buOrderData.top": expected top > bottom.');
|
|
1888
|
+
}
|
|
1889
|
+
const row = asPositiveInteger2(data.row, "buOrderData.row");
|
|
1890
|
+
if (row < 2 || row > 200) {
|
|
1891
|
+
throw new Error('Invalid "buOrderData.row": expected integer between 2 and 200.');
|
|
1892
|
+
}
|
|
1893
|
+
const gridType = asNonEmptyString2(data.gridType, "buOrderData.gridType");
|
|
1894
|
+
assertEnum2(gridType, "buOrderData.gridType", ["arithmetic", "geometric"]);
|
|
1895
|
+
const quoteTotalInvestment = asPositiveDecimalStringLoose2(data.quoteTotalInvestment, "buOrderData.quoteTotalInvestment");
|
|
1896
|
+
const out = {
|
|
1897
|
+
top,
|
|
1898
|
+
bottom,
|
|
1899
|
+
row,
|
|
1900
|
+
gridType,
|
|
1901
|
+
quoteTotalInvestment
|
|
1902
|
+
};
|
|
1903
|
+
if (data.lossStopType != null) {
|
|
1904
|
+
const v = asNonEmptyString2(data.lossStopType, "buOrderData.lossStopType");
|
|
1905
|
+
assertEnum2(v, "buOrderData.lossStopType", ["price", "profit_amount", "profit_ratio"]);
|
|
1906
|
+
out.lossStopType = v;
|
|
1907
|
+
}
|
|
1908
|
+
if (data.lossStop != null) out.lossStop = asOptionalString2(data.lossStop, "buOrderData.lossStop");
|
|
1909
|
+
if (data.lossStopDelay != null) out.lossStopDelay = asNonNegativeInteger(data.lossStopDelay, "buOrderData.lossStopDelay");
|
|
1910
|
+
if (data.profitStopType != null) {
|
|
1911
|
+
const v = asNonEmptyString2(data.profitStopType, "buOrderData.profitStopType");
|
|
1912
|
+
assertEnum2(v, "buOrderData.profitStopType", ["price", "profit_amount", "profit_ratio"]);
|
|
1913
|
+
out.profitStopType = v;
|
|
1914
|
+
}
|
|
1915
|
+
if (data.profitStop != null) out.profitStop = asOptionalString2(data.profitStop, "buOrderData.profitStop");
|
|
1916
|
+
if (data.profitStopDelay != null) out.profitStopDelay = asNonNegativeInteger(data.profitStopDelay, "buOrderData.profitStopDelay");
|
|
1917
|
+
if (data.condition != null) out.condition = asOptionalString2(data.condition, "buOrderData.condition");
|
|
1918
|
+
if (data.conditionDirection != null) {
|
|
1919
|
+
const v = asNonEmptyString2(data.conditionDirection, "buOrderData.conditionDirection");
|
|
1920
|
+
assertEnum2(v, "buOrderData.conditionDirection", ["-1", "1"]);
|
|
1921
|
+
out.conditionDirection = v;
|
|
1922
|
+
}
|
|
1923
|
+
if (data.slippage != null) out.slippage = asOptionalString2(data.slippage, "buOrderData.slippage");
|
|
1924
|
+
if (data.closeSellModel != null) {
|
|
1925
|
+
const v = asNonEmptyString2(data.closeSellModel, "buOrderData.closeSellModel");
|
|
1926
|
+
assertEnum2(v, "buOrderData.closeSellModel", ["NOT_SELL", "TO_QUOTE", "TO_USDT"]);
|
|
1927
|
+
out.closeSellModel = v;
|
|
1928
|
+
}
|
|
1929
|
+
return out;
|
|
1930
|
+
}
|
|
1931
|
+
var createSpotGridOrderDataJsonSchema = {
|
|
1932
|
+
type: "object",
|
|
1933
|
+
additionalProperties: false,
|
|
1934
|
+
description: "CreateSpotGridOrderData (openapi_bot.yaml PR #7). Required: top, bottom, row, gridType, quoteTotalInvestment.",
|
|
1935
|
+
required: ["top", "bottom", "row", "gridType", "quoteTotalInvestment"],
|
|
1936
|
+
properties: {
|
|
1937
|
+
top: { type: "string", description: "Grid upper price" },
|
|
1938
|
+
bottom: { type: "string", description: "Grid lower price" },
|
|
1939
|
+
row: { type: "number", description: "Number of grid levels (2\u2013200)" },
|
|
1940
|
+
gridType: {
|
|
1941
|
+
type: "string",
|
|
1942
|
+
enum: ["arithmetic", "geometric"],
|
|
1943
|
+
description: "Grid spacing: arithmetic (equal difference) or geometric (equal ratio)"
|
|
1944
|
+
},
|
|
1945
|
+
quoteTotalInvestment: { type: "string", description: "Total quote currency investment amount" },
|
|
1946
|
+
lossStopType: {
|
|
1947
|
+
type: "string",
|
|
1948
|
+
enum: ["price", "profit_amount", "profit_ratio"],
|
|
1949
|
+
description: "Stop loss type"
|
|
1950
|
+
},
|
|
1951
|
+
lossStop: { type: "string", description: "Stop loss threshold value" },
|
|
1952
|
+
lossStopDelay: { type: "number", description: "Seconds before executing stop loss (0=immediate)" },
|
|
1953
|
+
profitStopType: {
|
|
1954
|
+
type: "string",
|
|
1955
|
+
enum: ["price", "profit_amount", "profit_ratio"],
|
|
1956
|
+
description: "Take profit type"
|
|
1957
|
+
},
|
|
1958
|
+
profitStop: { type: "string", description: "Take profit threshold value" },
|
|
1959
|
+
profitStopDelay: { type: "number", description: "Seconds before executing take profit (0=immediate)" },
|
|
1960
|
+
condition: { type: "string", description: "Trigger price for conditional start" },
|
|
1961
|
+
conditionDirection: {
|
|
1962
|
+
type: "string",
|
|
1963
|
+
enum: ["-1", "1"],
|
|
1964
|
+
description: 'Trigger direction: "-1" drop below, "1" rise above'
|
|
1965
|
+
},
|
|
1966
|
+
slippage: { type: "string", description: "Slippage tolerance e.g. 0.01 = 1%" },
|
|
1967
|
+
closeSellModel: {
|
|
1968
|
+
type: "string",
|
|
1969
|
+
enum: ["NOT_SELL", "TO_QUOTE", "TO_USDT"],
|
|
1970
|
+
description: "Close sell model (default: NOT_SELL)"
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1974
|
+
var createSpotGridCreateToolInputSchema = {
|
|
1975
|
+
type: "object",
|
|
1976
|
+
additionalProperties: false,
|
|
1977
|
+
required: ["base", "quote", "buOrderData"],
|
|
1978
|
+
properties: {
|
|
1979
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
1980
|
+
quote: { type: "string", description: "Quote currency (e.g. USDT)" },
|
|
1981
|
+
note: { type: "string", description: "Optional order note" },
|
|
1982
|
+
buOrderData: createSpotGridOrderDataJsonSchema,
|
|
1983
|
+
__dryRun: { type: "boolean", description: "Internal: when true, return resolved body without POST" }
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
function asNonEmptyString3(value, field) {
|
|
1987
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1988
|
+
throw new Error(`Invalid "${field}": expected non-empty string.`);
|
|
1989
|
+
}
|
|
1990
|
+
return value.trim();
|
|
1991
|
+
}
|
|
1992
|
+
function asFiniteNumber3(value, field) {
|
|
1993
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1994
|
+
throw new Error(`Invalid "${field}": expected finite number.`);
|
|
1995
|
+
}
|
|
1996
|
+
return value;
|
|
1997
|
+
}
|
|
1998
|
+
function asPositiveNumber3(value, field) {
|
|
1999
|
+
const n = asFiniteNumber3(value, field);
|
|
2000
|
+
if (n <= 0) throw new Error(`Invalid "${field}": expected number > 0.`);
|
|
2001
|
+
return n;
|
|
2002
|
+
}
|
|
2003
|
+
function asPositiveInteger3(value, field) {
|
|
2004
|
+
const n = asPositiveNumber3(value, field);
|
|
2005
|
+
if (!Number.isInteger(n)) {
|
|
2006
|
+
throw new Error(`Invalid "${field}": expected positive integer.`);
|
|
2007
|
+
}
|
|
2008
|
+
return n;
|
|
2009
|
+
}
|
|
2010
|
+
function asBoolean2(value, field) {
|
|
2011
|
+
if (typeof value !== "boolean") {
|
|
2012
|
+
throw new Error(`Invalid "${field}": expected boolean.`);
|
|
2013
|
+
}
|
|
2014
|
+
return value;
|
|
2015
|
+
}
|
|
2016
|
+
function assertEnum3(value, field, allowed) {
|
|
2017
|
+
if (!allowed.includes(value)) {
|
|
2018
|
+
throw new Error(`Invalid "${field}": expected one of ${allowed.join(", ")}.`);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
function asObject(value, field) {
|
|
2022
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2023
|
+
throw new Error(`Invalid "${field}": expected JSON object.`);
|
|
2024
|
+
}
|
|
2025
|
+
return value;
|
|
2026
|
+
}
|
|
2027
|
+
function asPositiveDecimalString2(value, field) {
|
|
2028
|
+
const s = asNonEmptyString3(value, field);
|
|
2029
|
+
if (!/^\d+(\.\d+)?$/.test(s)) {
|
|
2030
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
2031
|
+
}
|
|
2032
|
+
const n = Number(s);
|
|
2033
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
2034
|
+
throw new Error(`Invalid "${field}": expected positive decimal string.`);
|
|
2035
|
+
}
|
|
2036
|
+
return s;
|
|
2037
|
+
}
|
|
2038
|
+
function normalizePerpBase(base) {
|
|
2039
|
+
return base.endsWith(".PERP") ? base : `${base}.PERP`;
|
|
2040
|
+
}
|
|
2041
|
+
function registerBotTools() {
|
|
2042
|
+
return [
|
|
2043
|
+
{
|
|
2044
|
+
name: "pionex_bot_futures_grid_get_order",
|
|
2045
|
+
module: "bot",
|
|
2046
|
+
isWrite: false,
|
|
2047
|
+
description: "Get one futures grid bot order by buOrderId.",
|
|
2048
|
+
inputSchema: {
|
|
2049
|
+
type: "object",
|
|
2050
|
+
additionalProperties: false,
|
|
2051
|
+
properties: {
|
|
2052
|
+
buOrderId: { type: "string", description: "Futures grid bot order id." },
|
|
2053
|
+
lang: { type: "string", description: "Optional language code." }
|
|
2054
|
+
},
|
|
2055
|
+
required: ["buOrderId"]
|
|
2056
|
+
},
|
|
2057
|
+
async handler(args, { client }) {
|
|
2058
|
+
const buOrderId = String(args.buOrderId);
|
|
2059
|
+
const q = { buOrderId };
|
|
2060
|
+
if (args.lang != null) q.lang = String(args.lang);
|
|
2061
|
+
return (await client.signedGet("/api/v1/bot/orders/futuresGrid/order", q)).data;
|
|
2062
|
+
}
|
|
2063
|
+
},
|
|
2064
|
+
{
|
|
2065
|
+
name: "pionex_bot_futures_grid_create",
|
|
2066
|
+
module: "bot",
|
|
2067
|
+
isWrite: true,
|
|
2068
|
+
description: "Create a futures grid order (openapi_bot.yaml CreateFuturesGridRequest / CreateFuturesGridOrderData). https://github.com/pionex-official/pionex-open-api/blob/main/openapi_bot.yaml \u2014 Required: base, quote, buOrderData. Optional: copyFrom, copyType, copyBotOrderId. buOrderData required: top, bottom, row, grid_type, trend, leverage, quoteInvestment; unknown keys rejected.",
|
|
2069
|
+
inputSchema: createFuturesGridCreateToolInputSchema,
|
|
2070
|
+
async handler(args, { client, config }) {
|
|
2071
|
+
if (config.readOnly) {
|
|
2072
|
+
throw new Error("Server is running in --read-only mode; bot futures_grid create is disabled.");
|
|
2073
|
+
}
|
|
2074
|
+
const rawBase = asNonEmptyString3(args.base, "base");
|
|
2075
|
+
const base = normalizePerpBase(rawBase);
|
|
2076
|
+
const quote = asNonEmptyString3(args.quote, "quote");
|
|
2077
|
+
const buOrderDataOut = parseAndValidateCreateFuturesGridBuOrderData(asObject(args.buOrderData, "buOrderData"));
|
|
2078
|
+
const row = buOrderDataOut.row;
|
|
2079
|
+
const gridType = buOrderDataOut.grid_type;
|
|
2080
|
+
const leverage = buOrderDataOut.leverage;
|
|
2081
|
+
const body = {
|
|
2082
|
+
base,
|
|
2083
|
+
quote,
|
|
2084
|
+
buOrderData: buOrderDataOut
|
|
2085
|
+
};
|
|
2086
|
+
if (args.copyFrom != null) body.copyFrom = String(args.copyFrom);
|
|
2087
|
+
if (args.copyType != null) body.copyType = String(args.copyType);
|
|
2088
|
+
if (args.copyBotOrderId != null) body.copyBotOrderId = String(args.copyBotOrderId);
|
|
2089
|
+
if (args.__dryRun === true) {
|
|
2090
|
+
return {
|
|
2091
|
+
dryRun: true,
|
|
2092
|
+
note: "No order was sent. Body matches openapi_bot.yaml CreateFuturesGridRequest.",
|
|
2093
|
+
resolvedParams: {
|
|
2094
|
+
row,
|
|
2095
|
+
grid_type: gridType,
|
|
2096
|
+
leverage
|
|
2097
|
+
},
|
|
2098
|
+
resolvedBody: body
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
return (await client.signedPost("/api/v1/bot/orders/futuresGrid/create", body)).data;
|
|
2102
|
+
}
|
|
2103
|
+
},
|
|
2104
|
+
{
|
|
2105
|
+
name: "pionex_bot_futures_grid_adjust_params",
|
|
2106
|
+
module: "bot",
|
|
2107
|
+
isWrite: true,
|
|
2108
|
+
description: "Adjust futures grid bot params (invest_in / adjust_params / invest_in_trigger).",
|
|
2109
|
+
inputSchema: {
|
|
2110
|
+
type: "object",
|
|
2111
|
+
additionalProperties: false,
|
|
2112
|
+
properties: {
|
|
2113
|
+
buOrderId: { type: "string" },
|
|
2114
|
+
type: { type: "string", enum: ["invest_in", "adjust_params", "invest_in_trigger"] },
|
|
2115
|
+
quoteInvestment: { type: "number" },
|
|
2116
|
+
extraMargin: { type: "boolean" },
|
|
2117
|
+
bottom: { type: "string" },
|
|
2118
|
+
top: { type: "string" },
|
|
2119
|
+
row: { type: "number" },
|
|
2120
|
+
extraMarginAmount: { type: "number" },
|
|
2121
|
+
isRecommend: { type: "boolean" },
|
|
2122
|
+
isReinvest: { type: "boolean" },
|
|
2123
|
+
investCoin: { type: "string" },
|
|
2124
|
+
investmentFrom: { type: "string", enum: ["USER", "LOCK_ACTIVITY"] },
|
|
2125
|
+
condition: { type: "string" },
|
|
2126
|
+
conditionDirection: { type: "string", enum: ["1", "-1"] },
|
|
2127
|
+
slippage: { type: "string" },
|
|
2128
|
+
adjustParamsScene: { type: "string" }
|
|
2129
|
+
},
|
|
2130
|
+
required: ["buOrderId", "type", "extraMargin"]
|
|
2131
|
+
},
|
|
2132
|
+
async handler(args, { client, config }) {
|
|
2133
|
+
if (config.readOnly) {
|
|
2134
|
+
throw new Error("Server is running in --read-only mode; bot futures_grid adjust_params is disabled.");
|
|
2135
|
+
}
|
|
2136
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2137
|
+
const type = asNonEmptyString3(args.type, "type");
|
|
2138
|
+
assertEnum3(type, "type", ["invest_in", "adjust_params", "invest_in_trigger"]);
|
|
2139
|
+
const extraMargin = asBoolean2(args.extraMargin, "extraMargin");
|
|
2140
|
+
if (type === "invest_in" && args.quoteInvestment != null) {
|
|
2141
|
+
asPositiveNumber3(args.quoteInvestment, "quoteInvestment");
|
|
2142
|
+
}
|
|
2143
|
+
if (type === "adjust_params") {
|
|
2144
|
+
const bottom = asPositiveDecimalString2(args.bottom, "bottom");
|
|
2145
|
+
const top = asPositiveDecimalString2(args.top, "top");
|
|
2146
|
+
if (Number(top) <= Number(bottom)) {
|
|
2147
|
+
throw new Error('Invalid "top": expected top > bottom.');
|
|
2148
|
+
}
|
|
2149
|
+
asPositiveInteger3(args.row, "row");
|
|
2150
|
+
}
|
|
2151
|
+
if (type === "invest_in_trigger") {
|
|
2152
|
+
asPositiveDecimalString2(args.condition, "condition");
|
|
2153
|
+
const conditionDirection = asNonEmptyString3(args.conditionDirection, "conditionDirection");
|
|
2154
|
+
assertEnum3(conditionDirection, "conditionDirection", ["1", "-1"]);
|
|
2155
|
+
}
|
|
2156
|
+
const body = {
|
|
2157
|
+
buOrderId,
|
|
2158
|
+
type,
|
|
2159
|
+
extraMargin
|
|
2160
|
+
};
|
|
2161
|
+
if (args.quoteInvestment != null) body.quoteInvestment = asFiniteNumber3(args.quoteInvestment, "quoteInvestment");
|
|
2162
|
+
if (args.bottom != null) body.bottom = asPositiveDecimalString2(args.bottom, "bottom");
|
|
2163
|
+
if (args.top != null) body.top = asPositiveDecimalString2(args.top, "top");
|
|
2164
|
+
if (args.row != null) body.row = asPositiveInteger3(args.row, "row");
|
|
2165
|
+
if (args.extraMarginAmount != null) body.extraMarginAmount = asFiniteNumber3(args.extraMarginAmount, "extraMarginAmount");
|
|
2166
|
+
if (args.isRecommend != null) body.isRecommend = asBoolean2(args.isRecommend, "isRecommend");
|
|
2167
|
+
if (args.isReinvest != null) body.isReinvest = asBoolean2(args.isReinvest, "isReinvest");
|
|
2168
|
+
if (args.investCoin != null) body.investCoin = String(args.investCoin);
|
|
2169
|
+
if (args.investmentFrom != null) {
|
|
2170
|
+
const investmentFrom = asNonEmptyString3(args.investmentFrom, "investmentFrom");
|
|
2171
|
+
assertEnum3(investmentFrom, "investmentFrom", ["USER", "LOCK_ACTIVITY"]);
|
|
2172
|
+
body.investmentFrom = investmentFrom;
|
|
2173
|
+
}
|
|
2174
|
+
if (args.condition != null) body.condition = asPositiveDecimalString2(args.condition, "condition");
|
|
2175
|
+
if (args.conditionDirection != null) {
|
|
2176
|
+
const conditionDirection = asNonEmptyString3(args.conditionDirection, "conditionDirection");
|
|
2177
|
+
assertEnum3(conditionDirection, "conditionDirection", ["1", "-1"]);
|
|
2178
|
+
body.conditionDirection = conditionDirection;
|
|
2179
|
+
}
|
|
2180
|
+
if (args.slippage != null) body.slippage = String(args.slippage);
|
|
2181
|
+
if (args.adjustParamsScene != null) body.adjustParamsScene = String(args.adjustParamsScene);
|
|
2182
|
+
return (await client.signedPost("/api/v1/bot/orders/futuresGrid/adjustParams", body)).data;
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
name: "pionex_bot_futures_grid_reduce",
|
|
2187
|
+
module: "bot",
|
|
2188
|
+
isWrite: true,
|
|
2189
|
+
description: "Reduce futures grid bot position.",
|
|
2190
|
+
inputSchema: {
|
|
2191
|
+
type: "object",
|
|
2192
|
+
additionalProperties: false,
|
|
2193
|
+
properties: {
|
|
2194
|
+
buOrderId: { type: "string" },
|
|
2195
|
+
reduceNum: { type: "number" },
|
|
2196
|
+
slippage: { type: "string" },
|
|
2197
|
+
condition: { type: "string" },
|
|
2198
|
+
conditionDirection: { type: "string", enum: ["1", "-1"] }
|
|
2199
|
+
},
|
|
2200
|
+
required: ["buOrderId", "reduceNum"]
|
|
2201
|
+
},
|
|
2202
|
+
async handler(args, { client, config }) {
|
|
2203
|
+
if (config.readOnly) {
|
|
2204
|
+
throw new Error("Server is running in --read-only mode; bot futures_grid reduce is disabled.");
|
|
2205
|
+
}
|
|
2206
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2207
|
+
const reduceNum = asPositiveInteger3(args.reduceNum, "reduceNum");
|
|
2208
|
+
const body = {
|
|
2209
|
+
buOrderId,
|
|
2210
|
+
reduceNum
|
|
2211
|
+
};
|
|
2212
|
+
if (args.slippage != null) body.slippage = String(args.slippage);
|
|
2213
|
+
if (args.condition != null) body.condition = String(args.condition);
|
|
2214
|
+
if (args.conditionDirection != null) {
|
|
2215
|
+
const conditionDirection = asNonEmptyString3(args.conditionDirection, "conditionDirection");
|
|
2216
|
+
assertEnum3(conditionDirection, "conditionDirection", ["1", "-1"]);
|
|
2217
|
+
body.conditionDirection = conditionDirection;
|
|
2218
|
+
}
|
|
2219
|
+
return (await client.signedPost("/api/v1/bot/orders/futuresGrid/reduce", body)).data;
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
name: "pionex_bot_futures_grid_check_params",
|
|
2224
|
+
module: "bot",
|
|
2225
|
+
isWrite: false,
|
|
2226
|
+
description: "Validate futures grid bot parameters before creating an order. Uses the same buOrderData structure as futures_grid_create. On FailedWithData error the response includes min_investment, max_investment, slippage. Endpoint: POST /api/v1/bot/orders/futuresGrid/checkParams",
|
|
2227
|
+
inputSchema: {
|
|
2228
|
+
type: "object",
|
|
2229
|
+
additionalProperties: false,
|
|
2230
|
+
required: ["base", "quote", "buOrderData"],
|
|
2231
|
+
properties: {
|
|
2232
|
+
base: { type: "string", description: "Base currency (e.g. BTC); *.PERP normalized in handler" },
|
|
2233
|
+
quote: { type: "string", description: "Quote currency (e.g. USDT)" },
|
|
2234
|
+
buOrderData: createFuturesGridOrderDataJsonSchema
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
async handler(args, { client }) {
|
|
2238
|
+
const base = normalizePerpBase(asNonEmptyString3(args.base, "base"));
|
|
2239
|
+
const quote = asNonEmptyString3(args.quote, "quote");
|
|
2240
|
+
const buOrderDataOut = parseAndValidateCreateFuturesGridBuOrderData(asObject(args.buOrderData, "buOrderData"));
|
|
2241
|
+
return (await client.signedPost("/api/v1/bot/orders/futuresGrid/checkParams", { base, quote, buOrderData: buOrderDataOut })).data;
|
|
2242
|
+
}
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
name: "pionex_bot_order_list",
|
|
2246
|
+
module: "bot",
|
|
2247
|
+
isWrite: false,
|
|
2248
|
+
description: "List bot orders with optional filters and pagination. status: 'running' (default) or 'finished'. buOrderTypes: one or more of futures_grid, spot_grid, smart_copy. Endpoint: GET /api/v1/bot/orders",
|
|
2249
|
+
inputSchema: {
|
|
2250
|
+
type: "object",
|
|
2251
|
+
additionalProperties: false,
|
|
2252
|
+
properties: {
|
|
2253
|
+
status: {
|
|
2254
|
+
type: "string",
|
|
2255
|
+
enum: ["running", "finished"],
|
|
2256
|
+
description: "Filter by order status. Default: 'running'."
|
|
2257
|
+
},
|
|
2258
|
+
base: { type: "string", description: "Base currency filter (e.g. BTC)." },
|
|
2259
|
+
quote: { type: "string", description: "Quote currency filter (e.g. USDT)." },
|
|
2260
|
+
pageToken: { type: "string", description: "Pagination token from a previous response." },
|
|
2261
|
+
buOrderTypes: {
|
|
2262
|
+
type: "array",
|
|
2263
|
+
items: { type: "string", enum: ["futures_grid", "spot_grid", "smart_copy"] },
|
|
2264
|
+
description: "Bot type filter: futures_grid, spot_grid, smart_copy. Omit to return all types."
|
|
2265
|
+
}
|
|
2266
|
+
},
|
|
2267
|
+
required: []
|
|
2268
|
+
},
|
|
2269
|
+
async handler(args, { client }) {
|
|
2270
|
+
const q = {};
|
|
2271
|
+
if (args.status != null) q.status = String(args.status);
|
|
2272
|
+
if (args.base != null) q.base = String(args.base);
|
|
2273
|
+
if (args.quote != null) q.quote = String(args.quote);
|
|
2274
|
+
if (args.pageToken != null) q.pageToken = String(args.pageToken);
|
|
2275
|
+
if (Array.isArray(args.buOrderTypes) && args.buOrderTypes.length > 0) {
|
|
2276
|
+
q.buOrderTypes = args.buOrderTypes.join(",");
|
|
2277
|
+
}
|
|
2278
|
+
return (await client.signedGet("/api/v1/bot/orders", q)).data;
|
|
2279
|
+
}
|
|
2280
|
+
},
|
|
2281
|
+
{
|
|
2282
|
+
name: "pionex_bot_futures_grid_cancel",
|
|
2283
|
+
module: "bot",
|
|
2284
|
+
isWrite: true,
|
|
2285
|
+
description: "Cancel and close a futures grid bot order.",
|
|
2286
|
+
inputSchema: {
|
|
2287
|
+
type: "object",
|
|
2288
|
+
additionalProperties: false,
|
|
2289
|
+
properties: {
|
|
2290
|
+
buOrderId: { type: "string" },
|
|
2291
|
+
closeNote: { type: "string" },
|
|
2292
|
+
closeSellModel: { type: "string", enum: ["TO_QUOTE", "TO_USDT"] },
|
|
2293
|
+
immediate: { type: "boolean" },
|
|
2294
|
+
closeSlippage: { type: "string" }
|
|
2295
|
+
},
|
|
2296
|
+
required: ["buOrderId"]
|
|
2297
|
+
},
|
|
2298
|
+
async handler(args, { client, config }) {
|
|
2299
|
+
if (config.readOnly) {
|
|
2300
|
+
throw new Error("Server is running in --read-only mode; bot futures_grid cancel is disabled.");
|
|
2301
|
+
}
|
|
2302
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2303
|
+
const body = { buOrderId };
|
|
2304
|
+
if (args.closeNote != null) body.closeNote = String(args.closeNote);
|
|
2305
|
+
if (args.closeSellModel != null) {
|
|
2306
|
+
const closeSellModel = asNonEmptyString3(args.closeSellModel, "closeSellModel");
|
|
2307
|
+
assertEnum3(closeSellModel, "closeSellModel", ["TO_QUOTE", "TO_USDT"]);
|
|
2308
|
+
body.closeSellModel = closeSellModel;
|
|
2309
|
+
}
|
|
2310
|
+
if (args.immediate != null) body.immediate = asBoolean2(args.immediate, "immediate");
|
|
2311
|
+
if (args.closeSlippage != null) body.closeSlippage = String(args.closeSlippage);
|
|
2312
|
+
return (await client.signedPost("/api/v1/bot/orders/futuresGrid/cancel", body)).data;
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
{
|
|
2316
|
+
name: "pionex_bot_spot_grid_get_order",
|
|
2317
|
+
module: "bot",
|
|
2318
|
+
isWrite: false,
|
|
2319
|
+
description: "Get one spot grid bot order by buOrderId.",
|
|
2320
|
+
inputSchema: {
|
|
2321
|
+
type: "object",
|
|
2322
|
+
additionalProperties: false,
|
|
2323
|
+
properties: {
|
|
2324
|
+
buOrderId: { type: "string", description: "Spot grid bot order id." }
|
|
2325
|
+
},
|
|
2326
|
+
required: ["buOrderId"]
|
|
2327
|
+
},
|
|
2328
|
+
async handler(args, { client }) {
|
|
2329
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2330
|
+
const q = { buOrderId };
|
|
2331
|
+
return (await client.signedGet("/api/v1/bot/orders/spotGrid/order", q)).data;
|
|
2332
|
+
}
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
name: "pionex_bot_spot_grid_get_ai_strategy",
|
|
2336
|
+
module: "bot",
|
|
2337
|
+
isWrite: false,
|
|
2338
|
+
description: "Retrieve AI-recommended spot grid strategy parameters for a trading pair.",
|
|
2339
|
+
inputSchema: {
|
|
2340
|
+
type: "object",
|
|
2341
|
+
additionalProperties: false,
|
|
2342
|
+
properties: {
|
|
2343
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2344
|
+
quote: { type: "string", description: "Quote currency (e.g. USDT)" }
|
|
2345
|
+
},
|
|
2346
|
+
required: ["base", "quote"]
|
|
2347
|
+
},
|
|
2348
|
+
async handler(args, { client }) {
|
|
2349
|
+
const base = asNonEmptyString3(args.base, "base");
|
|
2350
|
+
const quote = asNonEmptyString3(args.quote, "quote");
|
|
2351
|
+
return (await client.signedGet("/api/v1/bot/orders/spotGrid/aiStrategy", { base, quote })).data;
|
|
2352
|
+
}
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
name: "pionex_bot_spot_grid_check_params",
|
|
2356
|
+
module: "bot",
|
|
2357
|
+
isWrite: false,
|
|
2358
|
+
description: "Validate spot grid bot parameters before creating an order. Uses the same buOrderData structure as spot_grid_create. On FailedWithData error the response includes min_investment, max_investment, slippage. Endpoint: POST /api/v1/bot/orders/spotGrid/checkParams",
|
|
2359
|
+
inputSchema: {
|
|
2360
|
+
type: "object",
|
|
2361
|
+
additionalProperties: false,
|
|
2362
|
+
required: ["base", "quote", "buOrderData"],
|
|
2363
|
+
properties: {
|
|
2364
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2365
|
+
quote: { type: "string", description: "Quote currency (e.g. USDT)" },
|
|
2366
|
+
buOrderData: createSpotGridOrderDataJsonSchema
|
|
2367
|
+
}
|
|
2368
|
+
},
|
|
2369
|
+
async handler(args, { client }) {
|
|
2370
|
+
const base = asNonEmptyString3(args.base, "base");
|
|
2371
|
+
const quote = asNonEmptyString3(args.quote, "quote");
|
|
2372
|
+
const buOrderDataOut = parseAndValidateCreateSpotGridBuOrderData(asObject(args.buOrderData, "buOrderData"));
|
|
2373
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/checkParams", { base, quote, buOrderData: buOrderDataOut })).data;
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2376
|
+
{
|
|
2377
|
+
name: "pionex_bot_spot_grid_create",
|
|
2378
|
+
module: "bot",
|
|
2379
|
+
isWrite: true,
|
|
2380
|
+
description: "Create a spot grid bot order (openapi_bot.yaml CreateSpotGridRequest / CreateSpotGridOrderData). Required: base, quote, buOrderData. Optional: note. buOrderData required: top, bottom, row, gridType, quoteTotalInvestment; unknown keys rejected.",
|
|
2381
|
+
inputSchema: createSpotGridCreateToolInputSchema,
|
|
2382
|
+
async handler(args, { client, config }) {
|
|
2383
|
+
if (config.readOnly) {
|
|
2384
|
+
throw new Error("Server is running in --read-only mode; bot spot_grid create is disabled.");
|
|
2385
|
+
}
|
|
2386
|
+
const base = asNonEmptyString3(args.base, "base");
|
|
2387
|
+
const quote = asNonEmptyString3(args.quote, "quote");
|
|
2388
|
+
const buOrderDataOut = parseAndValidateCreateSpotGridBuOrderData(asObject(args.buOrderData, "buOrderData"));
|
|
2389
|
+
const body = { base, quote, buOrderData: buOrderDataOut };
|
|
2390
|
+
if (args.note != null) body.note = String(args.note);
|
|
2391
|
+
if (args.__dryRun === true) {
|
|
2392
|
+
return {
|
|
2393
|
+
dryRun: true,
|
|
2394
|
+
note: "No order was sent. Body matches openapi_bot.yaml CreateSpotGridRequest.",
|
|
2395
|
+
resolvedBody: body
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/create", body)).data;
|
|
2399
|
+
}
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
name: "pionex_bot_spot_grid_adjust_params",
|
|
2403
|
+
module: "bot",
|
|
2404
|
+
isWrite: true,
|
|
2405
|
+
description: "Adjust spot grid bot range or investment parameters.",
|
|
2406
|
+
inputSchema: {
|
|
2407
|
+
type: "object",
|
|
2408
|
+
additionalProperties: false,
|
|
2409
|
+
properties: {
|
|
2410
|
+
buOrderId: { type: "string" },
|
|
2411
|
+
top: { type: "string", description: "New upper price" },
|
|
2412
|
+
bottom: { type: "string", description: "New lower price" },
|
|
2413
|
+
row: { type: "number", description: "New number of grid levels" },
|
|
2414
|
+
quoteInvest: { type: "string", description: "Additional quote investment amount" }
|
|
2415
|
+
},
|
|
2416
|
+
required: ["buOrderId"]
|
|
2417
|
+
},
|
|
2418
|
+
async handler(args, { client, config }) {
|
|
2419
|
+
if (config.readOnly) {
|
|
2420
|
+
throw new Error("Server is running in --read-only mode; bot spot_grid adjust_params is disabled.");
|
|
2421
|
+
}
|
|
2422
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2423
|
+
const body = { buOrderId };
|
|
2424
|
+
if (args.top != null) body.top = asPositiveDecimalString2(args.top, "top");
|
|
2425
|
+
if (args.bottom != null) body.bottom = asPositiveDecimalString2(args.bottom, "bottom");
|
|
2426
|
+
if (args.row != null) body.row = asPositiveInteger3(args.row, "row");
|
|
2427
|
+
if (args.quoteInvest != null) body.quoteInvest = asPositiveDecimalString2(args.quoteInvest, "quoteInvest");
|
|
2428
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/adjustParams", body)).data;
|
|
2429
|
+
}
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: "pionex_bot_spot_grid_invest_in",
|
|
2433
|
+
module: "bot",
|
|
2434
|
+
isWrite: true,
|
|
2435
|
+
description: "Add additional quote investment to a running spot grid bot.",
|
|
2436
|
+
inputSchema: {
|
|
2437
|
+
type: "object",
|
|
2438
|
+
additionalProperties: false,
|
|
2439
|
+
properties: {
|
|
2440
|
+
buOrderId: { type: "string" },
|
|
2441
|
+
quoteInvest: { type: "string", description: "Quote amount to invest" }
|
|
2442
|
+
},
|
|
2443
|
+
required: ["buOrderId", "quoteInvest"]
|
|
2444
|
+
},
|
|
2445
|
+
async handler(args, { client, config }) {
|
|
2446
|
+
if (config.readOnly) {
|
|
2447
|
+
throw new Error("Server is running in --read-only mode; bot spot_grid invest_in is disabled.");
|
|
2448
|
+
}
|
|
2449
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2450
|
+
const quoteInvest = asPositiveDecimalString2(args.quoteInvest, "quoteInvest");
|
|
2451
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/investIn", { buOrderId, quoteInvest })).data;
|
|
2452
|
+
}
|
|
2453
|
+
},
|
|
2454
|
+
{
|
|
2455
|
+
name: "pionex_bot_spot_grid_cancel",
|
|
2456
|
+
module: "bot",
|
|
2457
|
+
isWrite: true,
|
|
2458
|
+
description: "Cancel and close a spot grid bot order.",
|
|
2459
|
+
inputSchema: {
|
|
2460
|
+
type: "object",
|
|
2461
|
+
additionalProperties: false,
|
|
2462
|
+
properties: {
|
|
2463
|
+
buOrderId: { type: "string" },
|
|
2464
|
+
closeSellModel: { type: "string", enum: ["NOT_SELL", "TO_QUOTE", "TO_USDT"] },
|
|
2465
|
+
slippage: { type: "string" }
|
|
2466
|
+
},
|
|
2467
|
+
required: ["buOrderId"]
|
|
2468
|
+
},
|
|
2469
|
+
async handler(args, { client, config }) {
|
|
2470
|
+
if (config.readOnly) {
|
|
2471
|
+
throw new Error("Server is running in --read-only mode; bot spot_grid cancel is disabled.");
|
|
2472
|
+
}
|
|
2473
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2474
|
+
const body = { buOrderId };
|
|
2475
|
+
if (args.closeSellModel != null) {
|
|
2476
|
+
const closeSellModel = asNonEmptyString3(args.closeSellModel, "closeSellModel");
|
|
2477
|
+
assertEnum3(closeSellModel, "closeSellModel", ["NOT_SELL", "TO_QUOTE", "TO_USDT"]);
|
|
2478
|
+
body.closeSellModel = closeSellModel;
|
|
2479
|
+
}
|
|
2480
|
+
if (args.slippage != null) body.slippage = String(args.slippage);
|
|
2481
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/cancel", body)).data;
|
|
2482
|
+
}
|
|
2483
|
+
},
|
|
2484
|
+
{
|
|
2485
|
+
name: "pionex_bot_spot_grid_profit",
|
|
2486
|
+
module: "bot",
|
|
2487
|
+
isWrite: true,
|
|
2488
|
+
description: "Extract accumulated grid profit from a spot grid bot order.",
|
|
2489
|
+
inputSchema: {
|
|
2490
|
+
type: "object",
|
|
2491
|
+
additionalProperties: false,
|
|
2492
|
+
properties: {
|
|
2493
|
+
buOrderId: { type: "string" },
|
|
2494
|
+
amount: { type: "string", description: "Amount of profit to extract" }
|
|
2495
|
+
},
|
|
2496
|
+
required: ["buOrderId", "amount"]
|
|
2497
|
+
},
|
|
2498
|
+
async handler(args, { client, config }) {
|
|
2499
|
+
if (config.readOnly) {
|
|
2500
|
+
throw new Error("Server is running in --read-only mode; bot spot_grid profit is disabled.");
|
|
2501
|
+
}
|
|
2502
|
+
const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
|
|
2503
|
+
const amount = asPositiveDecimalString2(args.amount, "amount");
|
|
2504
|
+
return (await client.signedPost("/api/v1/bot/orders/spotGrid/profit", { buOrderId, amount })).data;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
];
|
|
2508
|
+
}
|
|
2509
|
+
function registerEarnDualTools() {
|
|
2510
|
+
return [
|
|
2511
|
+
// ─── Public endpoints ────────────────────────────────────────────────────
|
|
2512
|
+
{
|
|
2513
|
+
name: "pionex_earn_dual_symbols",
|
|
2514
|
+
module: "earn_dual",
|
|
2515
|
+
isWrite: false,
|
|
2516
|
+
description: "List all trading pairs supported by Dual Investment, optionally filtered by base currency. Supported quote currencies: USDT, USDC, USD, USDXO. No authentication required.",
|
|
2517
|
+
inputSchema: {
|
|
2518
|
+
type: "object",
|
|
2519
|
+
additionalProperties: false,
|
|
2520
|
+
properties: {
|
|
2521
|
+
base: { type: "string", description: "Base currency filter (e.g. BTC, ETH). Omit to return all supported pairs." }
|
|
2522
|
+
}
|
|
2523
|
+
},
|
|
2524
|
+
async handler(args, { client }) {
|
|
2525
|
+
const base = args.base;
|
|
2526
|
+
return (await client.publicGet("/api/v1/earn/dual/symbols", base ? { base } : {})).data;
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
name: "pionex_earn_dual_open_products",
|
|
2531
|
+
module: "earn_dual",
|
|
2532
|
+
isWrite: false,
|
|
2533
|
+
description: "List currently open Dual Investment products for a specific trading pair and direction. DUAL_BASE: invest in base currency (e.g. BTC); DUAL_CURRENCY: invest in investment currency (e.g. USDT). Product ID format: {BASE}-{QUOTE}-{YYMMDD}-{STRIKE}-{C|P}-{CURRENCY}, where C=DUAL_BASE, P=DUAL_CURRENCY. For BTC/ETH use quote=USDXO with currency=USDT or USDC. For other bases use quote=USDT with currency=USDT. No authentication required.",
|
|
2534
|
+
inputSchema: {
|
|
2535
|
+
type: "object",
|
|
2536
|
+
additionalProperties: false,
|
|
2537
|
+
required: ["base", "quote", "type"],
|
|
2538
|
+
properties: {
|
|
2539
|
+
base: { type: "string", description: "Base currency (e.g. BTC, ETH, XRP)" },
|
|
2540
|
+
quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH; use USDT for all other base currencies." },
|
|
2541
|
+
type: { type: "string", enum: ["DUAL_BASE", "DUAL_CURRENCY"], description: "DUAL_BASE: invest in base currency (product ID suffix C); DUAL_CURRENCY: invest in investment currency (product ID suffix P)" },
|
|
2542
|
+
currency: { type: "string", description: "Investment currency filter. For BTC/ETH: USDT or USDC. For other pairs: USDT." }
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
async handler(args, { client }) {
|
|
2546
|
+
const base = args.base;
|
|
2547
|
+
const quote = args.quote;
|
|
2548
|
+
const type = args.type;
|
|
2549
|
+
const currency = args.currency;
|
|
2550
|
+
const query = { base, quote, type };
|
|
2551
|
+
if (currency) query.currency = currency;
|
|
2552
|
+
return (await client.publicGet("/api/v1/earn/dual/openProducts", query)).data;
|
|
2553
|
+
}
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
name: "pionex_earn_dual_prices",
|
|
2557
|
+
module: "earn_dual",
|
|
2558
|
+
isWrite: false,
|
|
2559
|
+
description: "Get latest yield rates and investability status for Dual Investment products. All three parameters (base, quote, productIds) are required. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. When canInvest is false, profit and baseSize will be empty strings. Always call this before placing an order \u2014 the profit value returned here must be passed unchanged to pionex_earn_dual_invest. No authentication required.",
|
|
2560
|
+
inputSchema: {
|
|
2561
|
+
type: "object",
|
|
2562
|
+
additionalProperties: false,
|
|
2563
|
+
required: ["base", "quote", "productIds"],
|
|
2564
|
+
properties: {
|
|
2565
|
+
base: { type: "string", description: "Base currency (e.g. BTC, ETH, LRC)" },
|
|
2566
|
+
quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." },
|
|
2567
|
+
productIds: {
|
|
2568
|
+
type: "array",
|
|
2569
|
+
items: { type: "string" },
|
|
2570
|
+
description: 'List of product IDs obtained from pionex_earn_dual_open_products (e.g. ["ETH-USDXO-260410-3000-C-USDT", "ETH-USDXO-260410-2900-C-USDT"]).'
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
},
|
|
2574
|
+
async handler(args, { client }) {
|
|
2575
|
+
const base = args.base;
|
|
2576
|
+
const quote = args.quote;
|
|
2577
|
+
const productIds = args.productIds;
|
|
2578
|
+
const query = { base, quote };
|
|
2579
|
+
if (productIds && productIds.length > 0) query.productIds = productIds.join(",");
|
|
2580
|
+
return (await client.publicGet("/api/v1/earn/dual/prices", query)).data;
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
name: "pionex_earn_dual_index",
|
|
2585
|
+
module: "earn_dual",
|
|
2586
|
+
isWrite: false,
|
|
2587
|
+
description: "Get real-time index price for a Dual Investment underlying asset. Both base and quote are required. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. The index price is the reference price used at settlement to determine payout direction. No authentication required.",
|
|
2588
|
+
inputSchema: {
|
|
2589
|
+
type: "object",
|
|
2590
|
+
additionalProperties: false,
|
|
2591
|
+
required: ["base", "quote"],
|
|
2592
|
+
properties: {
|
|
2593
|
+
base: { type: "string", description: "Base currency (e.g. BTC, ETH, LRC)" },
|
|
2594
|
+
quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." }
|
|
2595
|
+
}
|
|
2596
|
+
},
|
|
2597
|
+
async handler(args, { client }) {
|
|
2598
|
+
const base = args.base;
|
|
2599
|
+
const quote = args.quote;
|
|
2600
|
+
return (await client.publicGet("/api/v1/earn/dual/index", { base, quote })).data;
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
{
|
|
2604
|
+
name: "pionex_earn_dual_delivery_prices",
|
|
2605
|
+
module: "earn_dual",
|
|
2606
|
+
isWrite: false,
|
|
2607
|
+
description: "Get historical settlement delivery prices for a Dual Investment pair. base is required; quote is optional but recommended to narrow results. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. The delivery price is the index price recorded at expiry, used to determine the settlement direction. No authentication required.",
|
|
2608
|
+
inputSchema: {
|
|
2609
|
+
type: "object",
|
|
2610
|
+
additionalProperties: false,
|
|
2611
|
+
required: ["base"],
|
|
2612
|
+
properties: {
|
|
2613
|
+
base: { type: "string", description: "Base currency (e.g. BTC, XRP)" },
|
|
2614
|
+
quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency filter. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." },
|
|
2615
|
+
startTime: { type: "integer", description: "Start timestamp in milliseconds" },
|
|
2616
|
+
endTime: { type: "integer", description: "End timestamp in milliseconds" }
|
|
2617
|
+
}
|
|
2618
|
+
},
|
|
2619
|
+
async handler(args, { client }) {
|
|
2620
|
+
const base = args.base;
|
|
2621
|
+
const quote = args.quote;
|
|
2622
|
+
const startTime = args.startTime;
|
|
2623
|
+
const endTime = args.endTime;
|
|
2624
|
+
const query = { base };
|
|
2625
|
+
if (quote) query.quote = quote;
|
|
2626
|
+
if (startTime != null) query.startTime = String(startTime);
|
|
2627
|
+
if (endTime != null) query.endTime = String(endTime);
|
|
2628
|
+
return (await client.publicGet("/api/v1/earn/dual/deliveryPrices", query)).data;
|
|
2629
|
+
}
|
|
2630
|
+
},
|
|
2631
|
+
// ─── View endpoints (authentication required) ────────────────────────────
|
|
2632
|
+
{
|
|
2633
|
+
name: "pionex_earn_dual_balances",
|
|
2634
|
+
module: "earn_dual",
|
|
2635
|
+
isWrite: false,
|
|
2636
|
+
description: "Get authenticated user's Dual Investment account balances. Requires View permission (API key + secret).",
|
|
2637
|
+
inputSchema: {
|
|
2638
|
+
type: "object",
|
|
2639
|
+
additionalProperties: false,
|
|
2640
|
+
properties: {
|
|
2641
|
+
merge: { type: "boolean", description: "When true, merges balances with the same coin across different base currencies." }
|
|
2642
|
+
}
|
|
2643
|
+
},
|
|
2644
|
+
async handler(args, { client }) {
|
|
2645
|
+
const merge = args.merge;
|
|
2646
|
+
const query = {};
|
|
2647
|
+
if (merge != null) query.merge = String(merge);
|
|
2648
|
+
return (await client.signedGet("/api/v1/earn/dual/balances", query)).data;
|
|
2649
|
+
}
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
name: "pionex_earn_dual_get_invests",
|
|
2653
|
+
module: "earn_dual",
|
|
2654
|
+
isWrite: false,
|
|
2655
|
+
description: "Batch query Dual Investment orders by client order IDs. Requires View permission (API key + secret).",
|
|
2656
|
+
inputSchema: {
|
|
2657
|
+
type: "object",
|
|
2658
|
+
additionalProperties: false,
|
|
2659
|
+
properties: {
|
|
2660
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2661
|
+
clientDualIds: {
|
|
2662
|
+
type: "array",
|
|
2663
|
+
items: { type: "string" },
|
|
2664
|
+
description: "List of client-assigned dual investment order IDs to query."
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
},
|
|
2668
|
+
async handler(args, { client }) {
|
|
2669
|
+
const base = args.base;
|
|
2670
|
+
const clientDualIds = args.clientDualIds;
|
|
2671
|
+
const body = {};
|
|
2672
|
+
if (base) body.base = base;
|
|
2673
|
+
if (clientDualIds) body.clientDualIds = clientDualIds;
|
|
2674
|
+
return (await client.signedPost("/api/v1/earn/dual/invests", body)).data;
|
|
2675
|
+
}
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
name: "pionex_earn_dual_records",
|
|
2679
|
+
module: "earn_dual",
|
|
2680
|
+
isWrite: false,
|
|
2681
|
+
description: "Get paginated Dual Investment history for the authenticated user. Requires View permission (API key + secret).",
|
|
2682
|
+
inputSchema: {
|
|
2683
|
+
type: "object",
|
|
2684
|
+
additionalProperties: false,
|
|
2685
|
+
required: ["base", "endTime"],
|
|
2686
|
+
properties: {
|
|
2687
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2688
|
+
quote: { type: "string", description: "Quote currency filter. Use USDXO for BTC/ETH; use USDT for others." },
|
|
2689
|
+
currency: { type: "string", description: "Investment currency filter (e.g. USDT, BTC)" },
|
|
2690
|
+
filter: { type: "string", description: "Status filter" },
|
|
2691
|
+
startTime: { type: "integer", description: "Start timestamp in milliseconds" },
|
|
2692
|
+
endTime: { type: "integer", description: "End timestamp in milliseconds (required)" },
|
|
2693
|
+
limit: { type: "integer", description: "Maximum number of records per page (e.g. 20)" }
|
|
2694
|
+
}
|
|
2695
|
+
},
|
|
2696
|
+
async handler(args, { client }) {
|
|
2697
|
+
const base = args.base;
|
|
2698
|
+
const endTime = args.endTime;
|
|
2699
|
+
const quote = args.quote;
|
|
2700
|
+
const currency = args.currency;
|
|
2701
|
+
const filter = args.filter;
|
|
2702
|
+
const startTime = args.startTime;
|
|
2703
|
+
const limit = args.limit;
|
|
2704
|
+
const query = { base, endTime: String(endTime) };
|
|
2705
|
+
if (quote) query.quote = quote;
|
|
2706
|
+
if (currency) query.currency = currency;
|
|
2707
|
+
if (filter) query.filter = filter;
|
|
2708
|
+
if (startTime != null) query.startTime = String(startTime);
|
|
2709
|
+
if (limit != null) query.limit = String(limit);
|
|
2710
|
+
return (await client.signedGet("/api/v1/earn/dual/records", query)).data;
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
// ─── Earn/write endpoints (authentication required) ──────────────────────
|
|
2714
|
+
{
|
|
2715
|
+
name: "pionex_earn_dual_invest",
|
|
2716
|
+
module: "earn_dual",
|
|
2717
|
+
isWrite: true,
|
|
2718
|
+
description: "Create a new Dual Investment order. Requires Earn permission (API key + secret). Provide either baseAmount (invest in base currency) or currencyAmount (invest in investment currency), not both. profit must be obtained from pionex_earn_dual_prices and passed unchanged \u2014 a stale or mismatched value will be rejected. Product ID format: {BASE}-{QUOTE}-{YYMMDD}-{STRIKE}-{C|P}-{CURRENCY}, where C=DUAL_BASE, P=DUAL_CURRENCY.",
|
|
2719
|
+
inputSchema: {
|
|
2720
|
+
type: "object",
|
|
2721
|
+
additionalProperties: false,
|
|
2722
|
+
required: ["base"],
|
|
2723
|
+
properties: {
|
|
2724
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2725
|
+
productId: { type: "string", description: "Product ID to invest in (e.g. BTC-USDXO-260402-68000-P-USDT). Obtain from pionex_earn_dual_open_products." },
|
|
2726
|
+
clientDualId: { type: "string", description: "Client-assigned order ID used as an idempotency key. Recommended to avoid duplicate orders." },
|
|
2727
|
+
baseAmount: { type: "string", description: "Investment amount in base currency (e.g. '0.01'). Mutually exclusive with currencyAmount." },
|
|
2728
|
+
currencyAmount: { type: "string", description: "Investment amount in investment currency (e.g. '100'). Mutually exclusive with baseAmount." },
|
|
2729
|
+
profit: { type: "string", description: "Yield rate from pionex_earn_dual_prices (e.g. '0.0039'). Must be current \u2014 stale values are rejected." }
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
async handler(args, { client }) {
|
|
2733
|
+
const body = { base: args.base };
|
|
2734
|
+
if (args.productId) body.productId = args.productId;
|
|
2735
|
+
if (args.clientDualId) body.clientDualId = args.clientDualId;
|
|
2736
|
+
if (args.baseAmount) body.baseAmount = args.baseAmount;
|
|
2737
|
+
if (args.currencyAmount) body.currencyAmount = args.currencyAmount;
|
|
2738
|
+
if (args.profit) body.profit = args.profit;
|
|
2739
|
+
return (await client.signedPost("/api/v1/earn/dual/invest", body)).data;
|
|
2740
|
+
}
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
name: "pionex_earn_dual_revoke_invest",
|
|
2744
|
+
module: "earn_dual",
|
|
2745
|
+
isWrite: true,
|
|
2746
|
+
description: "Revoke a pending Dual Investment order before it is matched. Requires Earn permission (API key + secret). Parameters are sent as a JSON request body. Only orders in a pending/unmatched state can be revoked.",
|
|
2747
|
+
inputSchema: {
|
|
2748
|
+
type: "object",
|
|
2749
|
+
additionalProperties: false,
|
|
2750
|
+
required: ["base", "productId", "clientDualId"],
|
|
2751
|
+
properties: {
|
|
2752
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2753
|
+
clientDualId: { type: "string", description: "Client-assigned dual investment order ID" },
|
|
2754
|
+
productId: { type: "string", description: "Product ID of the order to revoke (e.g. BTC-USDXO-260402-68000-P-USDT)" }
|
|
2755
|
+
}
|
|
2756
|
+
},
|
|
2757
|
+
async handler(args, { client }) {
|
|
2758
|
+
const body = {
|
|
2759
|
+
base: args.base,
|
|
2760
|
+
productId: args.productId,
|
|
2761
|
+
clientDualId: args.clientDualId
|
|
2762
|
+
};
|
|
2763
|
+
return (await client.signedDelete("/api/v1/earn/dual/invest", body)).data;
|
|
2764
|
+
}
|
|
2765
|
+
},
|
|
2766
|
+
{
|
|
2767
|
+
name: "pionex_earn_dual_collect",
|
|
2768
|
+
module: "earn_dual",
|
|
2769
|
+
isWrite: true,
|
|
2770
|
+
description: "Collect settled Dual Investment earnings into the user's spot account. Requires Earn permission (API key + secret). Only orders in a settled state can be collected.",
|
|
2771
|
+
inputSchema: {
|
|
2772
|
+
type: "object",
|
|
2773
|
+
additionalProperties: false,
|
|
2774
|
+
required: ["base", "clientDualId", "productId"],
|
|
2775
|
+
properties: {
|
|
2776
|
+
base: { type: "string", description: "Base currency (e.g. BTC)" },
|
|
2777
|
+
clientDualId: { type: "string", description: "Client-assigned dual investment order ID to collect" },
|
|
2778
|
+
productId: { type: "string", description: "Product ID (e.g. BTC-USDXO-260402-68000-P-USDT)" }
|
|
2779
|
+
}
|
|
2780
|
+
},
|
|
2781
|
+
async handler(args, { client }) {
|
|
2782
|
+
const body = {
|
|
2783
|
+
base: args.base,
|
|
2784
|
+
clientDualId: args.clientDualId,
|
|
2785
|
+
productId: args.productId
|
|
2786
|
+
};
|
|
2787
|
+
return (await client.signedPost("/api/v1/earn/dual/collect", body)).data;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
];
|
|
2791
|
+
}
|
|
2792
|
+
function allToolSpecs() {
|
|
2793
|
+
return [...registerMarketTools(), ...registerAccountTools(), ...registerOrdersTools(), ...registerBotTools(), ...registerEarnDualTools()];
|
|
2794
|
+
}
|
|
2795
|
+
function createToolRunner(client, config) {
|
|
2796
|
+
const fullConfig = { ...config, modules: [...MODULES] };
|
|
2797
|
+
const toolMap = new Map(allToolSpecs().map((t) => [t.name, t]));
|
|
2798
|
+
return async (toolName, args) => {
|
|
2799
|
+
const tool = toolMap.get(toolName);
|
|
2800
|
+
if (!tool) throw new Error(`Unknown tool: ${toolName}`);
|
|
2801
|
+
const data = await tool.handler(args, { config: fullConfig, client });
|
|
2802
|
+
return { endpoint: toolName, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
// src/helpers.ts
|
|
2807
|
+
import { createRequire } from "module";
|
|
2808
|
+
var _require = createRequire(import.meta.url);
|
|
2809
|
+
var version = _require("../package.json").version;
|
|
2810
|
+
function resolveConfig(cmd) {
|
|
2811
|
+
const opts = cmd.optsWithGlobals();
|
|
2812
|
+
return loadConfig({
|
|
2813
|
+
profile: opts.profile,
|
|
2814
|
+
modules: opts.modules,
|
|
2815
|
+
baseUrl: opts.baseUrl,
|
|
2816
|
+
readOnly: opts.readOnly ?? false
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
function isDryRun(cmd) {
|
|
2820
|
+
return Boolean(cmd.optsWithGlobals().dryRun);
|
|
2821
|
+
}
|
|
2822
|
+
function print(data) {
|
|
2823
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
2824
|
+
}
|
|
2825
|
+
function parseJsonFlag(raw, flagName) {
|
|
2826
|
+
if (typeof raw !== "string") {
|
|
2827
|
+
throw new Error(`Missing required flag: --${flagName}`);
|
|
2828
|
+
}
|
|
2829
|
+
try {
|
|
2830
|
+
const parsed = JSON.parse(raw);
|
|
2831
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2832
|
+
throw new Error("must be a JSON object");
|
|
2833
|
+
}
|
|
2834
|
+
return parsed;
|
|
2835
|
+
} catch (e) {
|
|
2836
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2837
|
+
throw new Error(`Invalid --${flagName}: ${msg}`);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
function makeRunner(cmd) {
|
|
2841
|
+
const config = resolveConfig(cmd);
|
|
2842
|
+
const client = new PionexRestClient(config);
|
|
2843
|
+
return createToolRunner(client, config);
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
export {
|
|
2847
|
+
configFilePath,
|
|
2848
|
+
readFullConfig,
|
|
2849
|
+
writeFullConfig,
|
|
2850
|
+
SUPPORTED_CLIENTS,
|
|
2851
|
+
runSetup,
|
|
2852
|
+
toToolErrorPayload,
|
|
2853
|
+
parseAndValidateCreateFuturesGridBuOrderData,
|
|
2854
|
+
parseAndValidateCreateSpotGridBuOrderData,
|
|
2855
|
+
version,
|
|
2856
|
+
isDryRun,
|
|
2857
|
+
print,
|
|
2858
|
+
parseJsonFlag,
|
|
2859
|
+
makeRunner
|
|
2860
|
+
};
|
|
2861
|
+
/*! Bundled license information:
|
|
2862
|
+
|
|
2863
|
+
smol-toml/dist/error.js:
|
|
2864
|
+
smol-toml/dist/util.js:
|
|
2865
|
+
smol-toml/dist/date.js:
|
|
2866
|
+
smol-toml/dist/primitive.js:
|
|
2867
|
+
smol-toml/dist/extract.js:
|
|
2868
|
+
smol-toml/dist/struct.js:
|
|
2869
|
+
smol-toml/dist/parse.js:
|
|
2870
|
+
smol-toml/dist/stringify.js:
|
|
2871
|
+
smol-toml/dist/index.js:
|
|
2872
|
+
(*!
|
|
2873
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
2874
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
2875
|
+
*
|
|
2876
|
+
* Redistribution and use in source and binary forms, with or without
|
|
2877
|
+
* modification, are permitted provided that the following conditions are met:
|
|
2878
|
+
*
|
|
2879
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
2880
|
+
* list of conditions and the following disclaimer.
|
|
2881
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
2882
|
+
* this list of conditions and the following disclaimer in the
|
|
2883
|
+
* documentation and/or other materials provided with the distribution.
|
|
2884
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
2885
|
+
* may be used to endorse or promote products derived from this software without
|
|
2886
|
+
* specific prior written permission.
|
|
2887
|
+
*
|
|
2888
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
2889
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
2890
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
2891
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
2892
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
2893
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
2894
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
2895
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
2896
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
2897
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
2898
|
+
*)
|
|
2899
|
+
*/
|
|
2900
|
+
//# sourceMappingURL=chunk-NDTSX4HU.js.map
|