@okx_ai/okx-trade-cli 1.0.8
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/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/index.js +2580 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2580 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { parseArgs } from "util";
|
|
5
|
+
import { createRequire } from "module";
|
|
6
|
+
|
|
7
|
+
// ../core/dist/index.js
|
|
8
|
+
import { createHmac } from "crypto";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import os from "os";
|
|
11
|
+
import { readFileSync, existsSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
import { parse } from "smol-toml";
|
|
15
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
16
|
+
import { join as join2 } from "path";
|
|
17
|
+
import { homedir as homedir2 } from "os";
|
|
18
|
+
import * as fs3 from "fs";
|
|
19
|
+
import * as path3 from "path";
|
|
20
|
+
import * as os3 from "os";
|
|
21
|
+
import { execFileSync } from "child_process";
|
|
22
|
+
function getNow() {
|
|
23
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
24
|
+
}
|
|
25
|
+
function signOkxPayload(payload, secretKey) {
|
|
26
|
+
return createHmac("sha256", secretKey).update(payload).digest("base64");
|
|
27
|
+
}
|
|
28
|
+
var OkxMcpError = class extends Error {
|
|
29
|
+
type;
|
|
30
|
+
code;
|
|
31
|
+
suggestion;
|
|
32
|
+
endpoint;
|
|
33
|
+
traceId;
|
|
34
|
+
constructor(type, message, options) {
|
|
35
|
+
super(message, options?.cause ? { cause: options.cause } : void 0);
|
|
36
|
+
this.name = type;
|
|
37
|
+
this.type = type;
|
|
38
|
+
this.code = options?.code;
|
|
39
|
+
this.suggestion = options?.suggestion;
|
|
40
|
+
this.endpoint = options?.endpoint;
|
|
41
|
+
this.traceId = options?.traceId;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var ConfigError = class extends OkxMcpError {
|
|
45
|
+
constructor(message, suggestion) {
|
|
46
|
+
super("ConfigError", message, { suggestion });
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var RateLimitError = class extends OkxMcpError {
|
|
50
|
+
constructor(message, suggestion, endpoint, traceId) {
|
|
51
|
+
super("RateLimitError", message, { suggestion, endpoint, traceId });
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var AuthenticationError = class extends OkxMcpError {
|
|
55
|
+
constructor(message, suggestion, endpoint, traceId) {
|
|
56
|
+
super("AuthenticationError", message, { suggestion, endpoint, traceId });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var OkxApiError = class extends OkxMcpError {
|
|
60
|
+
constructor(message, options) {
|
|
61
|
+
super("OkxApiError", message, options);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var NetworkError = class extends OkxMcpError {
|
|
65
|
+
constructor(message, endpoint, cause) {
|
|
66
|
+
super("NetworkError", message, {
|
|
67
|
+
endpoint,
|
|
68
|
+
cause,
|
|
69
|
+
suggestion: "Please check network connectivity and retry the request in a few seconds."
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
function toToolErrorPayload(error, fallbackEndpoint) {
|
|
74
|
+
if (error instanceof OkxMcpError) {
|
|
75
|
+
return {
|
|
76
|
+
error: true,
|
|
77
|
+
type: error.type,
|
|
78
|
+
code: error.code,
|
|
79
|
+
message: error.message,
|
|
80
|
+
suggestion: error.suggestion,
|
|
81
|
+
endpoint: error.endpoint ?? fallbackEndpoint,
|
|
82
|
+
traceId: error.traceId,
|
|
83
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
+
return {
|
|
88
|
+
error: true,
|
|
89
|
+
type: "InternalError",
|
|
90
|
+
message,
|
|
91
|
+
suggestion: "Unexpected server error. Check tool arguments and retry. If it persists, inspect server logs.",
|
|
92
|
+
endpoint: fallbackEndpoint,
|
|
93
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function sleep(ms) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
setTimeout(resolve, ms);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
var RateLimiter = class {
|
|
102
|
+
buckets = /* @__PURE__ */ new Map();
|
|
103
|
+
maxWaitMs;
|
|
104
|
+
constructor(maxWaitMs = 3e4) {
|
|
105
|
+
this.maxWaitMs = maxWaitMs;
|
|
106
|
+
}
|
|
107
|
+
async consume(config, amount = 1) {
|
|
108
|
+
const bucket = this.getBucket(config);
|
|
109
|
+
this.refill(bucket);
|
|
110
|
+
if (bucket.tokens >= amount) {
|
|
111
|
+
bucket.tokens -= amount;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const missing = amount - bucket.tokens;
|
|
115
|
+
const secondsToWait = missing / bucket.refillPerSecond;
|
|
116
|
+
const waitMs = Math.ceil(secondsToWait * 1e3);
|
|
117
|
+
if (waitMs > this.maxWaitMs) {
|
|
118
|
+
throw new RateLimitError(
|
|
119
|
+
`Client-side rate limit reached for ${config.key}. Required wait ${waitMs}ms exceeds allowed max ${this.maxWaitMs}ms.`,
|
|
120
|
+
"Reduce tool call frequency or retry later."
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
await sleep(waitMs);
|
|
124
|
+
this.refill(bucket);
|
|
125
|
+
if (bucket.tokens < amount) {
|
|
126
|
+
throw new RateLimitError(
|
|
127
|
+
`Rate limiter failed to acquire enough tokens for ${config.key}.`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
bucket.tokens -= amount;
|
|
131
|
+
}
|
|
132
|
+
getBucket(config) {
|
|
133
|
+
const existing = this.buckets.get(config.key);
|
|
134
|
+
if (existing) {
|
|
135
|
+
if (existing.capacity !== config.capacity || existing.refillPerSecond !== config.refillPerSecond) {
|
|
136
|
+
existing.capacity = config.capacity;
|
|
137
|
+
existing.refillPerSecond = config.refillPerSecond;
|
|
138
|
+
existing.tokens = Math.min(existing.tokens, config.capacity);
|
|
139
|
+
}
|
|
140
|
+
return existing;
|
|
141
|
+
}
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const created = {
|
|
144
|
+
tokens: config.capacity,
|
|
145
|
+
lastRefillMs: now,
|
|
146
|
+
capacity: config.capacity,
|
|
147
|
+
refillPerSecond: config.refillPerSecond
|
|
148
|
+
};
|
|
149
|
+
this.buckets.set(config.key, created);
|
|
150
|
+
return created;
|
|
151
|
+
}
|
|
152
|
+
refill(bucket) {
|
|
153
|
+
const now = Date.now();
|
|
154
|
+
const elapsedMs = now - bucket.lastRefillMs;
|
|
155
|
+
if (elapsedMs <= 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const refillTokens = elapsedMs / 1e3 * bucket.refillPerSecond;
|
|
159
|
+
bucket.tokens = Math.min(bucket.capacity, bucket.tokens + refillTokens);
|
|
160
|
+
bucket.lastRefillMs = now;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
var OKX_CODE_BEHAVIORS = {
|
|
164
|
+
// Rate limit → throw RateLimitError
|
|
165
|
+
"50011": { retry: true, suggestion: "Rate limited. Back off and retry after a delay." },
|
|
166
|
+
"50061": { retry: true, suggestion: "Too many connections. Reduce request frequency and retry." },
|
|
167
|
+
// Server temporarily unavailable → retryable
|
|
168
|
+
"50001": { retry: true, suggestion: "OKX system upgrade in progress. Retry in a few minutes." },
|
|
169
|
+
"50004": { retry: true, suggestion: "Endpoint temporarily unavailable. Retry later." },
|
|
170
|
+
"50013": { retry: true, suggestion: "System busy. Retry after 1-2 seconds." },
|
|
171
|
+
"50026": { retry: true, suggestion: "Order book system upgrading. Retry in a few minutes." },
|
|
172
|
+
// Region / compliance restriction → do not retry
|
|
173
|
+
"51155": { retry: false, suggestion: "Feature unavailable in your region (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
|
|
174
|
+
"51734": { retry: false, suggestion: "Feature not supported for your KYC country (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
|
|
175
|
+
// Account issues → do not retry
|
|
176
|
+
"50007": { retry: false, suggestion: "Account suspended. Contact OKX support. Do not retry." },
|
|
177
|
+
"50009": { retry: false, suggestion: "Account blocked by risk control. Contact OKX support. Do not retry." },
|
|
178
|
+
"51009": { retry: false, suggestion: "Account mode not supported for this operation. Check account settings." },
|
|
179
|
+
// API key permission / expiry → do not retry
|
|
180
|
+
"50100": { retry: false, suggestion: "API key lacks required permissions. Update API key permissions." },
|
|
181
|
+
"50110": { retry: false, suggestion: "API key expired. Generate a new API key." },
|
|
182
|
+
// Insufficient funds / margin → do not retry
|
|
183
|
+
"51008": { retry: false, suggestion: "Insufficient balance. Top up account before retrying." },
|
|
184
|
+
"51119": { retry: false, suggestion: "Insufficient margin. Add margin before retrying." },
|
|
185
|
+
"51127": { retry: false, suggestion: "Insufficient available margin. Reduce position or add margin." },
|
|
186
|
+
// Instrument unavailable → do not retry
|
|
187
|
+
"51021": { retry: false, suggestion: "Instrument does not exist. Check instId." },
|
|
188
|
+
"51022": { retry: false, suggestion: "Instrument not available for trading." },
|
|
189
|
+
"51027": { retry: false, suggestion: "Contract has expired." }
|
|
190
|
+
};
|
|
191
|
+
function isDefined(value) {
|
|
192
|
+
return value !== void 0 && value !== null;
|
|
193
|
+
}
|
|
194
|
+
function extractTraceId(headers) {
|
|
195
|
+
return headers.get("x-trace-id") ?? headers.get("x-request-id") ?? headers.get("traceid") ?? void 0;
|
|
196
|
+
}
|
|
197
|
+
function stringifyQueryValue(value) {
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
return value.map((item) => String(item)).join(",");
|
|
200
|
+
}
|
|
201
|
+
return String(value);
|
|
202
|
+
}
|
|
203
|
+
function buildQueryString(query) {
|
|
204
|
+
if (!query) {
|
|
205
|
+
return "";
|
|
206
|
+
}
|
|
207
|
+
const entries = Object.entries(query).filter(([, value]) => isDefined(value));
|
|
208
|
+
if (entries.length === 0) {
|
|
209
|
+
return "";
|
|
210
|
+
}
|
|
211
|
+
const params = new URLSearchParams();
|
|
212
|
+
for (const [key, value] of entries) {
|
|
213
|
+
params.set(key, stringifyQueryValue(value));
|
|
214
|
+
}
|
|
215
|
+
return params.toString();
|
|
216
|
+
}
|
|
217
|
+
var OkxRestClient = class {
|
|
218
|
+
config;
|
|
219
|
+
rateLimiter = new RateLimiter();
|
|
220
|
+
constructor(config) {
|
|
221
|
+
this.config = config;
|
|
222
|
+
}
|
|
223
|
+
async publicGet(path4, query, rateLimit) {
|
|
224
|
+
return this.request({
|
|
225
|
+
method: "GET",
|
|
226
|
+
path: path4,
|
|
227
|
+
auth: "public",
|
|
228
|
+
query,
|
|
229
|
+
rateLimit
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async privateGet(path4, query, rateLimit) {
|
|
233
|
+
return this.request({
|
|
234
|
+
method: "GET",
|
|
235
|
+
path: path4,
|
|
236
|
+
auth: "private",
|
|
237
|
+
query,
|
|
238
|
+
rateLimit
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
async privatePost(path4, body, rateLimit) {
|
|
242
|
+
return this.request({
|
|
243
|
+
method: "POST",
|
|
244
|
+
path: path4,
|
|
245
|
+
auth: "private",
|
|
246
|
+
body,
|
|
247
|
+
rateLimit
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
async request(config) {
|
|
251
|
+
const queryString = buildQueryString(config.query);
|
|
252
|
+
const requestPath = queryString.length > 0 ? `${config.path}?${queryString}` : config.path;
|
|
253
|
+
const url = `${this.config.baseUrl}${requestPath}`;
|
|
254
|
+
const bodyJson = config.body ? JSON.stringify(config.body) : "";
|
|
255
|
+
const timestamp = getNow();
|
|
256
|
+
if (config.rateLimit) {
|
|
257
|
+
await this.rateLimiter.consume(config.rateLimit);
|
|
258
|
+
}
|
|
259
|
+
const headers = new Headers({
|
|
260
|
+
"Content-Type": "application/json",
|
|
261
|
+
Accept: "application/json"
|
|
262
|
+
});
|
|
263
|
+
if (this.config.userAgent) {
|
|
264
|
+
headers.set("User-Agent", this.config.userAgent);
|
|
265
|
+
}
|
|
266
|
+
if (config.auth === "private") {
|
|
267
|
+
if (!this.config.hasAuth) {
|
|
268
|
+
throw new ConfigError(
|
|
269
|
+
"Private endpoint requires API credentials.",
|
|
270
|
+
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
274
|
+
throw new ConfigError(
|
|
275
|
+
"Invalid private API credentials state.",
|
|
276
|
+
"Ensure all OKX credentials are set."
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
const payload = `${timestamp}${config.method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
280
|
+
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
281
|
+
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
282
|
+
headers.set("OK-ACCESS-SIGN", signature);
|
|
283
|
+
headers.set("OK-ACCESS-PASSPHRASE", this.config.passphrase);
|
|
284
|
+
headers.set("OK-ACCESS-TIMESTAMP", timestamp);
|
|
285
|
+
}
|
|
286
|
+
if (this.config.demo) {
|
|
287
|
+
headers.set("x-simulated-trading", "1");
|
|
288
|
+
}
|
|
289
|
+
let response;
|
|
290
|
+
try {
|
|
291
|
+
response = await fetch(url, {
|
|
292
|
+
method: config.method,
|
|
293
|
+
headers,
|
|
294
|
+
body: config.method === "POST" ? bodyJson : void 0,
|
|
295
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
throw new NetworkError(
|
|
299
|
+
`Failed to call OKX endpoint ${config.method} ${requestPath}.`,
|
|
300
|
+
`${config.method} ${requestPath}`,
|
|
301
|
+
error
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
const rawText = await response.text();
|
|
305
|
+
const traceId = extractTraceId(response.headers);
|
|
306
|
+
let parsed;
|
|
307
|
+
try {
|
|
308
|
+
parsed = rawText ? JSON.parse(rawText) : {};
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (!response.ok) {
|
|
311
|
+
const messagePreview = rawText.slice(0, 160).replace(/\s+/g, " ").trim();
|
|
312
|
+
throw new OkxApiError(
|
|
313
|
+
`HTTP ${response.status} from OKX: ${messagePreview || "Non-JSON response body"}`,
|
|
314
|
+
{
|
|
315
|
+
code: String(response.status),
|
|
316
|
+
endpoint: `${config.method} ${config.path}`,
|
|
317
|
+
suggestion: "Verify endpoint path and request parameters.",
|
|
318
|
+
traceId
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
throw new NetworkError(
|
|
323
|
+
`OKX returned non-JSON response for ${config.method} ${requestPath}.`,
|
|
324
|
+
`${config.method} ${requestPath}`,
|
|
325
|
+
error
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
if (!response.ok) {
|
|
329
|
+
throw new OkxApiError(
|
|
330
|
+
`HTTP ${response.status} from OKX: ${parsed.msg ?? "Unknown error"}`,
|
|
331
|
+
{
|
|
332
|
+
code: String(response.status),
|
|
333
|
+
endpoint: `${config.method} ${config.path}`,
|
|
334
|
+
suggestion: "Retry later or verify endpoint parameters.",
|
|
335
|
+
traceId
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const responseCode = parsed.code;
|
|
340
|
+
if (responseCode && responseCode !== "0") {
|
|
341
|
+
const message = parsed.msg ?? "OKX API request failed.";
|
|
342
|
+
const endpoint = `${config.method} ${config.path}`;
|
|
343
|
+
if (responseCode === "50111" || responseCode === "50112" || responseCode === "50113") {
|
|
344
|
+
throw new AuthenticationError(
|
|
345
|
+
message,
|
|
346
|
+
"Check API key, secret, passphrase and permissions.",
|
|
347
|
+
endpoint,
|
|
348
|
+
traceId
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
const behavior = OKX_CODE_BEHAVIORS[responseCode];
|
|
352
|
+
const suggestion = behavior?.suggestion?.replace("{site}", this.config.site);
|
|
353
|
+
if (responseCode === "50011" || responseCode === "50061") {
|
|
354
|
+
throw new RateLimitError(message, suggestion, endpoint, traceId);
|
|
355
|
+
}
|
|
356
|
+
throw new OkxApiError(message, {
|
|
357
|
+
code: responseCode,
|
|
358
|
+
endpoint,
|
|
359
|
+
suggestion,
|
|
360
|
+
traceId
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
endpoint: `${config.method} ${config.path}`,
|
|
365
|
+
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
366
|
+
data: parsed.data ?? null,
|
|
367
|
+
raw: parsed
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
var DEFAULT_LOG_DIR = path.join(os.homedir(), ".okx", "logs");
|
|
372
|
+
var OKX_SITES = {
|
|
373
|
+
global: {
|
|
374
|
+
label: "Global",
|
|
375
|
+
apiBaseUrl: "https://www.okx.com",
|
|
376
|
+
webUrl: "https://www.okx.com"
|
|
377
|
+
},
|
|
378
|
+
eea: {
|
|
379
|
+
label: "EEA",
|
|
380
|
+
apiBaseUrl: "https://eea.okx.com",
|
|
381
|
+
webUrl: "https://my.okx.com"
|
|
382
|
+
},
|
|
383
|
+
us: {
|
|
384
|
+
label: "US",
|
|
385
|
+
apiBaseUrl: "https://app.okx.com",
|
|
386
|
+
webUrl: "https://app.okx.com"
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var SITE_IDS = Object.keys(OKX_SITES);
|
|
390
|
+
var MODULES = [
|
|
391
|
+
"market",
|
|
392
|
+
"spot",
|
|
393
|
+
"swap",
|
|
394
|
+
"futures",
|
|
395
|
+
"option",
|
|
396
|
+
"account",
|
|
397
|
+
"bot"
|
|
398
|
+
];
|
|
399
|
+
var DEFAULT_MODULES = ["spot", "swap", "account"];
|
|
400
|
+
function configFilePath() {
|
|
401
|
+
return join(homedir(), ".okx", "config.toml");
|
|
402
|
+
}
|
|
403
|
+
function readTomlProfile(profileName) {
|
|
404
|
+
const path4 = configFilePath();
|
|
405
|
+
if (!existsSync(path4)) return {};
|
|
406
|
+
const raw = readFileSync(path4, "utf-8");
|
|
407
|
+
const config = parse(raw);
|
|
408
|
+
const name = profileName ?? config.default_profile ?? "default";
|
|
409
|
+
return config.profiles?.[name] ?? {};
|
|
410
|
+
}
|
|
411
|
+
function parseModuleList(rawModules) {
|
|
412
|
+
if (!rawModules || rawModules.trim().length === 0) {
|
|
413
|
+
return [...DEFAULT_MODULES];
|
|
414
|
+
}
|
|
415
|
+
const trimmed = rawModules.trim().toLowerCase();
|
|
416
|
+
if (trimmed === "all") {
|
|
417
|
+
return [...MODULES];
|
|
418
|
+
}
|
|
419
|
+
const requested = trimmed.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
420
|
+
if (requested.length === 0) {
|
|
421
|
+
return [...DEFAULT_MODULES];
|
|
422
|
+
}
|
|
423
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
424
|
+
for (const moduleId of requested) {
|
|
425
|
+
if (!MODULES.includes(moduleId)) {
|
|
426
|
+
throw new ConfigError(
|
|
427
|
+
`Unknown module "${moduleId}".`,
|
|
428
|
+
`Use one of: ${MODULES.join(", ")} or "all".`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
deduped.add(moduleId);
|
|
432
|
+
}
|
|
433
|
+
return Array.from(deduped);
|
|
434
|
+
}
|
|
435
|
+
function loadConfig(cli) {
|
|
436
|
+
const toml = readTomlProfile(cli.profile);
|
|
437
|
+
const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
|
|
438
|
+
const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
439
|
+
const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
|
|
440
|
+
const hasAuth = Boolean(apiKey && secretKey && passphrase);
|
|
441
|
+
const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
|
|
442
|
+
if (partialAuth && !hasAuth) {
|
|
443
|
+
throw new ConfigError(
|
|
444
|
+
"Partial API credentials detected.",
|
|
445
|
+
"Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
const demo = cli.demo || process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
449
|
+
const rawSite = cli.site?.trim() ?? process.env.OKX_SITE?.trim() ?? toml.site ?? "global";
|
|
450
|
+
if (!SITE_IDS.includes(rawSite)) {
|
|
451
|
+
throw new ConfigError(
|
|
452
|
+
`Unknown site "${rawSite}".`,
|
|
453
|
+
`Use one of: ${SITE_IDS.join(", ")}.`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
const site = rawSite;
|
|
457
|
+
const rawBaseUrl = process.env.OKX_API_BASE_URL?.trim() ?? toml.base_url ?? OKX_SITES[site].apiBaseUrl;
|
|
458
|
+
if (!rawBaseUrl.startsWith("http://") && !rawBaseUrl.startsWith("https://")) {
|
|
459
|
+
throw new ConfigError(
|
|
460
|
+
`Invalid base URL "${rawBaseUrl}".`,
|
|
461
|
+
"OKX_API_BASE_URL must start with http:// or https://"
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
465
|
+
const rawTimeout = process.env.OKX_TIMEOUT_MS ? Number(process.env.OKX_TIMEOUT_MS) : toml.timeout_ms ?? 15e3;
|
|
466
|
+
if (!Number.isFinite(rawTimeout) || rawTimeout <= 0) {
|
|
467
|
+
throw new ConfigError(
|
|
468
|
+
`Invalid timeout value "${rawTimeout}".`,
|
|
469
|
+
"Set OKX_TIMEOUT_MS as a positive integer in milliseconds."
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
apiKey,
|
|
474
|
+
secretKey,
|
|
475
|
+
passphrase,
|
|
476
|
+
hasAuth,
|
|
477
|
+
baseUrl,
|
|
478
|
+
timeoutMs: Math.floor(rawTimeout),
|
|
479
|
+
modules: parseModuleList(cli.modules),
|
|
480
|
+
readOnly: cli.readOnly,
|
|
481
|
+
demo,
|
|
482
|
+
site,
|
|
483
|
+
userAgent: cli.userAgent
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
var CACHE_FILE = join2(homedir2(), ".okx", "update-check.json");
|
|
487
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
488
|
+
function readCache() {
|
|
489
|
+
try {
|
|
490
|
+
if (existsSync2(CACHE_FILE)) {
|
|
491
|
+
return JSON.parse(readFileSync2(CACHE_FILE, "utf-8"));
|
|
492
|
+
}
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
return {};
|
|
496
|
+
}
|
|
497
|
+
function writeCache(cache) {
|
|
498
|
+
try {
|
|
499
|
+
mkdirSync(join2(homedir2(), ".okx"), { recursive: true });
|
|
500
|
+
writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function isNewerVersion(current, latest) {
|
|
505
|
+
const parse22 = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10));
|
|
506
|
+
const [cMaj, cMin, cPat] = parse22(current);
|
|
507
|
+
const [lMaj, lMin, lPat] = parse22(latest);
|
|
508
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
509
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
510
|
+
return lPat > cPat;
|
|
511
|
+
}
|
|
512
|
+
async function fetchLatestVersion(packageName) {
|
|
513
|
+
try {
|
|
514
|
+
const controller = new AbortController();
|
|
515
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
516
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
|
|
517
|
+
signal: controller.signal,
|
|
518
|
+
headers: { accept: "application/json" }
|
|
519
|
+
});
|
|
520
|
+
clearTimeout(timeout);
|
|
521
|
+
if (!res.ok) return null;
|
|
522
|
+
const data = await res.json();
|
|
523
|
+
return data.version ?? null;
|
|
524
|
+
} catch {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function refreshCacheInBackground(packageName) {
|
|
529
|
+
fetchLatestVersion(packageName).then((latest) => {
|
|
530
|
+
if (!latest) return;
|
|
531
|
+
const cache = readCache();
|
|
532
|
+
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
533
|
+
writeCache(cache);
|
|
534
|
+
}).catch(() => {
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
function checkForUpdates(packageName, currentVersion) {
|
|
538
|
+
const cache = readCache();
|
|
539
|
+
const entry = cache[packageName];
|
|
540
|
+
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
541
|
+
process.stderr.write(
|
|
542
|
+
`
|
|
543
|
+
Update available for ${packageName}: ${currentVersion} \u2192 ${entry.latestVersion}
|
|
544
|
+
Run: npm install -g ${packageName}
|
|
545
|
+
|
|
546
|
+
`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
if (!entry || Date.now() - entry.checkedAt > CHECK_INTERVAL_MS) {
|
|
550
|
+
refreshCacheInBackground(packageName);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
var CLIENT_NAMES = {
|
|
554
|
+
"claude-desktop": "Claude Desktop",
|
|
555
|
+
cursor: "Cursor",
|
|
556
|
+
windsurf: "Windsurf",
|
|
557
|
+
vscode: "VS Code",
|
|
558
|
+
"claude-code": "Claude Code CLI"
|
|
559
|
+
};
|
|
560
|
+
var SUPPORTED_CLIENTS = Object.keys(CLIENT_NAMES);
|
|
561
|
+
function appData() {
|
|
562
|
+
return process.env.APPDATA ?? path3.join(os3.homedir(), "AppData", "Roaming");
|
|
563
|
+
}
|
|
564
|
+
function getConfigPath(client) {
|
|
565
|
+
const home = os3.homedir();
|
|
566
|
+
const win = process.platform === "win32";
|
|
567
|
+
switch (client) {
|
|
568
|
+
case "claude-desktop":
|
|
569
|
+
return win ? path3.join(appData(), "Claude", "claude_desktop_config.json") : path3.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
570
|
+
case "cursor":
|
|
571
|
+
return path3.join(home, ".cursor", "mcp.json");
|
|
572
|
+
case "windsurf":
|
|
573
|
+
return path3.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
574
|
+
case "vscode":
|
|
575
|
+
return path3.join(process.cwd(), ".mcp.json");
|
|
576
|
+
case "claude-code":
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function buildEntry(client, args) {
|
|
581
|
+
if (client === "vscode") {
|
|
582
|
+
return { type: "stdio", command: "okx-trade-mcp", args };
|
|
583
|
+
}
|
|
584
|
+
return { command: "okx-trade-mcp", args };
|
|
585
|
+
}
|
|
586
|
+
function buildArgs(options) {
|
|
587
|
+
const args = [];
|
|
588
|
+
if (options.profile) args.push("--profile", options.profile);
|
|
589
|
+
args.push("--modules", options.modules ?? "all");
|
|
590
|
+
return args;
|
|
591
|
+
}
|
|
592
|
+
function mergeJsonConfig(configPath, serverName, entry) {
|
|
593
|
+
const dir = path3.dirname(configPath);
|
|
594
|
+
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
595
|
+
let data = {};
|
|
596
|
+
if (fs3.existsSync(configPath)) {
|
|
597
|
+
const raw = fs3.readFileSync(configPath, "utf-8");
|
|
598
|
+
try {
|
|
599
|
+
data = JSON.parse(raw);
|
|
600
|
+
} catch {
|
|
601
|
+
throw new Error(`Failed to parse existing config at ${configPath}`);
|
|
602
|
+
}
|
|
603
|
+
const backupPath = configPath + ".bak";
|
|
604
|
+
fs3.copyFileSync(configPath, backupPath);
|
|
605
|
+
process.stdout.write(` Backup \u2192 ${backupPath}
|
|
606
|
+
`);
|
|
607
|
+
}
|
|
608
|
+
if (typeof data.mcpServers !== "object" || data.mcpServers === null) {
|
|
609
|
+
data.mcpServers = {};
|
|
610
|
+
}
|
|
611
|
+
data.mcpServers[serverName] = entry;
|
|
612
|
+
fs3.writeFileSync(configPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
613
|
+
}
|
|
614
|
+
function printSetupUsage() {
|
|
615
|
+
process.stdout.write(
|
|
616
|
+
`Usage: okx-trade-mcp setup --client <client> [--profile <name>] [--modules <list>]
|
|
617
|
+
|
|
618
|
+
Clients:
|
|
619
|
+
` + SUPPORTED_CLIENTS.map((id) => ` ${id.padEnd(16)} ${CLIENT_NAMES[id]}`).join("\n") + `
|
|
620
|
+
|
|
621
|
+
Options:
|
|
622
|
+
--profile <name> Profile from ~/.okx/config.toml (default: uses default_profile)
|
|
623
|
+
--modules <list> Comma-separated modules or "all" (default: all)
|
|
624
|
+
`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
function runSetup(options) {
|
|
628
|
+
const { client } = options;
|
|
629
|
+
const name = CLIENT_NAMES[client];
|
|
630
|
+
const args = buildArgs(options);
|
|
631
|
+
const serverName = options.profile ? `okx-trade-mcp-${options.profile}` : "okx-trade-mcp";
|
|
632
|
+
if (client === "claude-code") {
|
|
633
|
+
const claudeArgs = [
|
|
634
|
+
"mcp",
|
|
635
|
+
"add",
|
|
636
|
+
"--transport",
|
|
637
|
+
"stdio",
|
|
638
|
+
serverName,
|
|
639
|
+
"--",
|
|
640
|
+
"okx-trade-mcp",
|
|
641
|
+
...args
|
|
642
|
+
];
|
|
643
|
+
process.stdout.write(`Running: claude ${claudeArgs.join(" ")}
|
|
644
|
+
`);
|
|
645
|
+
execFileSync("claude", claudeArgs, { stdio: "inherit" });
|
|
646
|
+
process.stdout.write(`\u2713 Configured ${name}
|
|
647
|
+
`);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const configPath = getConfigPath(client);
|
|
651
|
+
if (!configPath) {
|
|
652
|
+
throw new Error(`${name} is not supported on this platform`);
|
|
653
|
+
}
|
|
654
|
+
const entry = buildEntry(client, args);
|
|
655
|
+
mergeJsonConfig(configPath, serverName, entry);
|
|
656
|
+
process.stdout.write(
|
|
657
|
+
`\u2713 Configured ${name}
|
|
658
|
+
${configPath}
|
|
659
|
+
Server args: ${args.join(" ")}
|
|
660
|
+
`
|
|
661
|
+
);
|
|
662
|
+
if (client !== "vscode") {
|
|
663
|
+
process.stdout.write(` Restart ${name} to apply changes.
|
|
664
|
+
`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// src/config/loader.ts
|
|
669
|
+
function loadProfileConfig(opts) {
|
|
670
|
+
return loadConfig({
|
|
671
|
+
profile: opts.profile,
|
|
672
|
+
modules: opts.modules,
|
|
673
|
+
readOnly: opts.readOnly ?? false,
|
|
674
|
+
demo: opts.demo ?? false,
|
|
675
|
+
site: opts.site,
|
|
676
|
+
userAgent: opts.userAgent
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/formatter.ts
|
|
681
|
+
function printJson(data) {
|
|
682
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
683
|
+
}
|
|
684
|
+
function printTable(rows) {
|
|
685
|
+
if (rows.length === 0) {
|
|
686
|
+
process.stdout.write("(no data)\n");
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const keys = Object.keys(rows[0]);
|
|
690
|
+
const widths = keys.map(
|
|
691
|
+
(k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
|
|
692
|
+
);
|
|
693
|
+
const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
|
|
694
|
+
const divider = widths.map((w) => "-".repeat(w)).join(" ");
|
|
695
|
+
process.stdout.write(header + "\n" + divider + "\n");
|
|
696
|
+
for (const row of rows) {
|
|
697
|
+
process.stdout.write(
|
|
698
|
+
keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" ") + "\n"
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function printKv(obj, indent = 0) {
|
|
703
|
+
const pad = " ".repeat(indent);
|
|
704
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
705
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
706
|
+
process.stdout.write(`${pad}${k}:
|
|
707
|
+
`);
|
|
708
|
+
printKv(v, indent + 2);
|
|
709
|
+
} else {
|
|
710
|
+
process.stdout.write(`${pad}${k.padEnd(20 - indent)} ${v}
|
|
711
|
+
`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/commands/market.ts
|
|
717
|
+
async function cmdMarketInstruments(client, opts) {
|
|
718
|
+
const params = { instType: opts.instType };
|
|
719
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
720
|
+
const res = await client.publicGet("/api/v5/public/instruments", params);
|
|
721
|
+
const items = res.data;
|
|
722
|
+
if (opts.json) return printJson(items);
|
|
723
|
+
printTable(
|
|
724
|
+
(items ?? []).slice(0, 50).map((t) => ({
|
|
725
|
+
instId: t["instId"],
|
|
726
|
+
ctVal: t["ctVal"],
|
|
727
|
+
lotSz: t["lotSz"],
|
|
728
|
+
minSz: t["minSz"],
|
|
729
|
+
tickSz: t["tickSz"],
|
|
730
|
+
state: t["state"]
|
|
731
|
+
}))
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
async function cmdMarketFundingRate(client, instId, opts) {
|
|
735
|
+
if (opts.history) {
|
|
736
|
+
const params = { instId };
|
|
737
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
738
|
+
const res = await client.publicGet("/api/v5/public/funding-rate-history", params);
|
|
739
|
+
const items = res.data;
|
|
740
|
+
if (opts.json) return printJson(items);
|
|
741
|
+
printTable(
|
|
742
|
+
(items ?? []).map((r) => ({
|
|
743
|
+
instId: r["instId"],
|
|
744
|
+
fundingRate: r["fundingRate"],
|
|
745
|
+
realizedRate: r["realizedRate"],
|
|
746
|
+
fundingTime: new Date(Number(r["fundingTime"])).toLocaleString()
|
|
747
|
+
}))
|
|
748
|
+
);
|
|
749
|
+
} else {
|
|
750
|
+
const res = await client.publicGet("/api/v5/public/funding-rate", { instId });
|
|
751
|
+
const items = res.data;
|
|
752
|
+
if (opts.json) return printJson(items);
|
|
753
|
+
const r = items?.[0];
|
|
754
|
+
if (!r) {
|
|
755
|
+
process.stdout.write("No data\n");
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
printKv({
|
|
759
|
+
instId: r["instId"],
|
|
760
|
+
fundingRate: r["fundingRate"],
|
|
761
|
+
nextFundingRate: r["nextFundingRate"],
|
|
762
|
+
fundingTime: new Date(Number(r["fundingTime"])).toLocaleString(),
|
|
763
|
+
nextFundingTime: new Date(Number(r["nextFundingTime"])).toLocaleString()
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async function cmdMarketMarkPrice(client, opts) {
|
|
768
|
+
const params = { instType: opts.instType };
|
|
769
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
770
|
+
const res = await client.publicGet("/api/v5/public/mark-price", params);
|
|
771
|
+
const items = res.data;
|
|
772
|
+
if (opts.json) return printJson(items);
|
|
773
|
+
printTable(
|
|
774
|
+
(items ?? []).map((r) => ({
|
|
775
|
+
instId: r["instId"],
|
|
776
|
+
instType: r["instType"],
|
|
777
|
+
markPx: r["markPx"],
|
|
778
|
+
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
779
|
+
}))
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
async function cmdMarketTrades(client, instId, opts) {
|
|
783
|
+
const params = { instId };
|
|
784
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
785
|
+
const res = await client.publicGet("/api/v5/market/trades", params);
|
|
786
|
+
const items = res.data;
|
|
787
|
+
if (opts.json) return printJson(items);
|
|
788
|
+
printTable(
|
|
789
|
+
(items ?? []).map((t) => ({
|
|
790
|
+
tradeId: t["tradeId"],
|
|
791
|
+
px: t["px"],
|
|
792
|
+
sz: t["sz"],
|
|
793
|
+
side: t["side"],
|
|
794
|
+
ts: new Date(Number(t["ts"])).toLocaleString()
|
|
795
|
+
}))
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
async function cmdMarketIndexTicker(client, opts) {
|
|
799
|
+
const params = {};
|
|
800
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
801
|
+
if (opts.quoteCcy) params["quoteCcy"] = opts.quoteCcy;
|
|
802
|
+
const res = await client.publicGet("/api/v5/market/index-tickers", params);
|
|
803
|
+
const items = res.data;
|
|
804
|
+
if (opts.json) return printJson(items);
|
|
805
|
+
printTable(
|
|
806
|
+
(items ?? []).map((t) => ({
|
|
807
|
+
instId: t["instId"],
|
|
808
|
+
idxPx: t["idxPx"],
|
|
809
|
+
high24h: t["high24h"],
|
|
810
|
+
low24h: t["low24h"],
|
|
811
|
+
ts: new Date(Number(t["ts"])).toLocaleString()
|
|
812
|
+
}))
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
async function cmdMarketIndexCandles(client, instId, opts) {
|
|
816
|
+
const path2 = opts.history ? "/api/v5/market/history-index-candles" : "/api/v5/market/index-candles";
|
|
817
|
+
const params = { instId };
|
|
818
|
+
if (opts.bar) params["bar"] = opts.bar;
|
|
819
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
820
|
+
const res = await client.publicGet(path2, params);
|
|
821
|
+
const candles = res.data;
|
|
822
|
+
if (opts.json) return printJson(candles);
|
|
823
|
+
printTable(
|
|
824
|
+
(candles ?? []).map(([ts, o, h, l, c]) => ({
|
|
825
|
+
time: new Date(Number(ts)).toLocaleString(),
|
|
826
|
+
open: o,
|
|
827
|
+
high: h,
|
|
828
|
+
low: l,
|
|
829
|
+
close: c
|
|
830
|
+
}))
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
async function cmdMarketPriceLimit(client, instId, json) {
|
|
834
|
+
const res = await client.publicGet("/api/v5/public/price-limit", { instId });
|
|
835
|
+
const items = res.data;
|
|
836
|
+
if (json) return printJson(items);
|
|
837
|
+
const r = items?.[0];
|
|
838
|
+
if (!r) {
|
|
839
|
+
process.stdout.write("No data\n");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
printKv({
|
|
843
|
+
instId: r["instId"],
|
|
844
|
+
buyLmt: r["buyLmt"],
|
|
845
|
+
sellLmt: r["sellLmt"],
|
|
846
|
+
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
async function cmdMarketOpenInterest(client, opts) {
|
|
850
|
+
const params = { instType: opts.instType };
|
|
851
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
852
|
+
const res = await client.publicGet("/api/v5/public/open-interest", params);
|
|
853
|
+
const items = res.data;
|
|
854
|
+
if (opts.json) return printJson(items);
|
|
855
|
+
printTable(
|
|
856
|
+
(items ?? []).map((r) => ({
|
|
857
|
+
instId: r["instId"],
|
|
858
|
+
oi: r["oi"],
|
|
859
|
+
oiCcy: r["oiCcy"],
|
|
860
|
+
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
861
|
+
}))
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
async function cmdMarketTicker(client, instId, json) {
|
|
865
|
+
const res = await client.publicGet("/api/v5/market/ticker", { instId });
|
|
866
|
+
const items = res.data;
|
|
867
|
+
if (json) return printJson(items);
|
|
868
|
+
if (!items?.length) {
|
|
869
|
+
process.stdout.write("No data\n");
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const t = items[0];
|
|
873
|
+
printKv({
|
|
874
|
+
instId: t["instId"],
|
|
875
|
+
last: t["last"],
|
|
876
|
+
"24h change %": t["sodUtc8"],
|
|
877
|
+
"24h high": t["high24h"],
|
|
878
|
+
"24h low": t["low24h"],
|
|
879
|
+
"24h vol": t["vol24h"],
|
|
880
|
+
time: new Date(Number(t["ts"])).toLocaleString()
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
async function cmdMarketTickers(client, instType, json) {
|
|
884
|
+
const res = await client.publicGet("/api/v5/market/tickers", { instType });
|
|
885
|
+
const items = res.data;
|
|
886
|
+
if (json) return printJson(items);
|
|
887
|
+
printTable(
|
|
888
|
+
(items ?? []).map((t) => ({
|
|
889
|
+
instId: t["instId"],
|
|
890
|
+
last: t["last"],
|
|
891
|
+
"24h high": t["high24h"],
|
|
892
|
+
"24h low": t["low24h"],
|
|
893
|
+
"24h vol": t["vol24h"]
|
|
894
|
+
}))
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
async function cmdMarketOrderbook(client, instId, sz, json) {
|
|
898
|
+
const params = { instId };
|
|
899
|
+
if (sz !== void 0) params["sz"] = String(sz);
|
|
900
|
+
const res = await client.publicGet("/api/v5/market/books", params);
|
|
901
|
+
if (json) return printJson(res.data);
|
|
902
|
+
const book = res.data[0];
|
|
903
|
+
if (!book) {
|
|
904
|
+
process.stdout.write("No data\n");
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
const asks = book["asks"].slice(0, 5);
|
|
908
|
+
const bids = book["bids"].slice(0, 5);
|
|
909
|
+
process.stdout.write("Asks (price / size):\n");
|
|
910
|
+
for (const [p, s] of asks.reverse()) process.stdout.write(` ${p.padStart(16)} ${s}
|
|
911
|
+
`);
|
|
912
|
+
process.stdout.write("Bids (price / size):\n");
|
|
913
|
+
for (const [p, s] of bids) process.stdout.write(` ${p.padStart(16)} ${s}
|
|
914
|
+
`);
|
|
915
|
+
}
|
|
916
|
+
async function cmdMarketCandles(client, instId, opts) {
|
|
917
|
+
const params = { instId };
|
|
918
|
+
if (opts.bar) params["bar"] = opts.bar;
|
|
919
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
920
|
+
const res = await client.publicGet("/api/v5/market/candles", params);
|
|
921
|
+
const candles = res.data;
|
|
922
|
+
if (opts.json) return printJson(candles);
|
|
923
|
+
printTable(
|
|
924
|
+
(candles ?? []).map(([ts, o, h, l, c, vol]) => ({
|
|
925
|
+
time: new Date(Number(ts)).toLocaleString(),
|
|
926
|
+
open: o,
|
|
927
|
+
high: h,
|
|
928
|
+
low: l,
|
|
929
|
+
close: c,
|
|
930
|
+
vol
|
|
931
|
+
}))
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// src/commands/account.ts
|
|
936
|
+
async function cmdAccountBalance(client, ccy, json) {
|
|
937
|
+
const params = {};
|
|
938
|
+
if (ccy) params["ccy"] = ccy;
|
|
939
|
+
const res = await client.privateGet("/api/v5/account/balance", params);
|
|
940
|
+
const data = res.data;
|
|
941
|
+
if (json) return printJson(data);
|
|
942
|
+
const details = data?.[0]?.["details"] ?? [];
|
|
943
|
+
printTable(
|
|
944
|
+
details.filter((d) => Number(d["eq"]) > 0).map((d) => ({
|
|
945
|
+
currency: d["ccy"],
|
|
946
|
+
equity: d["eq"],
|
|
947
|
+
available: d["availEq"],
|
|
948
|
+
frozen: d["frozenBal"]
|
|
949
|
+
}))
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
async function cmdAccountAssetBalance(client, ccy, json) {
|
|
953
|
+
const params = {};
|
|
954
|
+
if (ccy) params["ccy"] = ccy;
|
|
955
|
+
const res = await client.privateGet("/api/v5/asset/balances", params);
|
|
956
|
+
const data = res.data;
|
|
957
|
+
if (json) return printJson(data);
|
|
958
|
+
printTable(
|
|
959
|
+
(data ?? []).filter((r) => Number(r["bal"]) > 0).map((r) => ({
|
|
960
|
+
ccy: r["ccy"],
|
|
961
|
+
bal: r["bal"],
|
|
962
|
+
availBal: r["availBal"],
|
|
963
|
+
frozenBal: r["frozenBal"]
|
|
964
|
+
}))
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
async function cmdAccountPositions(client, opts) {
|
|
968
|
+
const params = {};
|
|
969
|
+
if (opts.instType) params["instType"] = opts.instType;
|
|
970
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
971
|
+
const res = await client.privateGet("/api/v5/account/positions", params);
|
|
972
|
+
const positions = res.data;
|
|
973
|
+
if (opts.json) return printJson(positions);
|
|
974
|
+
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
975
|
+
if (!open.length) {
|
|
976
|
+
process.stdout.write("No open positions\n");
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
printTable(
|
|
980
|
+
open.map((p) => ({
|
|
981
|
+
instId: p["instId"],
|
|
982
|
+
instType: p["instType"],
|
|
983
|
+
side: p["posSide"],
|
|
984
|
+
pos: p["pos"],
|
|
985
|
+
avgPx: p["avgPx"],
|
|
986
|
+
upl: p["upl"],
|
|
987
|
+
lever: p["lever"]
|
|
988
|
+
}))
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
async function cmdAccountBills(client, opts) {
|
|
992
|
+
const endpoint = opts.archive ? "/api/v5/account/bills-archive" : "/api/v5/account/bills";
|
|
993
|
+
const params = {};
|
|
994
|
+
if (opts.instType) params["instType"] = opts.instType;
|
|
995
|
+
if (opts.ccy) params["ccy"] = opts.ccy;
|
|
996
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
997
|
+
const res = await client.privateGet(endpoint, params);
|
|
998
|
+
const bills = res.data;
|
|
999
|
+
if (opts.json) return printJson(bills);
|
|
1000
|
+
printTable(
|
|
1001
|
+
(bills ?? []).map((b) => ({
|
|
1002
|
+
billId: b["billId"],
|
|
1003
|
+
instId: b["instId"],
|
|
1004
|
+
type: b["type"],
|
|
1005
|
+
ccy: b["ccy"],
|
|
1006
|
+
balChg: b["balChg"],
|
|
1007
|
+
bal: b["bal"],
|
|
1008
|
+
ts: new Date(Number(b["ts"])).toLocaleString()
|
|
1009
|
+
}))
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
async function cmdAccountFees(client, opts) {
|
|
1013
|
+
const params = { instType: opts.instType };
|
|
1014
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1015
|
+
const res = await client.privateGet("/api/v5/account/trade-fee", params);
|
|
1016
|
+
const data = res.data;
|
|
1017
|
+
if (opts.json) return printJson(data);
|
|
1018
|
+
const fee = data?.[0];
|
|
1019
|
+
if (!fee) {
|
|
1020
|
+
process.stdout.write("No data\n");
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
printKv({
|
|
1024
|
+
level: fee["level"],
|
|
1025
|
+
maker: fee["maker"],
|
|
1026
|
+
taker: fee["taker"],
|
|
1027
|
+
makerU: fee["makerU"],
|
|
1028
|
+
takerU: fee["takerU"],
|
|
1029
|
+
ts: new Date(Number(fee["ts"])).toLocaleString()
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
async function cmdAccountConfig(client, json) {
|
|
1033
|
+
const res = await client.privateGet("/api/v5/account/config", {});
|
|
1034
|
+
const data = res.data;
|
|
1035
|
+
if (json) return printJson(data);
|
|
1036
|
+
const cfg = data?.[0];
|
|
1037
|
+
if (!cfg) {
|
|
1038
|
+
process.stdout.write("No data\n");
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
printKv({
|
|
1042
|
+
uid: cfg["uid"],
|
|
1043
|
+
acctLv: cfg["acctLv"],
|
|
1044
|
+
posMode: cfg["posMode"],
|
|
1045
|
+
autoLoan: cfg["autoLoan"],
|
|
1046
|
+
greeksType: cfg["greeksType"],
|
|
1047
|
+
level: cfg["level"],
|
|
1048
|
+
levelTmp: cfg["levelTmp"]
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
async function cmdAccountSetPositionMode(client, posMode, json) {
|
|
1052
|
+
const res = await client.privatePost("/api/v5/account/set-position-mode", { posMode });
|
|
1053
|
+
if (json) return printJson(res.data);
|
|
1054
|
+
const r = res.data[0];
|
|
1055
|
+
process.stdout.write(`Position mode set: ${r?.["posMode"]}
|
|
1056
|
+
`);
|
|
1057
|
+
}
|
|
1058
|
+
async function cmdAccountMaxSize(client, opts) {
|
|
1059
|
+
const params = { instId: opts.instId, tdMode: opts.tdMode };
|
|
1060
|
+
if (opts.px) params["px"] = opts.px;
|
|
1061
|
+
const res = await client.privateGet("/api/v5/account/max-size", params);
|
|
1062
|
+
const data = res.data;
|
|
1063
|
+
if (opts.json) return printJson(data);
|
|
1064
|
+
const r = data?.[0];
|
|
1065
|
+
if (!r) {
|
|
1066
|
+
process.stdout.write("No data\n");
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
printKv({ instId: r["instId"], maxBuy: r["maxBuy"], maxSell: r["maxSell"] });
|
|
1070
|
+
}
|
|
1071
|
+
async function cmdAccountMaxAvailSize(client, opts) {
|
|
1072
|
+
const res = await client.privateGet("/api/v5/account/max-avail-size", {
|
|
1073
|
+
instId: opts.instId,
|
|
1074
|
+
tdMode: opts.tdMode
|
|
1075
|
+
});
|
|
1076
|
+
const data = res.data;
|
|
1077
|
+
if (opts.json) return printJson(data);
|
|
1078
|
+
const r = data?.[0];
|
|
1079
|
+
if (!r) {
|
|
1080
|
+
process.stdout.write("No data\n");
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
printKv({ instId: r["instId"], availBuy: r["availBuy"], availSell: r["availSell"] });
|
|
1084
|
+
}
|
|
1085
|
+
async function cmdAccountMaxWithdrawal(client, ccy, json) {
|
|
1086
|
+
const params = {};
|
|
1087
|
+
if (ccy) params["ccy"] = ccy;
|
|
1088
|
+
const res = await client.privateGet("/api/v5/account/max-withdrawal", params);
|
|
1089
|
+
const data = res.data;
|
|
1090
|
+
if (json) return printJson(data);
|
|
1091
|
+
printTable(
|
|
1092
|
+
(data ?? []).map((r) => ({
|
|
1093
|
+
ccy: r["ccy"],
|
|
1094
|
+
maxWd: r["maxWd"],
|
|
1095
|
+
maxWdEx: r["maxWdEx"]
|
|
1096
|
+
}))
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
async function cmdAccountPositionsHistory(client, opts) {
|
|
1100
|
+
const params = {};
|
|
1101
|
+
if (opts.instType) params["instType"] = opts.instType;
|
|
1102
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1103
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
1104
|
+
const res = await client.privateGet("/api/v5/account/positions-history", params);
|
|
1105
|
+
const data = res.data;
|
|
1106
|
+
if (opts.json) return printJson(data);
|
|
1107
|
+
printTable(
|
|
1108
|
+
(data ?? []).map((p) => ({
|
|
1109
|
+
instId: p["instId"],
|
|
1110
|
+
direction: p["direction"],
|
|
1111
|
+
openAvgPx: p["openAvgPx"],
|
|
1112
|
+
closeAvgPx: p["closeAvgPx"],
|
|
1113
|
+
realizedPnl: p["realizedPnl"],
|
|
1114
|
+
uTime: new Date(Number(p["uTime"])).toLocaleString()
|
|
1115
|
+
}))
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
async function cmdAccountTransfer(client, opts) {
|
|
1119
|
+
const body = {
|
|
1120
|
+
ccy: opts.ccy,
|
|
1121
|
+
amt: opts.amt,
|
|
1122
|
+
from: opts.from,
|
|
1123
|
+
to: opts.to
|
|
1124
|
+
};
|
|
1125
|
+
if (opts.transferType) body["type"] = opts.transferType;
|
|
1126
|
+
if (opts.subAcct) body["subAcct"] = opts.subAcct;
|
|
1127
|
+
const res = await client.privatePost("/api/v5/asset/transfer", body);
|
|
1128
|
+
if (opts.json) return printJson(res.data);
|
|
1129
|
+
const r = res.data[0];
|
|
1130
|
+
process.stdout.write(`Transfer: ${r?.["transId"]} (${r?.["ccy"]} ${r?.["amt"]})
|
|
1131
|
+
`);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// src/commands/spot.ts
|
|
1135
|
+
async function cmdSpotOrders(client, opts) {
|
|
1136
|
+
const endpoint = opts.status === "history" ? "/api/v5/trade/orders-history" : "/api/v5/trade/orders-pending";
|
|
1137
|
+
const params = { instType: "SPOT" };
|
|
1138
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1139
|
+
const res = await client.privateGet(endpoint, params);
|
|
1140
|
+
const orders = res.data;
|
|
1141
|
+
if (opts.json) return printJson(orders);
|
|
1142
|
+
printTable(
|
|
1143
|
+
(orders ?? []).map((o) => ({
|
|
1144
|
+
ordId: o["ordId"],
|
|
1145
|
+
instId: o["instId"],
|
|
1146
|
+
side: o["side"],
|
|
1147
|
+
type: o["ordType"],
|
|
1148
|
+
price: o["px"],
|
|
1149
|
+
size: o["sz"],
|
|
1150
|
+
filled: o["fillSz"],
|
|
1151
|
+
state: o["state"]
|
|
1152
|
+
}))
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
async function cmdSpotPlace(client, opts) {
|
|
1156
|
+
const body = {
|
|
1157
|
+
instId: opts.instId,
|
|
1158
|
+
tdMode: "cash",
|
|
1159
|
+
side: opts.side,
|
|
1160
|
+
ordType: opts.ordType,
|
|
1161
|
+
sz: opts.sz
|
|
1162
|
+
};
|
|
1163
|
+
if (opts.px) body["px"] = opts.px;
|
|
1164
|
+
const res = await client.privatePost("/api/v5/trade/order", body);
|
|
1165
|
+
if (opts.json) return printJson(res.data);
|
|
1166
|
+
const order = res.data[0];
|
|
1167
|
+
process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1168
|
+
`);
|
|
1169
|
+
}
|
|
1170
|
+
async function cmdSpotCancel(client, instId, ordId, json) {
|
|
1171
|
+
const res = await client.privatePost("/api/v5/trade/cancel-order", { instId, ordId });
|
|
1172
|
+
if (json) return printJson(res.data);
|
|
1173
|
+
const r = res.data[0];
|
|
1174
|
+
process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1175
|
+
`);
|
|
1176
|
+
}
|
|
1177
|
+
async function cmdSpotAlgoPlace(client, opts) {
|
|
1178
|
+
const body = {
|
|
1179
|
+
instId: opts.instId,
|
|
1180
|
+
tdMode: "cash",
|
|
1181
|
+
side: opts.side,
|
|
1182
|
+
ordType: opts.ordType,
|
|
1183
|
+
sz: opts.sz
|
|
1184
|
+
};
|
|
1185
|
+
if (opts.tpTriggerPx) body["tpTriggerPx"] = opts.tpTriggerPx;
|
|
1186
|
+
if (opts.tpOrdPx) body["tpOrdPx"] = opts.tpOrdPx;
|
|
1187
|
+
if (opts.slTriggerPx) body["slTriggerPx"] = opts.slTriggerPx;
|
|
1188
|
+
if (opts.slOrdPx) body["slOrdPx"] = opts.slOrdPx;
|
|
1189
|
+
const res = await client.privatePost("/api/v5/trade/order-algo", body);
|
|
1190
|
+
if (opts.json) return printJson(res.data);
|
|
1191
|
+
const order = res.data[0];
|
|
1192
|
+
process.stdout.write(
|
|
1193
|
+
`Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1194
|
+
`
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
async function cmdSpotAlgoAmend(client, opts) {
|
|
1198
|
+
const body = {
|
|
1199
|
+
instId: opts.instId,
|
|
1200
|
+
algoId: opts.algoId
|
|
1201
|
+
};
|
|
1202
|
+
if (opts.newSz) body["newSz"] = opts.newSz;
|
|
1203
|
+
if (opts.newTpTriggerPx) body["newTpTriggerPx"] = opts.newTpTriggerPx;
|
|
1204
|
+
if (opts.newTpOrdPx) body["newTpOrdPx"] = opts.newTpOrdPx;
|
|
1205
|
+
if (opts.newSlTriggerPx) body["newSlTriggerPx"] = opts.newSlTriggerPx;
|
|
1206
|
+
if (opts.newSlOrdPx) body["newSlOrdPx"] = opts.newSlOrdPx;
|
|
1207
|
+
const res = await client.privatePost("/api/v5/trade/amend-algos", body);
|
|
1208
|
+
if (opts.json) return printJson(res.data);
|
|
1209
|
+
const r = res.data[0];
|
|
1210
|
+
process.stdout.write(
|
|
1211
|
+
`Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1212
|
+
`
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
async function cmdSpotAlgoCancel(client, instId, algoId, json) {
|
|
1216
|
+
const res = await client.privatePost("/api/v5/trade/cancel-algos", [
|
|
1217
|
+
{ algoId, instId }
|
|
1218
|
+
]);
|
|
1219
|
+
if (json) return printJson(res.data);
|
|
1220
|
+
const r = res.data[0];
|
|
1221
|
+
process.stdout.write(
|
|
1222
|
+
`Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1223
|
+
`
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
async function cmdSpotGet(client, opts) {
|
|
1227
|
+
const params = { instId: opts.instId };
|
|
1228
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1229
|
+
if (opts.clOrdId) params["clOrdId"] = opts.clOrdId;
|
|
1230
|
+
const res = await client.privateGet("/api/v5/trade/order", params);
|
|
1231
|
+
const data = res.data;
|
|
1232
|
+
if (opts.json) return printJson(data);
|
|
1233
|
+
const o = data?.[0];
|
|
1234
|
+
if (!o) {
|
|
1235
|
+
process.stdout.write("No data\n");
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
printKv({
|
|
1239
|
+
ordId: o["ordId"],
|
|
1240
|
+
instId: o["instId"],
|
|
1241
|
+
side: o["side"],
|
|
1242
|
+
ordType: o["ordType"],
|
|
1243
|
+
px: o["px"],
|
|
1244
|
+
sz: o["sz"],
|
|
1245
|
+
fillSz: o["fillSz"],
|
|
1246
|
+
avgPx: o["avgPx"],
|
|
1247
|
+
state: o["state"],
|
|
1248
|
+
cTime: new Date(Number(o["cTime"])).toLocaleString()
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
async function cmdSpotAmend(client, opts) {
|
|
1252
|
+
const body = { instId: opts.instId };
|
|
1253
|
+
if (opts.ordId) body["ordId"] = opts.ordId;
|
|
1254
|
+
if (opts.clOrdId) body["clOrdId"] = opts.clOrdId;
|
|
1255
|
+
if (opts.newSz) body["newSz"] = opts.newSz;
|
|
1256
|
+
if (opts.newPx) body["newPx"] = opts.newPx;
|
|
1257
|
+
const res = await client.privatePost("/api/v5/trade/amend-order", body);
|
|
1258
|
+
if (opts.json) return printJson(res.data);
|
|
1259
|
+
const r = res.data[0];
|
|
1260
|
+
process.stdout.write(`Order amended: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1261
|
+
`);
|
|
1262
|
+
}
|
|
1263
|
+
async function cmdSpotAlgoOrders(client, opts) {
|
|
1264
|
+
const endpoint = opts.status === "history" ? "/api/v5/trade/orders-algo-history" : "/api/v5/trade/orders-algo-pending";
|
|
1265
|
+
const baseParams = { instType: "SPOT" };
|
|
1266
|
+
if (opts.instId) baseParams["instId"] = opts.instId;
|
|
1267
|
+
let orders;
|
|
1268
|
+
if (opts.ordType) {
|
|
1269
|
+
const res = await client.privateGet(endpoint, { ...baseParams, ordType: opts.ordType });
|
|
1270
|
+
orders = res.data ?? [];
|
|
1271
|
+
} else {
|
|
1272
|
+
const [r1, r2] = await Promise.all([
|
|
1273
|
+
client.privateGet(endpoint, { ...baseParams, ordType: "conditional" }),
|
|
1274
|
+
client.privateGet(endpoint, { ...baseParams, ordType: "oco" })
|
|
1275
|
+
]);
|
|
1276
|
+
orders = [
|
|
1277
|
+
...r1.data ?? [],
|
|
1278
|
+
...r2.data ?? []
|
|
1279
|
+
];
|
|
1280
|
+
}
|
|
1281
|
+
if (opts.json) return printJson(orders);
|
|
1282
|
+
if (!(orders ?? []).length) {
|
|
1283
|
+
process.stdout.write("No algo orders\n");
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
printTable(
|
|
1287
|
+
orders.map((o) => ({
|
|
1288
|
+
algoId: o["algoId"],
|
|
1289
|
+
instId: o["instId"],
|
|
1290
|
+
type: o["ordType"],
|
|
1291
|
+
side: o["side"],
|
|
1292
|
+
sz: o["sz"],
|
|
1293
|
+
tpTrigger: o["tpTriggerPx"],
|
|
1294
|
+
slTrigger: o["slTriggerPx"],
|
|
1295
|
+
state: o["state"]
|
|
1296
|
+
}))
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
async function cmdSpotFills(client, opts) {
|
|
1300
|
+
const params = { instType: "SPOT" };
|
|
1301
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1302
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1303
|
+
const res = await client.privateGet("/api/v5/trade/fills", params);
|
|
1304
|
+
const fills = res.data;
|
|
1305
|
+
if (opts.json) return printJson(fills);
|
|
1306
|
+
printTable(
|
|
1307
|
+
(fills ?? []).map((f) => ({
|
|
1308
|
+
instId: f["instId"],
|
|
1309
|
+
side: f["side"],
|
|
1310
|
+
fillPx: f["fillPx"],
|
|
1311
|
+
fillSz: f["fillSz"],
|
|
1312
|
+
fee: f["fee"],
|
|
1313
|
+
ts: new Date(Number(f["ts"])).toLocaleString()
|
|
1314
|
+
}))
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// src/commands/swap.ts
|
|
1319
|
+
async function cmdSwapPositions(client, instId, json) {
|
|
1320
|
+
const params = { instType: "SWAP" };
|
|
1321
|
+
if (instId) params["instId"] = instId;
|
|
1322
|
+
const res = await client.privateGet("/api/v5/account/positions", params);
|
|
1323
|
+
const positions = res.data;
|
|
1324
|
+
if (json) return printJson(positions);
|
|
1325
|
+
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
1326
|
+
if (!open.length) {
|
|
1327
|
+
process.stdout.write("No open positions\n");
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
printTable(
|
|
1331
|
+
open.map((p) => ({
|
|
1332
|
+
instId: p["instId"],
|
|
1333
|
+
side: p["posSide"],
|
|
1334
|
+
size: p["pos"],
|
|
1335
|
+
avgPx: p["avgPx"],
|
|
1336
|
+
upl: p["upl"],
|
|
1337
|
+
uplRatio: p["uplRatio"],
|
|
1338
|
+
lever: p["lever"]
|
|
1339
|
+
}))
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
async function cmdSwapOrders(client, opts) {
|
|
1343
|
+
const endpoint = opts.status === "history" ? "/api/v5/trade/orders-history" : "/api/v5/trade/orders-pending";
|
|
1344
|
+
const params = { instType: "SWAP" };
|
|
1345
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1346
|
+
const res = await client.privateGet(endpoint, params);
|
|
1347
|
+
const orders = res.data;
|
|
1348
|
+
if (opts.json) return printJson(orders);
|
|
1349
|
+
printTable(
|
|
1350
|
+
(orders ?? []).map((o) => ({
|
|
1351
|
+
ordId: o["ordId"],
|
|
1352
|
+
instId: o["instId"],
|
|
1353
|
+
side: o["side"],
|
|
1354
|
+
posSide: o["posSide"],
|
|
1355
|
+
type: o["ordType"],
|
|
1356
|
+
price: o["px"],
|
|
1357
|
+
size: o["sz"],
|
|
1358
|
+
state: o["state"]
|
|
1359
|
+
}))
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
async function cmdSwapPlace(client, opts) {
|
|
1363
|
+
const body = {
|
|
1364
|
+
instId: opts.instId,
|
|
1365
|
+
tdMode: opts.tdMode,
|
|
1366
|
+
side: opts.side,
|
|
1367
|
+
ordType: opts.ordType,
|
|
1368
|
+
sz: opts.sz
|
|
1369
|
+
};
|
|
1370
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1371
|
+
if (opts.px) body["px"] = opts.px;
|
|
1372
|
+
const res = await client.privatePost("/api/v5/trade/order", body);
|
|
1373
|
+
if (opts.json) return printJson(res.data);
|
|
1374
|
+
const order = res.data[0];
|
|
1375
|
+
process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1376
|
+
`);
|
|
1377
|
+
}
|
|
1378
|
+
async function cmdSwapCancel(client, instId, ordId, json) {
|
|
1379
|
+
const res = await client.privatePost("/api/v5/trade/cancel-order", { instId, ordId });
|
|
1380
|
+
if (json) return printJson(res.data);
|
|
1381
|
+
const r = res.data[0];
|
|
1382
|
+
process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1383
|
+
`);
|
|
1384
|
+
}
|
|
1385
|
+
async function cmdSwapAlgoPlace(client, opts) {
|
|
1386
|
+
const body = {
|
|
1387
|
+
instId: opts.instId,
|
|
1388
|
+
tdMode: opts.tdMode,
|
|
1389
|
+
side: opts.side,
|
|
1390
|
+
ordType: opts.ordType,
|
|
1391
|
+
sz: opts.sz
|
|
1392
|
+
};
|
|
1393
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1394
|
+
if (opts.tpTriggerPx) body["tpTriggerPx"] = opts.tpTriggerPx;
|
|
1395
|
+
if (opts.tpOrdPx) body["tpOrdPx"] = opts.tpOrdPx;
|
|
1396
|
+
if (opts.slTriggerPx) body["slTriggerPx"] = opts.slTriggerPx;
|
|
1397
|
+
if (opts.slOrdPx) body["slOrdPx"] = opts.slOrdPx;
|
|
1398
|
+
if (opts.reduceOnly !== void 0) body["reduceOnly"] = String(opts.reduceOnly);
|
|
1399
|
+
const res = await client.privatePost("/api/v5/trade/order-algo", body);
|
|
1400
|
+
if (opts.json) return printJson(res.data);
|
|
1401
|
+
const order = res.data[0];
|
|
1402
|
+
process.stdout.write(
|
|
1403
|
+
`Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1404
|
+
`
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
async function cmdSwapAlgoAmend(client, opts) {
|
|
1408
|
+
const body = {
|
|
1409
|
+
instId: opts.instId,
|
|
1410
|
+
algoId: opts.algoId
|
|
1411
|
+
};
|
|
1412
|
+
if (opts.newSz) body["newSz"] = opts.newSz;
|
|
1413
|
+
if (opts.newTpTriggerPx) body["newTpTriggerPx"] = opts.newTpTriggerPx;
|
|
1414
|
+
if (opts.newTpOrdPx) body["newTpOrdPx"] = opts.newTpOrdPx;
|
|
1415
|
+
if (opts.newSlTriggerPx) body["newSlTriggerPx"] = opts.newSlTriggerPx;
|
|
1416
|
+
if (opts.newSlOrdPx) body["newSlOrdPx"] = opts.newSlOrdPx;
|
|
1417
|
+
const res = await client.privatePost("/api/v5/trade/amend-algos", body);
|
|
1418
|
+
if (opts.json) return printJson(res.data);
|
|
1419
|
+
const r = res.data[0];
|
|
1420
|
+
process.stdout.write(
|
|
1421
|
+
`Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1422
|
+
`
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
async function cmdSwapAlgoTrailPlace(client, opts) {
|
|
1426
|
+
const body = {
|
|
1427
|
+
instId: opts.instId,
|
|
1428
|
+
tdMode: opts.tdMode,
|
|
1429
|
+
side: opts.side,
|
|
1430
|
+
ordType: "move_order_stop",
|
|
1431
|
+
sz: opts.sz
|
|
1432
|
+
};
|
|
1433
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1434
|
+
if (opts.callbackRatio) body["callbackRatio"] = opts.callbackRatio;
|
|
1435
|
+
if (opts.callbackSpread) body["callbackSpread"] = opts.callbackSpread;
|
|
1436
|
+
if (opts.activePx) body["activePx"] = opts.activePx;
|
|
1437
|
+
if (opts.reduceOnly !== void 0) body["reduceOnly"] = String(opts.reduceOnly);
|
|
1438
|
+
const res = await client.privatePost("/api/v5/trade/order-algo", body);
|
|
1439
|
+
if (opts.json) return printJson(res.data);
|
|
1440
|
+
const order = res.data[0];
|
|
1441
|
+
process.stdout.write(
|
|
1442
|
+
`Trailing stop placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1443
|
+
`
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
async function cmdSwapAlgoCancel(client, instId, algoId, json) {
|
|
1447
|
+
const res = await client.privatePost("/api/v5/trade/cancel-algos", [
|
|
1448
|
+
{ algoId, instId }
|
|
1449
|
+
]);
|
|
1450
|
+
if (json) return printJson(res.data);
|
|
1451
|
+
const r = res.data[0];
|
|
1452
|
+
process.stdout.write(
|
|
1453
|
+
`Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1454
|
+
`
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
async function cmdSwapAlgoOrders(client, opts) {
|
|
1458
|
+
const endpoint = opts.status === "history" ? "/api/v5/trade/orders-algo-history" : "/api/v5/trade/orders-algo-pending";
|
|
1459
|
+
const baseParams = { instType: "SWAP" };
|
|
1460
|
+
if (opts.instId) baseParams["instId"] = opts.instId;
|
|
1461
|
+
let orders;
|
|
1462
|
+
if (opts.ordType) {
|
|
1463
|
+
const res = await client.privateGet(endpoint, { ...baseParams, ordType: opts.ordType });
|
|
1464
|
+
orders = res.data ?? [];
|
|
1465
|
+
} else {
|
|
1466
|
+
const [r1, r2, r3] = await Promise.all([
|
|
1467
|
+
client.privateGet(endpoint, { ...baseParams, ordType: "conditional" }),
|
|
1468
|
+
client.privateGet(endpoint, { ...baseParams, ordType: "oco" }),
|
|
1469
|
+
client.privateGet(endpoint, { ...baseParams, ordType: "move_order_stop" })
|
|
1470
|
+
]);
|
|
1471
|
+
orders = [
|
|
1472
|
+
...r1.data ?? [],
|
|
1473
|
+
...r2.data ?? [],
|
|
1474
|
+
...r3.data ?? []
|
|
1475
|
+
];
|
|
1476
|
+
}
|
|
1477
|
+
if (opts.json) return printJson(orders);
|
|
1478
|
+
if (!(orders ?? []).length) {
|
|
1479
|
+
process.stdout.write("No algo orders\n");
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
printTable(
|
|
1483
|
+
orders.map((o) => ({
|
|
1484
|
+
algoId: o["algoId"],
|
|
1485
|
+
instId: o["instId"],
|
|
1486
|
+
type: o["ordType"],
|
|
1487
|
+
side: o["side"],
|
|
1488
|
+
sz: o["sz"],
|
|
1489
|
+
tpTrigger: o["tpTriggerPx"],
|
|
1490
|
+
slTrigger: o["slTriggerPx"],
|
|
1491
|
+
state: o["state"]
|
|
1492
|
+
}))
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
async function cmdSwapFills(client, opts) {
|
|
1496
|
+
const path2 = opts.archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
1497
|
+
const params = { instType: "SWAP" };
|
|
1498
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1499
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1500
|
+
const res = await client.privateGet(path2, params);
|
|
1501
|
+
const fills = res.data;
|
|
1502
|
+
if (opts.json) return printJson(fills);
|
|
1503
|
+
printTable(
|
|
1504
|
+
(fills ?? []).map((f) => ({
|
|
1505
|
+
instId: f["instId"],
|
|
1506
|
+
side: f["side"],
|
|
1507
|
+
fillPx: f["fillPx"],
|
|
1508
|
+
fillSz: f["fillSz"],
|
|
1509
|
+
fee: f["fee"],
|
|
1510
|
+
ts: new Date(Number(f["ts"])).toLocaleString()
|
|
1511
|
+
}))
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
async function cmdSwapGet(client, opts) {
|
|
1515
|
+
const params = { instId: opts.instId };
|
|
1516
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1517
|
+
if (opts.clOrdId) params["clOrdId"] = opts.clOrdId;
|
|
1518
|
+
const res = await client.privateGet("/api/v5/trade/order", params);
|
|
1519
|
+
const data = res.data;
|
|
1520
|
+
if (opts.json) return printJson(data);
|
|
1521
|
+
const o = data?.[0];
|
|
1522
|
+
if (!o) {
|
|
1523
|
+
process.stdout.write("No data\n");
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
printKv({
|
|
1527
|
+
ordId: o["ordId"],
|
|
1528
|
+
instId: o["instId"],
|
|
1529
|
+
side: o["side"],
|
|
1530
|
+
posSide: o["posSide"],
|
|
1531
|
+
ordType: o["ordType"],
|
|
1532
|
+
px: o["px"],
|
|
1533
|
+
sz: o["sz"],
|
|
1534
|
+
fillSz: o["fillSz"],
|
|
1535
|
+
avgPx: o["avgPx"],
|
|
1536
|
+
state: o["state"],
|
|
1537
|
+
cTime: new Date(Number(o["cTime"])).toLocaleString()
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
async function cmdSwapClose(client, opts) {
|
|
1541
|
+
const body = {
|
|
1542
|
+
instId: opts.instId,
|
|
1543
|
+
mgnMode: opts.mgnMode
|
|
1544
|
+
};
|
|
1545
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1546
|
+
if (opts.autoCxl !== void 0) body["autoCxl"] = String(opts.autoCxl);
|
|
1547
|
+
const res = await client.privatePost("/api/v5/trade/close-position", body);
|
|
1548
|
+
if (opts.json) return printJson(res.data);
|
|
1549
|
+
const r = res.data[0];
|
|
1550
|
+
process.stdout.write(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}
|
|
1551
|
+
`);
|
|
1552
|
+
}
|
|
1553
|
+
async function cmdSwapGetLeverage(client, opts) {
|
|
1554
|
+
const res = await client.privateGet("/api/v5/account/leverage-info", {
|
|
1555
|
+
instId: opts.instId,
|
|
1556
|
+
mgnMode: opts.mgnMode
|
|
1557
|
+
});
|
|
1558
|
+
const data = res.data;
|
|
1559
|
+
if (opts.json) return printJson(data);
|
|
1560
|
+
printTable(
|
|
1561
|
+
(data ?? []).map((r) => ({
|
|
1562
|
+
instId: r["instId"],
|
|
1563
|
+
mgnMode: r["mgnMode"],
|
|
1564
|
+
posSide: r["posSide"],
|
|
1565
|
+
lever: r["lever"]
|
|
1566
|
+
}))
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
async function cmdSwapSetLeverage(client, opts) {
|
|
1570
|
+
const body = {
|
|
1571
|
+
instId: opts.instId,
|
|
1572
|
+
lever: opts.lever,
|
|
1573
|
+
mgnMode: opts.mgnMode
|
|
1574
|
+
};
|
|
1575
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1576
|
+
const res = await client.privatePost("/api/v5/account/set-leverage", body);
|
|
1577
|
+
if (opts.json) return printJson(res.data);
|
|
1578
|
+
const r = res.data[0];
|
|
1579
|
+
process.stdout.write(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}
|
|
1580
|
+
`);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// src/commands/futures.ts
|
|
1584
|
+
async function cmdFuturesOrders(client, opts) {
|
|
1585
|
+
const path2 = opts.status === "archive" ? "/api/v5/trade/orders-history-archive" : opts.status === "history" ? "/api/v5/trade/orders-history" : "/api/v5/trade/orders-pending";
|
|
1586
|
+
const params = { instType: "FUTURES" };
|
|
1587
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1588
|
+
const res = await client.privateGet(path2, params);
|
|
1589
|
+
const orders = res.data;
|
|
1590
|
+
if (opts.json) return printJson(orders);
|
|
1591
|
+
printTable(
|
|
1592
|
+
(orders ?? []).map((o) => ({
|
|
1593
|
+
ordId: o["ordId"],
|
|
1594
|
+
instId: o["instId"],
|
|
1595
|
+
side: o["side"],
|
|
1596
|
+
posSide: o["posSide"],
|
|
1597
|
+
type: o["ordType"],
|
|
1598
|
+
price: o["px"],
|
|
1599
|
+
size: o["sz"],
|
|
1600
|
+
state: o["state"]
|
|
1601
|
+
}))
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
async function cmdFuturesPositions(client, instId, json) {
|
|
1605
|
+
const params = { instType: "FUTURES" };
|
|
1606
|
+
if (instId) params["instId"] = instId;
|
|
1607
|
+
const res = await client.privateGet("/api/v5/account/positions", params);
|
|
1608
|
+
const positions = res.data;
|
|
1609
|
+
if (json) return printJson(positions);
|
|
1610
|
+
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
1611
|
+
if (!open.length) {
|
|
1612
|
+
process.stdout.write("No open positions\n");
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
printTable(
|
|
1616
|
+
open.map((p) => ({
|
|
1617
|
+
instId: p["instId"],
|
|
1618
|
+
side: p["posSide"],
|
|
1619
|
+
pos: p["pos"],
|
|
1620
|
+
avgPx: p["avgPx"],
|
|
1621
|
+
upl: p["upl"],
|
|
1622
|
+
lever: p["lever"]
|
|
1623
|
+
}))
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
async function cmdFuturesFills(client, opts) {
|
|
1627
|
+
const path2 = opts.archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
1628
|
+
const params = { instType: "FUTURES" };
|
|
1629
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1630
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1631
|
+
const res = await client.privateGet(path2, params);
|
|
1632
|
+
const fills = res.data;
|
|
1633
|
+
if (opts.json) return printJson(fills);
|
|
1634
|
+
printTable(
|
|
1635
|
+
(fills ?? []).map((f) => ({
|
|
1636
|
+
instId: f["instId"],
|
|
1637
|
+
side: f["side"],
|
|
1638
|
+
fillPx: f["fillPx"],
|
|
1639
|
+
fillSz: f["fillSz"],
|
|
1640
|
+
fee: f["fee"],
|
|
1641
|
+
ts: new Date(Number(f["ts"])).toLocaleString()
|
|
1642
|
+
}))
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
async function cmdFuturesPlace(client, opts) {
|
|
1646
|
+
const body = {
|
|
1647
|
+
instId: opts.instId,
|
|
1648
|
+
tdMode: opts.tdMode,
|
|
1649
|
+
side: opts.side,
|
|
1650
|
+
ordType: opts.ordType,
|
|
1651
|
+
sz: opts.sz
|
|
1652
|
+
};
|
|
1653
|
+
if (opts.posSide) body["posSide"] = opts.posSide;
|
|
1654
|
+
if (opts.px) body["px"] = opts.px;
|
|
1655
|
+
if (opts.reduceOnly !== void 0) body["reduceOnly"] = String(opts.reduceOnly);
|
|
1656
|
+
const res = await client.privatePost("/api/v5/trade/order", body);
|
|
1657
|
+
if (opts.json) return printJson(res.data);
|
|
1658
|
+
const order = res.data[0];
|
|
1659
|
+
process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
|
|
1660
|
+
`);
|
|
1661
|
+
}
|
|
1662
|
+
async function cmdFuturesCancel(client, instId, ordId, json) {
|
|
1663
|
+
const res = await client.privatePost("/api/v5/trade/cancel-order", { instId, ordId });
|
|
1664
|
+
if (json) return printJson(res.data);
|
|
1665
|
+
const r = res.data[0];
|
|
1666
|
+
process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1667
|
+
`);
|
|
1668
|
+
}
|
|
1669
|
+
async function cmdFuturesGet(client, opts) {
|
|
1670
|
+
const params = { instId: opts.instId };
|
|
1671
|
+
if (opts.ordId) params["ordId"] = opts.ordId;
|
|
1672
|
+
const res = await client.privateGet("/api/v5/trade/order", params);
|
|
1673
|
+
const data = res.data;
|
|
1674
|
+
if (opts.json) return printJson(data);
|
|
1675
|
+
const o = data?.[0];
|
|
1676
|
+
if (!o) {
|
|
1677
|
+
process.stdout.write("No data\n");
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
printKv({
|
|
1681
|
+
ordId: o["ordId"],
|
|
1682
|
+
instId: o["instId"],
|
|
1683
|
+
side: o["side"],
|
|
1684
|
+
posSide: o["posSide"],
|
|
1685
|
+
ordType: o["ordType"],
|
|
1686
|
+
px: o["px"],
|
|
1687
|
+
sz: o["sz"],
|
|
1688
|
+
fillSz: o["fillSz"],
|
|
1689
|
+
avgPx: o["avgPx"],
|
|
1690
|
+
state: o["state"],
|
|
1691
|
+
cTime: new Date(Number(o["cTime"])).toLocaleString()
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/config/toml.ts
|
|
1696
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
|
|
1697
|
+
import { stringify } from "smol-toml";
|
|
1698
|
+
function configDir() {
|
|
1699
|
+
return configFilePath().replace(/\/config\.toml$/, "");
|
|
1700
|
+
}
|
|
1701
|
+
function writeCliConfig(config) {
|
|
1702
|
+
const dir = configDir();
|
|
1703
|
+
if (!existsSync4(dir)) {
|
|
1704
|
+
mkdirSync3(dir, { recursive: true });
|
|
1705
|
+
}
|
|
1706
|
+
writeFileSync3(configFilePath(), stringify(config), "utf-8");
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// src/commands/config.ts
|
|
1710
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1711
|
+
import { parse as parse2, stringify as stringify2 } from "smol-toml";
|
|
1712
|
+
import { createInterface } from "readline";
|
|
1713
|
+
import { spawnSync } from "child_process";
|
|
1714
|
+
function readFullConfig() {
|
|
1715
|
+
const path2 = configFilePath();
|
|
1716
|
+
if (!existsSync5(path2)) return { profiles: {} };
|
|
1717
|
+
const raw = readFileSync4(path2, "utf-8");
|
|
1718
|
+
return parse2(raw);
|
|
1719
|
+
}
|
|
1720
|
+
function prompt(rl, question) {
|
|
1721
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
1722
|
+
}
|
|
1723
|
+
function cmdConfigShow(json) {
|
|
1724
|
+
const config = readFullConfig();
|
|
1725
|
+
if (json) return printJson(config);
|
|
1726
|
+
process.stdout.write(`Config: ${configFilePath()}
|
|
1727
|
+
|
|
1728
|
+
`);
|
|
1729
|
+
process.stdout.write(`default_profile: ${config.default_profile ?? "(not set)"}
|
|
1730
|
+
|
|
1731
|
+
`);
|
|
1732
|
+
for (const [name, profile] of Object.entries(config.profiles)) {
|
|
1733
|
+
process.stdout.write(`[${name}]
|
|
1734
|
+
`);
|
|
1735
|
+
printKv({
|
|
1736
|
+
api_key: profile.api_key ? "***" + profile.api_key.slice(-4) : "(not set)",
|
|
1737
|
+
demo: profile.demo ?? false,
|
|
1738
|
+
base_url: profile.base_url ?? "(default)"
|
|
1739
|
+
}, 2);
|
|
1740
|
+
process.stdout.write("\n");
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
function cmdConfigSet(key, value) {
|
|
1744
|
+
const config = readFullConfig();
|
|
1745
|
+
if (key === "default_profile") {
|
|
1746
|
+
config.default_profile = value;
|
|
1747
|
+
writeCliConfig(config);
|
|
1748
|
+
process.stdout.write(`default_profile set to "${value}"
|
|
1749
|
+
`);
|
|
1750
|
+
} else {
|
|
1751
|
+
process.stderr.write(`Unknown config key: ${key}
|
|
1752
|
+
`);
|
|
1753
|
+
process.exitCode = 1;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
var SITES = {
|
|
1757
|
+
global: { label: "Global (www.okx.com)", webUrl: "https://www.okx.com" },
|
|
1758
|
+
eea: { label: "EEA (my.okx.com)", webUrl: "https://my.okx.com" },
|
|
1759
|
+
us: { label: "US (app.okx.com)", webUrl: "https://app.okx.com" }
|
|
1760
|
+
};
|
|
1761
|
+
function parseSiteKey(raw) {
|
|
1762
|
+
if (raw === "2") return "eea";
|
|
1763
|
+
if (raw === "3") return "us";
|
|
1764
|
+
return "global";
|
|
1765
|
+
}
|
|
1766
|
+
function buildApiUrl(siteKey, demo) {
|
|
1767
|
+
const query = demo ? "?go-demo-trading=1" : "?go-live-trading=1";
|
|
1768
|
+
return `${SITES[siteKey].webUrl}/account/my-api${query}`;
|
|
1769
|
+
}
|
|
1770
|
+
function buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo) {
|
|
1771
|
+
const entry = { api_key: apiKey, secret_key: secretKey, passphrase, demo };
|
|
1772
|
+
if (siteKey !== "global") {
|
|
1773
|
+
entry.base_url = SITES[siteKey].webUrl;
|
|
1774
|
+
}
|
|
1775
|
+
return entry;
|
|
1776
|
+
}
|
|
1777
|
+
async function cmdConfigInit() {
|
|
1778
|
+
process.stdout.write("OKX Trade CLI \u2014 \u914D\u7F6E\u5411\u5BFC\n\n");
|
|
1779
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1780
|
+
try {
|
|
1781
|
+
process.stdout.write("\u8BF7\u9009\u62E9\u7AD9\u70B9:\n");
|
|
1782
|
+
process.stdout.write(" 1) Global (www.okx.com) [\u9ED8\u8BA4]\n");
|
|
1783
|
+
process.stdout.write(" 2) EEA (my.okx.com)\n");
|
|
1784
|
+
process.stdout.write(" 3) US (app.okx.com)\n");
|
|
1785
|
+
const siteRaw = (await prompt(rl, "\u7AD9\u70B9 (1/2/3, \u9ED8\u8BA4: 1): ")).trim();
|
|
1786
|
+
const siteKey = parseSiteKey(siteRaw);
|
|
1787
|
+
const demoRaw = (await prompt(rl, "\u4F7F\u7528\u6A21\u62DF\u76D8\uFF1F(Y/n) ")).trim().toLowerCase();
|
|
1788
|
+
const demo = demoRaw !== "n";
|
|
1789
|
+
const apiUrl = buildApiUrl(siteKey, demo);
|
|
1790
|
+
const hint = demo ? "\u9875\u9762\u4F1A\u81EA\u52A8\u8DF3\u8F6C\u5230\u6A21\u62DF\u76D8 API \u7BA1\u7406" : "\u9875\u9762\u4F1A\u81EA\u52A8\u8DF3\u8F6C\u5230\u5B9E\u76D8 API \u7BA1\u7406";
|
|
1791
|
+
process.stdout.write(`
|
|
1792
|
+
\u8BF7\u524D\u5F80 ${apiUrl} \u521B\u5EFA API Key\uFF08\u9700\u8981 trade \u6743\u9650\uFF09
|
|
1793
|
+
`);
|
|
1794
|
+
process.stdout.write(`\u63D0\u793A\uFF1A${hint}
|
|
1795
|
+
|
|
1796
|
+
`);
|
|
1797
|
+
try {
|
|
1798
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1799
|
+
spawnSync(opener, [apiUrl], { stdio: "ignore", shell: process.platform === "win32" });
|
|
1800
|
+
} catch {
|
|
1801
|
+
}
|
|
1802
|
+
const profileNameRaw = await prompt(rl, "Profile \u540D\u79F0 (\u9ED8\u8BA4: default): ");
|
|
1803
|
+
const profileName = profileNameRaw.trim() || "default";
|
|
1804
|
+
const apiKey = (await prompt(rl, "API Key: ")).trim();
|
|
1805
|
+
if (!apiKey) {
|
|
1806
|
+
process.stderr.write("\u9519\u8BEF: API Key \u4E0D\u80FD\u4E3A\u7A7A\n");
|
|
1807
|
+
process.exitCode = 1;
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
const secretKey = (await prompt(rl, "Secret Key: ")).trim();
|
|
1811
|
+
if (!secretKey) {
|
|
1812
|
+
process.stderr.write("\u9519\u8BEF: Secret Key \u4E0D\u80FD\u4E3A\u7A7A\n");
|
|
1813
|
+
process.exitCode = 1;
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const passphrase = (await prompt(rl, "Passphrase: ")).trim();
|
|
1817
|
+
if (!passphrase) {
|
|
1818
|
+
process.stderr.write("\u9519\u8BEF: Passphrase \u4E0D\u80FD\u4E3A\u7A7A\n");
|
|
1819
|
+
process.exitCode = 1;
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
if (demo) {
|
|
1823
|
+
process.stdout.write("\u5DF2\u9009\u62E9\u6A21\u62DF\u76D8\u6A21\u5F0F\uFF0C\u53EF\u968F\u65F6\u901A\u8FC7 okx config set \u5207\u6362\u4E3A\u5B9E\u76D8\u3002\n");
|
|
1824
|
+
}
|
|
1825
|
+
const config = readFullConfig();
|
|
1826
|
+
const profileEntry = buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo);
|
|
1827
|
+
config.profiles[profileName] = profileEntry;
|
|
1828
|
+
const configPath = configFilePath();
|
|
1829
|
+
try {
|
|
1830
|
+
writeCliConfig(config);
|
|
1831
|
+
process.stdout.write(`
|
|
1832
|
+
\u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}
|
|
1833
|
+
`);
|
|
1834
|
+
process.stdout.write(`\u4F7F\u7528\u65B9\u5F0F: okx --profile ${profileName} account balance
|
|
1835
|
+
`);
|
|
1836
|
+
if (!config.default_profile) {
|
|
1837
|
+
process.stdout.write(`\u63D0\u793A: \u8FD0\u884C okx config set default_profile ${profileName} \u53EF\u5C06\u5176\u8BBE\u4E3A\u9ED8\u8BA4
|
|
1838
|
+
`);
|
|
1839
|
+
}
|
|
1840
|
+
} catch (err) {
|
|
1841
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1842
|
+
const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
|
|
1843
|
+
process.stderr.write(`\u5199\u5165\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${message}
|
|
1844
|
+
`);
|
|
1845
|
+
if (isPermission) {
|
|
1846
|
+
process.stderr.write(`\u6743\u9650\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5 ${configPath} \u53CA\u5176\u7236\u76EE\u5F55\u7684\u8BFB\u5199\u6743\u9650\u3002
|
|
1847
|
+
`);
|
|
1848
|
+
}
|
|
1849
|
+
process.stderr.write("\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u5185\u5BB9\u5199\u5165 " + configPath + ":\n\n");
|
|
1850
|
+
process.stdout.write(stringify2(config) + "\n");
|
|
1851
|
+
process.exitCode = 1;
|
|
1852
|
+
}
|
|
1853
|
+
} finally {
|
|
1854
|
+
rl.close();
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// src/commands/client-setup.ts
|
|
1859
|
+
import * as fs from "fs";
|
|
1860
|
+
function cmdSetupClient(options) {
|
|
1861
|
+
runSetup(options);
|
|
1862
|
+
}
|
|
1863
|
+
function cmdSetupClients() {
|
|
1864
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1865
|
+
const detectedPaths = {
|
|
1866
|
+
"claude-desktop": `${home}/Library/Application Support/Claude/claude_desktop_config.json`,
|
|
1867
|
+
cursor: `${home}/.cursor/mcp.json`,
|
|
1868
|
+
windsurf: `${home}/.codeium/windsurf/mcp_config.json`
|
|
1869
|
+
};
|
|
1870
|
+
const detected = Object.entries(detectedPaths).filter(
|
|
1871
|
+
([, p]) => fs.existsSync(p)
|
|
1872
|
+
);
|
|
1873
|
+
if (detected.length > 0) {
|
|
1874
|
+
process.stdout.write(`Detected clients:
|
|
1875
|
+
`);
|
|
1876
|
+
for (const [id] of detected) {
|
|
1877
|
+
process.stdout.write(` ${id}
|
|
1878
|
+
`);
|
|
1879
|
+
}
|
|
1880
|
+
process.stdout.write(`
|
|
1881
|
+
`);
|
|
1882
|
+
}
|
|
1883
|
+
printSetupUsage();
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/commands/bot.ts
|
|
1887
|
+
async function cmdGridOrders(client, opts) {
|
|
1888
|
+
const path2 = opts.status === "history" ? "/api/v5/tradingBot/grid/orders-algo-history" : "/api/v5/tradingBot/grid/orders-algo-pending";
|
|
1889
|
+
const params = { algoOrdType: opts.algoOrdType };
|
|
1890
|
+
if (opts.instId) params["instId"] = opts.instId;
|
|
1891
|
+
if (opts.algoId) params["algoId"] = opts.algoId;
|
|
1892
|
+
const res = await client.privateGet(path2, params);
|
|
1893
|
+
const orders = res.data ?? [];
|
|
1894
|
+
if (opts.json) return printJson(orders);
|
|
1895
|
+
if (!orders.length) {
|
|
1896
|
+
process.stdout.write("No grid bots\n");
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
printTable(
|
|
1900
|
+
orders.map((o) => ({
|
|
1901
|
+
algoId: o["algoId"],
|
|
1902
|
+
instId: o["instId"],
|
|
1903
|
+
type: o["algoOrdType"],
|
|
1904
|
+
state: o["state"],
|
|
1905
|
+
pnl: o["pnlRatio"],
|
|
1906
|
+
gridNum: o["gridNum"],
|
|
1907
|
+
maxPx: o["maxPx"],
|
|
1908
|
+
minPx: o["minPx"],
|
|
1909
|
+
createdAt: new Date(Number(o["cTime"])).toLocaleString()
|
|
1910
|
+
}))
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
async function cmdGridDetails(client, opts) {
|
|
1914
|
+
const res = await client.privateGet("/api/v5/tradingBot/grid/orders-algo-details", {
|
|
1915
|
+
algoOrdType: opts.algoOrdType,
|
|
1916
|
+
algoId: opts.algoId
|
|
1917
|
+
});
|
|
1918
|
+
const detail = (res.data ?? [])[0];
|
|
1919
|
+
if (!detail) {
|
|
1920
|
+
process.stdout.write("Bot not found\n");
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
if (opts.json) return printJson(detail);
|
|
1924
|
+
printKv({
|
|
1925
|
+
algoId: detail["algoId"],
|
|
1926
|
+
instId: detail["instId"],
|
|
1927
|
+
type: detail["algoOrdType"],
|
|
1928
|
+
state: detail["state"],
|
|
1929
|
+
maxPx: detail["maxPx"],
|
|
1930
|
+
minPx: detail["minPx"],
|
|
1931
|
+
gridNum: detail["gridNum"],
|
|
1932
|
+
runType: detail["runType"] === "1" ? "arithmetic" : "geometric",
|
|
1933
|
+
pnl: detail["pnl"],
|
|
1934
|
+
pnlRatio: detail["pnlRatio"],
|
|
1935
|
+
investAmt: detail["investAmt"],
|
|
1936
|
+
totalAnnRate: detail["totalAnnRate"],
|
|
1937
|
+
createdAt: new Date(Number(detail["cTime"])).toLocaleString()
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
async function cmdGridSubOrders(client, opts) {
|
|
1941
|
+
const res = await client.privateGet("/api/v5/tradingBot/grid/sub-orders", {
|
|
1942
|
+
algoOrdType: opts.algoOrdType,
|
|
1943
|
+
algoId: opts.algoId,
|
|
1944
|
+
type: opts.type
|
|
1945
|
+
});
|
|
1946
|
+
const orders = res.data ?? [];
|
|
1947
|
+
if (opts.json) return printJson(orders);
|
|
1948
|
+
if (!orders.length) {
|
|
1949
|
+
process.stdout.write("No sub-orders\n");
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
printTable(
|
|
1953
|
+
orders.map((o) => ({
|
|
1954
|
+
ordId: o["ordId"],
|
|
1955
|
+
side: o["side"],
|
|
1956
|
+
px: o["px"],
|
|
1957
|
+
sz: o["sz"],
|
|
1958
|
+
fillPx: o["fillPx"],
|
|
1959
|
+
fillSz: o["fillSz"],
|
|
1960
|
+
state: o["state"],
|
|
1961
|
+
fee: o["fee"]
|
|
1962
|
+
}))
|
|
1963
|
+
);
|
|
1964
|
+
}
|
|
1965
|
+
async function cmdGridCreate(client, opts) {
|
|
1966
|
+
const body = {
|
|
1967
|
+
instId: opts.instId,
|
|
1968
|
+
algoOrdType: opts.algoOrdType,
|
|
1969
|
+
maxPx: opts.maxPx,
|
|
1970
|
+
minPx: opts.minPx,
|
|
1971
|
+
gridNum: opts.gridNum
|
|
1972
|
+
};
|
|
1973
|
+
if (opts.runType) body["runType"] = opts.runType;
|
|
1974
|
+
if (opts.quoteSz) body["quoteSz"] = opts.quoteSz;
|
|
1975
|
+
if (opts.baseSz) body["baseSz"] = opts.baseSz;
|
|
1976
|
+
if (opts.direction) body["direction"] = opts.direction;
|
|
1977
|
+
if (opts.lever) body["lever"] = opts.lever;
|
|
1978
|
+
if (opts.sz) body["sz"] = opts.sz;
|
|
1979
|
+
const res = await client.privatePost("/api/v5/tradingBot/grid/order-algo", body);
|
|
1980
|
+
if (opts.json) return printJson(res.data);
|
|
1981
|
+
const r = res.data[0];
|
|
1982
|
+
process.stdout.write(
|
|
1983
|
+
`Grid bot created: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1984
|
+
`
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
async function cmdGridStop(client, opts) {
|
|
1988
|
+
const entry = {
|
|
1989
|
+
algoId: opts.algoId,
|
|
1990
|
+
algoOrdType: opts.algoOrdType,
|
|
1991
|
+
instId: opts.instId
|
|
1992
|
+
};
|
|
1993
|
+
if (opts.stopType) entry["stopType"] = opts.stopType;
|
|
1994
|
+
const res = await client.privatePost("/api/v5/tradingBot/grid/stop-order-algo", [entry]);
|
|
1995
|
+
if (opts.json) return printJson(res.data);
|
|
1996
|
+
const r = res.data[0];
|
|
1997
|
+
process.stdout.write(
|
|
1998
|
+
`Grid bot stopped: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
|
|
1999
|
+
`
|
|
2000
|
+
);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/index.ts
|
|
2004
|
+
var _require = createRequire(import.meta.url);
|
|
2005
|
+
var CLI_VERSION = _require("../package.json").version;
|
|
2006
|
+
function printHelp() {
|
|
2007
|
+
process.stdout.write(`
|
|
2008
|
+
Usage: okx [--profile <name>] [--json] <command> [args]
|
|
2009
|
+
|
|
2010
|
+
Global Options:
|
|
2011
|
+
--profile <name> Use a named profile from ~/.okx/config.toml
|
|
2012
|
+
--demo Use simulated trading (demo) mode
|
|
2013
|
+
--json Output raw JSON
|
|
2014
|
+
--help Show this help
|
|
2015
|
+
|
|
2016
|
+
Commands:
|
|
2017
|
+
market ticker <instId>
|
|
2018
|
+
market tickers <instType> (SPOT|SWAP|FUTURES|OPTION)
|
|
2019
|
+
market orderbook <instId> [--sz <n>]
|
|
2020
|
+
market candles <instId> [--bar <bar>] [--limit <n>]
|
|
2021
|
+
market instruments --instType <type> [--instId <id>]
|
|
2022
|
+
market funding-rate <instId> [--history] [--limit <n>]
|
|
2023
|
+
market mark-price --instType <MARGIN|SWAP|FUTURES|OPTION> [--instId <id>]
|
|
2024
|
+
market trades <instId> [--limit <n>]
|
|
2025
|
+
market index-ticker [--instId <id>] [--quoteCcy <ccy>]
|
|
2026
|
+
market index-candles <instId> [--bar <bar>] [--limit <n>] [--history]
|
|
2027
|
+
market price-limit <instId>
|
|
2028
|
+
market open-interest --instType <SWAP|FUTURES|OPTION> [--instId <id>]
|
|
2029
|
+
|
|
2030
|
+
account balance [<ccy>]
|
|
2031
|
+
account asset-balance [--ccy <ccy>]
|
|
2032
|
+
account positions [--instType <type>] [--instId <id>]
|
|
2033
|
+
account positions-history [--instType <type>] [--instId <id>] [--limit <n>]
|
|
2034
|
+
account bills [--instType <type>] [--ccy <ccy>] [--limit <n>] [--archive]
|
|
2035
|
+
account fees --instType <type> [--instId <id>]
|
|
2036
|
+
account config
|
|
2037
|
+
account set-position-mode --posMode <long_short_mode|net_mode>
|
|
2038
|
+
account max-size --instId <id> --tdMode <cross|isolated> [--px <price>]
|
|
2039
|
+
account max-avail-size --instId <id> --tdMode <cross|isolated|cash>
|
|
2040
|
+
account max-withdrawal [--ccy <ccy>]
|
|
2041
|
+
account transfer --ccy <ccy> --amt <n> --from <acct> --to <acct> [--transferType <0|1|2|3>]
|
|
2042
|
+
|
|
2043
|
+
spot orders [--instId <id>] [--history]
|
|
2044
|
+
spot get --instId <id> --ordId <id>
|
|
2045
|
+
spot fills [--instId <id>] [--ordId <id>]
|
|
2046
|
+
spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>]
|
|
2047
|
+
spot amend --instId <id> --ordId <id> [--newSz <n>] [--newPx <price>]
|
|
2048
|
+
spot cancel <instId> --ordId <id>
|
|
2049
|
+
spot algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]
|
|
2050
|
+
spot algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]
|
|
2051
|
+
[--tpTriggerPx <price>] [--tpOrdPx <price|-1>]
|
|
2052
|
+
[--slTriggerPx <price>] [--slOrdPx <price|-1>]
|
|
2053
|
+
spot algo amend --instId <id> --algoId <id> [--newSz <n>]
|
|
2054
|
+
[--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]
|
|
2055
|
+
[--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]
|
|
2056
|
+
spot algo cancel --instId <id> --algoId <id>
|
|
2057
|
+
|
|
2058
|
+
swap positions [<instId>]
|
|
2059
|
+
swap orders [--instId <id>] [--history] [--archive]
|
|
2060
|
+
swap get --instId <id> --ordId <id>
|
|
2061
|
+
swap fills [--instId <id>] [--ordId <id>] [--archive]
|
|
2062
|
+
swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>] [--tdMode <cross|isolated>]
|
|
2063
|
+
swap cancel <instId> --ordId <id>
|
|
2064
|
+
swap close --instId <id> --mgnMode <cross|isolated> [--posSide <net|long|short>] [--autoCxl]
|
|
2065
|
+
swap leverage --instId <id> --lever <n> --mgnMode <cross|isolated> [--posSide <side>]
|
|
2066
|
+
swap get-leverage --instId <id> --mgnMode <cross|isolated>
|
|
2067
|
+
swap algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]
|
|
2068
|
+
swap algo trail --instId <id> --side <buy|sell> --sz <n> --callbackRatio <ratio>
|
|
2069
|
+
[--activePx <price>] [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]
|
|
2070
|
+
swap algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]
|
|
2071
|
+
[--tpTriggerPx <price>] [--tpOrdPx <price|-1>]
|
|
2072
|
+
[--slTriggerPx <price>] [--slOrdPx <price|-1>]
|
|
2073
|
+
[--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]
|
|
2074
|
+
swap algo amend --instId <id> --algoId <id> [--newSz <n>]
|
|
2075
|
+
[--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]
|
|
2076
|
+
[--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]
|
|
2077
|
+
swap algo cancel --instId <id> --algoId <id>
|
|
2078
|
+
|
|
2079
|
+
futures orders [--instId <id>] [--history] [--archive]
|
|
2080
|
+
futures positions [--instId <id>]
|
|
2081
|
+
futures fills [--instId <id>] [--ordId <id>] [--archive]
|
|
2082
|
+
futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--tdMode <cross|isolated>]
|
|
2083
|
+
[--posSide <net|long|short>] [--px <price>] [--reduceOnly]
|
|
2084
|
+
futures cancel <instId> --ordId <id>
|
|
2085
|
+
futures get --instId <id> --ordId <id>
|
|
2086
|
+
|
|
2087
|
+
bot grid orders --algoOrdType <grid|contract_grid|moon_grid> [--instId <id>] [--algoId <id>] [--history]
|
|
2088
|
+
bot grid details --algoOrdType <type> --algoId <id>
|
|
2089
|
+
bot grid sub-orders --algoOrdType <type> --algoId <id> [--live]
|
|
2090
|
+
bot grid create --instId <id> --algoOrdType <grid|contract_grid> --maxPx <px> --minPx <px> --gridNum <n>
|
|
2091
|
+
[--runType <1|2>] [--quoteSz <n>] [--baseSz <n>]
|
|
2092
|
+
[--direction <long|short|neutral>] [--lever <n>] [--sz <n>]
|
|
2093
|
+
bot grid stop --algoId <id> --algoOrdType <type> --instId <id> [--stopType <1|2|3|5|6>]
|
|
2094
|
+
|
|
2095
|
+
config init
|
|
2096
|
+
config show
|
|
2097
|
+
config set <key> <value>
|
|
2098
|
+
config setup-clients
|
|
2099
|
+
|
|
2100
|
+
setup --client <client> [--profile <name>] [--modules <list>]
|
|
2101
|
+
|
|
2102
|
+
Clients: ${SUPPORTED_CLIENTS.join(", ")}
|
|
2103
|
+
`);
|
|
2104
|
+
}
|
|
2105
|
+
function handleConfigCommand(action, rest, json) {
|
|
2106
|
+
if (action === "init") return cmdConfigInit();
|
|
2107
|
+
if (action === "show") return cmdConfigShow(json);
|
|
2108
|
+
if (action === "set") return cmdConfigSet(rest[0], rest[1]);
|
|
2109
|
+
if (action === "setup-clients") return cmdSetupClients();
|
|
2110
|
+
process.stderr.write(`Unknown config command: ${action}
|
|
2111
|
+
`);
|
|
2112
|
+
process.exitCode = 1;
|
|
2113
|
+
}
|
|
2114
|
+
function handleSetupCommand(v) {
|
|
2115
|
+
if (!v.client) {
|
|
2116
|
+
printSetupUsage();
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
if (!SUPPORTED_CLIENTS.includes(v.client)) {
|
|
2120
|
+
process.stderr.write(
|
|
2121
|
+
`Unknown client: "${v.client}"
|
|
2122
|
+
Supported: ${SUPPORTED_CLIENTS.join(", ")}
|
|
2123
|
+
`
|
|
2124
|
+
);
|
|
2125
|
+
process.exitCode = 1;
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
cmdSetupClient({
|
|
2129
|
+
client: v.client,
|
|
2130
|
+
profile: v.profile,
|
|
2131
|
+
modules: v.modules
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
function handleMarketPublicCommand(client, action, rest, v, json) {
|
|
2135
|
+
if (action === "ticker") return cmdMarketTicker(client, rest[0], json);
|
|
2136
|
+
if (action === "tickers") return cmdMarketTickers(client, rest[0], json);
|
|
2137
|
+
if (action === "instruments")
|
|
2138
|
+
return cmdMarketInstruments(client, { instType: v.instType, instId: v.instId, json });
|
|
2139
|
+
if (action === "mark-price")
|
|
2140
|
+
return cmdMarketMarkPrice(client, { instType: v.instType, instId: v.instId, json });
|
|
2141
|
+
if (action === "index-ticker")
|
|
2142
|
+
return cmdMarketIndexTicker(client, { instId: v.instId, quoteCcy: v.quoteCcy, json });
|
|
2143
|
+
if (action === "price-limit") return cmdMarketPriceLimit(client, rest[0], json);
|
|
2144
|
+
if (action === "open-interest")
|
|
2145
|
+
return cmdMarketOpenInterest(client, { instType: v.instType, instId: v.instId, json });
|
|
2146
|
+
}
|
|
2147
|
+
function handleMarketDataCommand(client, action, rest, v, json) {
|
|
2148
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
2149
|
+
if (action === "orderbook")
|
|
2150
|
+
return cmdMarketOrderbook(client, rest[0], v.sz !== void 0 ? Number(v.sz) : void 0, json);
|
|
2151
|
+
if (action === "candles")
|
|
2152
|
+
return cmdMarketCandles(client, rest[0], { bar: v.bar, limit, json });
|
|
2153
|
+
if (action === "funding-rate")
|
|
2154
|
+
return cmdMarketFundingRate(client, rest[0], { history: v.history ?? false, limit, json });
|
|
2155
|
+
if (action === "trades")
|
|
2156
|
+
return cmdMarketTrades(client, rest[0], { limit, json });
|
|
2157
|
+
if (action === "index-candles")
|
|
2158
|
+
return cmdMarketIndexCandles(client, rest[0], { bar: v.bar, limit, history: v.history ?? false, json });
|
|
2159
|
+
}
|
|
2160
|
+
function handleMarketCommand(client, action, rest, v, json) {
|
|
2161
|
+
return handleMarketPublicCommand(client, action, rest, v, json) ?? handleMarketDataCommand(client, action, rest, v, json);
|
|
2162
|
+
}
|
|
2163
|
+
function handleAccountWriteCommand(client, action, v, json) {
|
|
2164
|
+
if (action === "set-position-mode")
|
|
2165
|
+
return cmdAccountSetPositionMode(client, v.posMode, json);
|
|
2166
|
+
if (action === "max-size")
|
|
2167
|
+
return cmdAccountMaxSize(client, { instId: v.instId, tdMode: v.tdMode, px: v.px, json });
|
|
2168
|
+
if (action === "max-avail-size")
|
|
2169
|
+
return cmdAccountMaxAvailSize(client, { instId: v.instId, tdMode: v.tdMode, json });
|
|
2170
|
+
if (action === "max-withdrawal") return cmdAccountMaxWithdrawal(client, v.ccy, json);
|
|
2171
|
+
if (action === "transfer")
|
|
2172
|
+
return cmdAccountTransfer(client, {
|
|
2173
|
+
ccy: v.ccy,
|
|
2174
|
+
amt: v.amt,
|
|
2175
|
+
from: v.from,
|
|
2176
|
+
to: v.to,
|
|
2177
|
+
transferType: v.transferType,
|
|
2178
|
+
subAcct: v.subAcct,
|
|
2179
|
+
json
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
function handleAccountCommand(client, action, rest, v, json) {
|
|
2183
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
2184
|
+
if (action === "balance") return cmdAccountBalance(client, rest[0], json);
|
|
2185
|
+
if (action === "asset-balance") return cmdAccountAssetBalance(client, v.ccy, json);
|
|
2186
|
+
if (action === "positions")
|
|
2187
|
+
return cmdAccountPositions(client, { instType: v.instType, instId: v.instId, json });
|
|
2188
|
+
if (action === "positions-history")
|
|
2189
|
+
return cmdAccountPositionsHistory(client, {
|
|
2190
|
+
instType: v.instType,
|
|
2191
|
+
instId: v.instId,
|
|
2192
|
+
limit,
|
|
2193
|
+
json
|
|
2194
|
+
});
|
|
2195
|
+
if (action === "bills")
|
|
2196
|
+
return cmdAccountBills(client, {
|
|
2197
|
+
archive: v.archive ?? false,
|
|
2198
|
+
instType: v.instType,
|
|
2199
|
+
ccy: v.ccy,
|
|
2200
|
+
limit,
|
|
2201
|
+
json
|
|
2202
|
+
});
|
|
2203
|
+
if (action === "fees")
|
|
2204
|
+
return cmdAccountFees(client, { instType: v.instType, instId: v.instId, json });
|
|
2205
|
+
if (action === "config") return cmdAccountConfig(client, json);
|
|
2206
|
+
return handleAccountWriteCommand(client, action, v, json);
|
|
2207
|
+
}
|
|
2208
|
+
function handleSpotAlgoCommand(client, subAction, v, json) {
|
|
2209
|
+
if (subAction === "place")
|
|
2210
|
+
return cmdSpotAlgoPlace(client, {
|
|
2211
|
+
instId: v.instId,
|
|
2212
|
+
side: v.side,
|
|
2213
|
+
ordType: v.ordType ?? "conditional",
|
|
2214
|
+
sz: v.sz,
|
|
2215
|
+
tpTriggerPx: v.tpTriggerPx,
|
|
2216
|
+
tpOrdPx: v.tpOrdPx,
|
|
2217
|
+
slTriggerPx: v.slTriggerPx,
|
|
2218
|
+
slOrdPx: v.slOrdPx,
|
|
2219
|
+
json
|
|
2220
|
+
});
|
|
2221
|
+
if (subAction === "amend")
|
|
2222
|
+
return cmdSpotAlgoAmend(client, {
|
|
2223
|
+
instId: v.instId,
|
|
2224
|
+
algoId: v.algoId,
|
|
2225
|
+
newSz: v.newSz,
|
|
2226
|
+
newTpTriggerPx: v.newTpTriggerPx,
|
|
2227
|
+
newTpOrdPx: v.newTpOrdPx,
|
|
2228
|
+
newSlTriggerPx: v.newSlTriggerPx,
|
|
2229
|
+
newSlOrdPx: v.newSlOrdPx,
|
|
2230
|
+
json
|
|
2231
|
+
});
|
|
2232
|
+
if (subAction === "cancel")
|
|
2233
|
+
return cmdSpotAlgoCancel(client, v.instId, v.algoId, json);
|
|
2234
|
+
if (subAction === "orders")
|
|
2235
|
+
return cmdSpotAlgoOrders(client, {
|
|
2236
|
+
instId: v.instId,
|
|
2237
|
+
status: v.history ? "history" : "pending",
|
|
2238
|
+
ordType: v.ordType,
|
|
2239
|
+
json
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
function handleSpotCommand(client, action, rest, v, json) {
|
|
2243
|
+
if (action === "orders")
|
|
2244
|
+
return cmdSpotOrders(client, {
|
|
2245
|
+
instId: v.instId,
|
|
2246
|
+
status: v.history ? "history" : "open",
|
|
2247
|
+
json
|
|
2248
|
+
});
|
|
2249
|
+
if (action === "get")
|
|
2250
|
+
return cmdSpotGet(client, { instId: v.instId, ordId: v.ordId, clOrdId: v.clOrdId, json });
|
|
2251
|
+
if (action === "fills")
|
|
2252
|
+
return cmdSpotFills(client, { instId: v.instId, ordId: v.ordId, json });
|
|
2253
|
+
if (action === "amend")
|
|
2254
|
+
return cmdSpotAmend(client, {
|
|
2255
|
+
instId: v.instId,
|
|
2256
|
+
ordId: v.ordId,
|
|
2257
|
+
clOrdId: v.clOrdId,
|
|
2258
|
+
newSz: v.newSz,
|
|
2259
|
+
newPx: v.newPx,
|
|
2260
|
+
json
|
|
2261
|
+
});
|
|
2262
|
+
if (action === "place")
|
|
2263
|
+
return cmdSpotPlace(client, {
|
|
2264
|
+
instId: v.instId,
|
|
2265
|
+
side: v.side,
|
|
2266
|
+
ordType: v.ordType,
|
|
2267
|
+
sz: v.sz,
|
|
2268
|
+
px: v.px,
|
|
2269
|
+
json
|
|
2270
|
+
});
|
|
2271
|
+
if (action === "cancel")
|
|
2272
|
+
return cmdSpotCancel(client, rest[0], v.ordId, json);
|
|
2273
|
+
if (action === "algo")
|
|
2274
|
+
return handleSpotAlgoCommand(client, rest[0], v, json);
|
|
2275
|
+
}
|
|
2276
|
+
function handleSwapAlgoCommand(client, subAction, v, json) {
|
|
2277
|
+
if (subAction === "trail")
|
|
2278
|
+
return cmdSwapAlgoTrailPlace(client, {
|
|
2279
|
+
instId: v.instId,
|
|
2280
|
+
side: v.side,
|
|
2281
|
+
sz: v.sz,
|
|
2282
|
+
callbackRatio: v.callbackRatio,
|
|
2283
|
+
callbackSpread: v.callbackSpread,
|
|
2284
|
+
activePx: v.activePx,
|
|
2285
|
+
posSide: v.posSide,
|
|
2286
|
+
tdMode: v.tdMode ?? "cross",
|
|
2287
|
+
reduceOnly: v.reduceOnly,
|
|
2288
|
+
json
|
|
2289
|
+
});
|
|
2290
|
+
if (subAction === "place")
|
|
2291
|
+
return cmdSwapAlgoPlace(client, {
|
|
2292
|
+
instId: v.instId,
|
|
2293
|
+
side: v.side,
|
|
2294
|
+
ordType: v.ordType ?? "conditional",
|
|
2295
|
+
sz: v.sz,
|
|
2296
|
+
posSide: v.posSide,
|
|
2297
|
+
tdMode: v.tdMode ?? "cross",
|
|
2298
|
+
tpTriggerPx: v.tpTriggerPx,
|
|
2299
|
+
tpOrdPx: v.tpOrdPx,
|
|
2300
|
+
slTriggerPx: v.slTriggerPx,
|
|
2301
|
+
slOrdPx: v.slOrdPx,
|
|
2302
|
+
reduceOnly: v.reduceOnly,
|
|
2303
|
+
json
|
|
2304
|
+
});
|
|
2305
|
+
if (subAction === "amend")
|
|
2306
|
+
return cmdSwapAlgoAmend(client, {
|
|
2307
|
+
instId: v.instId,
|
|
2308
|
+
algoId: v.algoId,
|
|
2309
|
+
newSz: v.newSz,
|
|
2310
|
+
newTpTriggerPx: v.newTpTriggerPx,
|
|
2311
|
+
newTpOrdPx: v.newTpOrdPx,
|
|
2312
|
+
newSlTriggerPx: v.newSlTriggerPx,
|
|
2313
|
+
newSlOrdPx: v.newSlOrdPx,
|
|
2314
|
+
json
|
|
2315
|
+
});
|
|
2316
|
+
if (subAction === "cancel")
|
|
2317
|
+
return cmdSwapAlgoCancel(client, v.instId, v.algoId, json);
|
|
2318
|
+
if (subAction === "orders")
|
|
2319
|
+
return cmdSwapAlgoOrders(client, {
|
|
2320
|
+
instId: v.instId,
|
|
2321
|
+
status: v.history ? "history" : "pending",
|
|
2322
|
+
ordType: v.ordType,
|
|
2323
|
+
json
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
function handleSwapCommand(client, action, rest, v, json) {
|
|
2327
|
+
if (action === "positions")
|
|
2328
|
+
return cmdSwapPositions(client, rest[0] ?? v.instId, json);
|
|
2329
|
+
if (action === "orders")
|
|
2330
|
+
return cmdSwapOrders(client, {
|
|
2331
|
+
instId: v.instId,
|
|
2332
|
+
status: v.history ? "history" : "open",
|
|
2333
|
+
json
|
|
2334
|
+
});
|
|
2335
|
+
if (action === "get")
|
|
2336
|
+
return cmdSwapGet(client, { instId: v.instId, ordId: v.ordId, clOrdId: v.clOrdId, json });
|
|
2337
|
+
if (action === "fills")
|
|
2338
|
+
return cmdSwapFills(client, {
|
|
2339
|
+
instId: v.instId,
|
|
2340
|
+
ordId: v.ordId,
|
|
2341
|
+
archive: v.archive ?? false,
|
|
2342
|
+
json
|
|
2343
|
+
});
|
|
2344
|
+
if (action === "close")
|
|
2345
|
+
return cmdSwapClose(client, {
|
|
2346
|
+
instId: v.instId,
|
|
2347
|
+
mgnMode: v.mgnMode,
|
|
2348
|
+
posSide: v.posSide,
|
|
2349
|
+
autoCxl: v.autoCxl,
|
|
2350
|
+
json
|
|
2351
|
+
});
|
|
2352
|
+
if (action === "get-leverage")
|
|
2353
|
+
return cmdSwapGetLeverage(client, { instId: v.instId, mgnMode: v.mgnMode, json });
|
|
2354
|
+
if (action === "place")
|
|
2355
|
+
return cmdSwapPlace(client, {
|
|
2356
|
+
instId: v.instId,
|
|
2357
|
+
side: v.side,
|
|
2358
|
+
ordType: v.ordType,
|
|
2359
|
+
sz: v.sz,
|
|
2360
|
+
posSide: v.posSide,
|
|
2361
|
+
px: v.px,
|
|
2362
|
+
tdMode: v.tdMode ?? "cross",
|
|
2363
|
+
json
|
|
2364
|
+
});
|
|
2365
|
+
if (action === "cancel")
|
|
2366
|
+
return cmdSwapCancel(client, rest[0], v.ordId, json);
|
|
2367
|
+
if (action === "leverage")
|
|
2368
|
+
return cmdSwapSetLeverage(client, {
|
|
2369
|
+
instId: v.instId,
|
|
2370
|
+
lever: v.lever,
|
|
2371
|
+
mgnMode: v.mgnMode,
|
|
2372
|
+
posSide: v.posSide,
|
|
2373
|
+
json
|
|
2374
|
+
});
|
|
2375
|
+
if (action === "algo")
|
|
2376
|
+
return handleSwapAlgoCommand(client, rest[0], v, json);
|
|
2377
|
+
}
|
|
2378
|
+
function handleFuturesCommand(client, action, rest, v, json) {
|
|
2379
|
+
if (action === "orders") {
|
|
2380
|
+
let status = "open";
|
|
2381
|
+
if (v.archive) status = "archive";
|
|
2382
|
+
else if (v.history) status = "history";
|
|
2383
|
+
return cmdFuturesOrders(client, { instId: v.instId, status, json });
|
|
2384
|
+
}
|
|
2385
|
+
if (action === "positions") return cmdFuturesPositions(client, v.instId, json);
|
|
2386
|
+
if (action === "fills")
|
|
2387
|
+
return cmdFuturesFills(client, {
|
|
2388
|
+
instId: v.instId,
|
|
2389
|
+
ordId: v.ordId,
|
|
2390
|
+
archive: v.archive ?? false,
|
|
2391
|
+
json
|
|
2392
|
+
});
|
|
2393
|
+
if (action === "place")
|
|
2394
|
+
return cmdFuturesPlace(client, {
|
|
2395
|
+
instId: v.instId,
|
|
2396
|
+
side: v.side,
|
|
2397
|
+
ordType: v.ordType,
|
|
2398
|
+
sz: v.sz,
|
|
2399
|
+
tdMode: v.tdMode ?? "cross",
|
|
2400
|
+
posSide: v.posSide,
|
|
2401
|
+
px: v.px,
|
|
2402
|
+
reduceOnly: v.reduceOnly,
|
|
2403
|
+
json
|
|
2404
|
+
});
|
|
2405
|
+
if (action === "cancel")
|
|
2406
|
+
return cmdFuturesCancel(client, rest[0] ?? v.instId, v.ordId, json);
|
|
2407
|
+
if (action === "get")
|
|
2408
|
+
return cmdFuturesGet(client, { instId: rest[0] ?? v.instId, ordId: v.ordId, json });
|
|
2409
|
+
}
|
|
2410
|
+
function handleBotGridCommand(client, v, rest, json) {
|
|
2411
|
+
const subAction = rest[0];
|
|
2412
|
+
if (subAction === "orders")
|
|
2413
|
+
return cmdGridOrders(client, {
|
|
2414
|
+
algoOrdType: v.algoOrdType,
|
|
2415
|
+
instId: v.instId,
|
|
2416
|
+
algoId: v.algoId,
|
|
2417
|
+
status: v.history ? "history" : "active",
|
|
2418
|
+
json
|
|
2419
|
+
});
|
|
2420
|
+
if (subAction === "details")
|
|
2421
|
+
return cmdGridDetails(client, {
|
|
2422
|
+
algoOrdType: v.algoOrdType,
|
|
2423
|
+
algoId: v.algoId,
|
|
2424
|
+
json
|
|
2425
|
+
});
|
|
2426
|
+
if (subAction === "sub-orders")
|
|
2427
|
+
return cmdGridSubOrders(client, {
|
|
2428
|
+
algoOrdType: v.algoOrdType,
|
|
2429
|
+
algoId: v.algoId,
|
|
2430
|
+
type: v.live ? "live" : "filled",
|
|
2431
|
+
json
|
|
2432
|
+
});
|
|
2433
|
+
if (subAction === "create")
|
|
2434
|
+
return cmdGridCreate(client, {
|
|
2435
|
+
instId: v.instId,
|
|
2436
|
+
algoOrdType: v.algoOrdType,
|
|
2437
|
+
maxPx: v.maxPx,
|
|
2438
|
+
minPx: v.minPx,
|
|
2439
|
+
gridNum: v.gridNum,
|
|
2440
|
+
runType: v.runType,
|
|
2441
|
+
quoteSz: v.quoteSz,
|
|
2442
|
+
baseSz: v.baseSz,
|
|
2443
|
+
direction: v.direction,
|
|
2444
|
+
lever: v.lever,
|
|
2445
|
+
sz: v.sz,
|
|
2446
|
+
json
|
|
2447
|
+
});
|
|
2448
|
+
if (subAction === "stop")
|
|
2449
|
+
return cmdGridStop(client, {
|
|
2450
|
+
algoId: v.algoId,
|
|
2451
|
+
algoOrdType: v.algoOrdType,
|
|
2452
|
+
instId: v.instId,
|
|
2453
|
+
stopType: v.stopType,
|
|
2454
|
+
json
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
function handleBotCommand(client, action, rest, v, json) {
|
|
2458
|
+
if (action === "grid") return handleBotGridCommand(client, v, rest, json);
|
|
2459
|
+
}
|
|
2460
|
+
async function main() {
|
|
2461
|
+
checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION);
|
|
2462
|
+
const { values, positionals } = parseArgs({
|
|
2463
|
+
args: process.argv.slice(2),
|
|
2464
|
+
options: {
|
|
2465
|
+
profile: { type: "string" },
|
|
2466
|
+
demo: { type: "boolean", default: false },
|
|
2467
|
+
json: { type: "boolean", default: false },
|
|
2468
|
+
help: { type: "boolean", default: false },
|
|
2469
|
+
// setup command
|
|
2470
|
+
client: { type: "string" },
|
|
2471
|
+
modules: { type: "string" },
|
|
2472
|
+
// market candles
|
|
2473
|
+
bar: { type: "string" },
|
|
2474
|
+
limit: { type: "string" },
|
|
2475
|
+
sz: { type: "string" },
|
|
2476
|
+
// orders
|
|
2477
|
+
instId: { type: "string" },
|
|
2478
|
+
history: { type: "boolean", default: false },
|
|
2479
|
+
ordId: { type: "string" },
|
|
2480
|
+
// trade
|
|
2481
|
+
side: { type: "string" },
|
|
2482
|
+
ordType: { type: "string" },
|
|
2483
|
+
px: { type: "string" },
|
|
2484
|
+
posSide: { type: "string" },
|
|
2485
|
+
tdMode: { type: "string" },
|
|
2486
|
+
// leverage
|
|
2487
|
+
lever: { type: "string" },
|
|
2488
|
+
mgnMode: { type: "string" },
|
|
2489
|
+
// algo orders
|
|
2490
|
+
tpTriggerPx: { type: "string" },
|
|
2491
|
+
tpOrdPx: { type: "string" },
|
|
2492
|
+
slTriggerPx: { type: "string" },
|
|
2493
|
+
slOrdPx: { type: "string" },
|
|
2494
|
+
algoId: { type: "string" },
|
|
2495
|
+
reduceOnly: { type: "boolean", default: false },
|
|
2496
|
+
// algo amend
|
|
2497
|
+
newSz: { type: "string" },
|
|
2498
|
+
newTpTriggerPx: { type: "string" },
|
|
2499
|
+
newTpOrdPx: { type: "string" },
|
|
2500
|
+
newSlTriggerPx: { type: "string" },
|
|
2501
|
+
newSlOrdPx: { type: "string" },
|
|
2502
|
+
// trailing stop
|
|
2503
|
+
callbackRatio: { type: "string" },
|
|
2504
|
+
callbackSpread: { type: "string" },
|
|
2505
|
+
activePx: { type: "string" },
|
|
2506
|
+
// grid bot
|
|
2507
|
+
algoOrdType: { type: "string" },
|
|
2508
|
+
gridNum: { type: "string" },
|
|
2509
|
+
maxPx: { type: "string" },
|
|
2510
|
+
minPx: { type: "string" },
|
|
2511
|
+
runType: { type: "string" },
|
|
2512
|
+
quoteSz: { type: "string" },
|
|
2513
|
+
baseSz: { type: "string" },
|
|
2514
|
+
direction: { type: "string" },
|
|
2515
|
+
stopType: { type: "string" },
|
|
2516
|
+
live: { type: "boolean", default: false },
|
|
2517
|
+
// market extras
|
|
2518
|
+
instType: { type: "string" },
|
|
2519
|
+
quoteCcy: { type: "string" },
|
|
2520
|
+
// account extras
|
|
2521
|
+
archive: { type: "boolean", default: false },
|
|
2522
|
+
posMode: { type: "string" },
|
|
2523
|
+
ccy: { type: "string" },
|
|
2524
|
+
from: { type: "string" },
|
|
2525
|
+
to: { type: "string" },
|
|
2526
|
+
transferType: { type: "string" },
|
|
2527
|
+
subAcct: { type: "string" },
|
|
2528
|
+
amt: { type: "string" },
|
|
2529
|
+
// swap/order extras
|
|
2530
|
+
autoCxl: { type: "boolean", default: false },
|
|
2531
|
+
clOrdId: { type: "string" },
|
|
2532
|
+
newPx: { type: "string" }
|
|
2533
|
+
},
|
|
2534
|
+
allowPositionals: true
|
|
2535
|
+
});
|
|
2536
|
+
if (values.help || positionals.length === 0) {
|
|
2537
|
+
printHelp();
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
const [module, action, ...rest] = positionals;
|
|
2541
|
+
const v = values;
|
|
2542
|
+
const json = v.json ?? false;
|
|
2543
|
+
if (module === "config") return handleConfigCommand(action, rest, json);
|
|
2544
|
+
if (module === "setup") return handleSetupCommand(v);
|
|
2545
|
+
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, userAgent: `okx-trade-cli/${CLI_VERSION}` });
|
|
2546
|
+
const client = new OkxRestClient(config);
|
|
2547
|
+
if (module === "market") return handleMarketCommand(client, action, rest, v, json);
|
|
2548
|
+
if (module === "account") return handleAccountCommand(client, action, rest, v, json);
|
|
2549
|
+
if (module === "spot") return handleSpotCommand(client, action, rest, v, json);
|
|
2550
|
+
if (module === "swap") return handleSwapCommand(client, action, rest, v, json);
|
|
2551
|
+
if (module === "futures") return handleFuturesCommand(client, action, rest, v, json);
|
|
2552
|
+
if (module === "bot") return handleBotCommand(client, action, rest, v, json);
|
|
2553
|
+
process.stderr.write(`Unknown command: ${module} ${action ?? ""}
|
|
2554
|
+
`);
|
|
2555
|
+
process.exitCode = 1;
|
|
2556
|
+
}
|
|
2557
|
+
main().catch((error) => {
|
|
2558
|
+
const payload = toToolErrorPayload(error);
|
|
2559
|
+
process.stderr.write(`Error: ${payload.message}
|
|
2560
|
+
`);
|
|
2561
|
+
if (payload.traceId) process.stderr.write(`TraceId: ${payload.traceId}
|
|
2562
|
+
`);
|
|
2563
|
+
if (payload.suggestion) process.stderr.write(`Hint: ${payload.suggestion}
|
|
2564
|
+
`);
|
|
2565
|
+
process.stderr.write(`Version: @okx_ai/okx-trade-cli@${CLI_VERSION}
|
|
2566
|
+
`);
|
|
2567
|
+
process.exitCode = 1;
|
|
2568
|
+
});
|
|
2569
|
+
export {
|
|
2570
|
+
handleAccountWriteCommand,
|
|
2571
|
+
handleBotCommand,
|
|
2572
|
+
handleBotGridCommand,
|
|
2573
|
+
handleConfigCommand,
|
|
2574
|
+
handleMarketCommand,
|
|
2575
|
+
handleMarketDataCommand,
|
|
2576
|
+
handleMarketPublicCommand,
|
|
2577
|
+
handleSetupCommand,
|
|
2578
|
+
printHelp
|
|
2579
|
+
};
|
|
2580
|
+
//# sourceMappingURL=index.js.map
|