okx-trade-cli 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,13 +2,19 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { parseArgs } from "util";
5
+ import { createRequire } from "module";
5
6
 
6
- // node_modules/@okx-hub/core/dist/index.js
7
+ // ../core/dist/index.js
7
8
  import { createHmac } from "crypto";
9
+ import path from "path";
10
+ import os from "os";
8
11
  import { readFileSync, existsSync } from "fs";
9
12
  import { join } from "path";
10
13
  import { homedir } from "os";
11
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";
12
18
  function getNow() {
13
19
  return (/* @__PURE__ */ new Date()).toISOString();
14
20
  }
@@ -20,6 +26,7 @@ var OkxMcpError = class extends Error {
20
26
  code;
21
27
  suggestion;
22
28
  endpoint;
29
+ traceId;
23
30
  constructor(type, message, options) {
24
31
  super(message, options?.cause ? { cause: options.cause } : void 0);
25
32
  this.name = type;
@@ -27,6 +34,7 @@ var OkxMcpError = class extends Error {
27
34
  this.code = options?.code;
28
35
  this.suggestion = options?.suggestion;
29
36
  this.endpoint = options?.endpoint;
37
+ this.traceId = options?.traceId;
30
38
  }
31
39
  };
32
40
  var ConfigError = class extends OkxMcpError {
@@ -35,13 +43,13 @@ var ConfigError = class extends OkxMcpError {
35
43
  }
36
44
  };
37
45
  var RateLimitError = class extends OkxMcpError {
38
- constructor(message, suggestion, endpoint) {
39
- super("RateLimitError", message, { suggestion, endpoint });
46
+ constructor(message, suggestion, endpoint, traceId) {
47
+ super("RateLimitError", message, { suggestion, endpoint, traceId });
40
48
  }
41
49
  };
42
50
  var AuthenticationError = class extends OkxMcpError {
43
- constructor(message, suggestion, endpoint) {
44
- super("AuthenticationError", message, { suggestion, endpoint });
51
+ constructor(message, suggestion, endpoint, traceId) {
52
+ super("AuthenticationError", message, { suggestion, endpoint, traceId });
45
53
  }
46
54
  };
47
55
  var OkxApiError = class extends OkxMcpError {
@@ -67,6 +75,7 @@ function toToolErrorPayload(error, fallbackEndpoint) {
67
75
  message: error.message,
68
76
  suggestion: error.suggestion,
69
77
  endpoint: error.endpoint ?? fallbackEndpoint,
78
+ traceId: error.traceId,
70
79
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
71
80
  };
72
81
  }
@@ -150,6 +159,9 @@ var RateLimiter = class {
150
159
  function isDefined(value) {
151
160
  return value !== void 0 && value !== null;
152
161
  }
162
+ function extractTraceId(headers) {
163
+ return headers.get("x-trace-id") ?? headers.get("x-request-id") ?? headers.get("traceid") ?? void 0;
164
+ }
153
165
  function stringifyQueryValue(value) {
154
166
  if (Array.isArray(value)) {
155
167
  return value.map((item) => String(item)).join(",");
@@ -176,28 +188,28 @@ var OkxRestClient = class {
176
188
  constructor(config) {
177
189
  this.config = config;
178
190
  }
179
- async publicGet(path, query, rateLimit) {
191
+ async publicGet(path3, query, rateLimit) {
180
192
  return this.request({
181
193
  method: "GET",
182
- path,
194
+ path: path3,
183
195
  auth: "public",
184
196
  query,
185
197
  rateLimit
186
198
  });
187
199
  }
188
- async privateGet(path, query, rateLimit) {
200
+ async privateGet(path3, query, rateLimit) {
189
201
  return this.request({
190
202
  method: "GET",
191
- path,
203
+ path: path3,
192
204
  auth: "private",
193
205
  query,
194
206
  rateLimit
195
207
  });
196
208
  }
197
- async privatePost(path, body, rateLimit) {
209
+ async privatePost(path3, body, rateLimit) {
198
210
  return this.request({
199
211
  method: "POST",
200
- path,
212
+ path: path3,
201
213
  auth: "private",
202
214
  body,
203
215
  rateLimit
@@ -216,6 +228,9 @@ var OkxRestClient = class {
216
228
  "Content-Type": "application/json",
217
229
  Accept: "application/json"
218
230
  });
231
+ if (this.config.userAgent) {
232
+ headers.set("User-Agent", this.config.userAgent);
233
+ }
219
234
  if (config.auth === "private") {
220
235
  if (!this.config.hasAuth) {
221
236
  throw new ConfigError(
@@ -255,6 +270,7 @@ var OkxRestClient = class {
255
270
  );
256
271
  }
257
272
  const rawText = await response.text();
273
+ const traceId = extractTraceId(response.headers);
258
274
  let parsed;
259
275
  try {
260
276
  parsed = rawText ? JSON.parse(rawText) : {};
@@ -266,7 +282,8 @@ var OkxRestClient = class {
266
282
  {
267
283
  code: String(response.status),
268
284
  endpoint: `${config.method} ${config.path}`,
269
- suggestion: "Verify endpoint path and request parameters."
285
+ suggestion: "Verify endpoint path and request parameters.",
286
+ traceId
270
287
  }
271
288
  );
272
289
  }
@@ -282,7 +299,8 @@ var OkxRestClient = class {
282
299
  {
283
300
  code: String(response.status),
284
301
  endpoint: `${config.method} ${config.path}`,
285
- suggestion: "Retry later or verify endpoint parameters."
302
+ suggestion: "Retry later or verify endpoint parameters.",
303
+ traceId
286
304
  }
287
305
  );
288
306
  }
@@ -293,12 +311,14 @@ var OkxRestClient = class {
293
311
  throw new AuthenticationError(
294
312
  message,
295
313
  "Check API key, secret, passphrase and permissions.",
296
- `${config.method} ${config.path}`
314
+ `${config.method} ${config.path}`,
315
+ traceId
297
316
  );
298
317
  }
299
318
  throw new OkxApiError(message, {
300
319
  code: responseCode,
301
- endpoint: `${config.method} ${config.path}`
320
+ endpoint: `${config.method} ${config.path}`,
321
+ traceId
302
322
  });
303
323
  }
304
324
  return {
@@ -309,21 +329,24 @@ var OkxRestClient = class {
309
329
  };
310
330
  }
311
331
  };
332
+ var DEFAULT_LOG_DIR = path.join(os.homedir(), ".okx", "logs");
312
333
  var OKX_API_BASE_URL = "https://www.okx.com";
313
334
  var MODULES = [
314
335
  "market",
315
336
  "spot",
316
337
  "swap",
317
- "account"
338
+ "futures",
339
+ "account",
340
+ "bot"
318
341
  ];
319
342
  var DEFAULT_MODULES = ["spot", "swap", "account"];
320
343
  function configFilePath() {
321
344
  return join(homedir(), ".okx", "config.toml");
322
345
  }
323
346
  function readTomlProfile(profileName) {
324
- const path = configFilePath();
325
- if (!existsSync(path)) return {};
326
- const raw = readFileSync(path, "utf-8");
347
+ const path3 = configFilePath();
348
+ if (!existsSync(path3)) return {};
349
+ const raw = readFileSync(path3, "utf-8");
327
350
  const config = parse(raw);
328
351
  const name = profileName ?? config.default_profile ?? "default";
329
352
  return config.profiles?.[name] ?? {};
@@ -390,9 +413,77 @@ function loadConfig(cli) {
390
413
  timeoutMs: Math.floor(rawTimeout),
391
414
  modules: parseModuleList(cli.modules),
392
415
  readOnly: cli.readOnly,
393
- demo
416
+ demo,
417
+ userAgent: cli.userAgent
394
418
  };
395
419
  }
420
+ var CACHE_FILE = join2(homedir2(), ".okx", "update-check.json");
421
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
422
+ function readCache() {
423
+ try {
424
+ if (existsSync2(CACHE_FILE)) {
425
+ return JSON.parse(readFileSync2(CACHE_FILE, "utf-8"));
426
+ }
427
+ } catch {
428
+ }
429
+ return {};
430
+ }
431
+ function writeCache(cache) {
432
+ try {
433
+ mkdirSync(join2(homedir2(), ".okx"), { recursive: true });
434
+ writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
435
+ } catch {
436
+ }
437
+ }
438
+ function isNewerVersion(current, latest) {
439
+ const parse22 = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10));
440
+ const [cMaj, cMin, cPat] = parse22(current);
441
+ const [lMaj, lMin, lPat] = parse22(latest);
442
+ if (lMaj !== cMaj) return lMaj > cMaj;
443
+ if (lMin !== cMin) return lMin > cMin;
444
+ return lPat > cPat;
445
+ }
446
+ async function fetchLatestVersion(packageName) {
447
+ try {
448
+ const controller = new AbortController();
449
+ const timeout = setTimeout(() => controller.abort(), 3e3);
450
+ const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
451
+ signal: controller.signal,
452
+ headers: { accept: "application/json" }
453
+ });
454
+ clearTimeout(timeout);
455
+ if (!res.ok) return null;
456
+ const data = await res.json();
457
+ return data.version ?? null;
458
+ } catch {
459
+ return null;
460
+ }
461
+ }
462
+ function refreshCacheInBackground(packageName) {
463
+ fetchLatestVersion(packageName).then((latest) => {
464
+ if (!latest) return;
465
+ const cache = readCache();
466
+ cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
467
+ writeCache(cache);
468
+ }).catch(() => {
469
+ });
470
+ }
471
+ function checkForUpdates(packageName, currentVersion) {
472
+ const cache = readCache();
473
+ const entry = cache[packageName];
474
+ if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
475
+ process.stderr.write(
476
+ `
477
+ Update available for ${packageName}: ${currentVersion} \u2192 ${entry.latestVersion}
478
+ Run: npm install -g ${packageName}
479
+
480
+ `
481
+ );
482
+ }
483
+ if (!entry || Date.now() - entry.checkedAt > CHECK_INTERVAL_MS) {
484
+ refreshCacheInBackground(packageName);
485
+ }
486
+ }
396
487
 
397
488
  // src/config/loader.ts
398
489
  function loadProfileConfig(opts) {
@@ -400,7 +491,8 @@ function loadProfileConfig(opts) {
400
491
  profile: opts.profile,
401
492
  modules: opts.modules,
402
493
  readOnly: opts.readOnly ?? false,
403
- demo: opts.demo ?? false
494
+ demo: opts.demo ?? false,
495
+ userAgent: opts.userAgent
404
496
  });
405
497
  }
406
498
 
@@ -784,6 +876,27 @@ async function cmdSwapAlgoAmend(client, opts) {
784
876
  `
785
877
  );
786
878
  }
879
+ async function cmdSwapAlgoTrailPlace(client, opts) {
880
+ const body = {
881
+ instId: opts.instId,
882
+ tdMode: opts.tdMode,
883
+ side: opts.side,
884
+ ordType: "move_order_stop",
885
+ sz: opts.sz
886
+ };
887
+ if (opts.posSide) body["posSide"] = opts.posSide;
888
+ if (opts.callbackRatio) body["callbackRatio"] = opts.callbackRatio;
889
+ if (opts.callbackSpread) body["callbackSpread"] = opts.callbackSpread;
890
+ if (opts.activePx) body["activePx"] = opts.activePx;
891
+ if (opts.reduceOnly !== void 0) body["reduceOnly"] = String(opts.reduceOnly);
892
+ const res = await client.privatePost("/api/v5/trade/order-algo", body);
893
+ if (opts.json) return printJson(res.data);
894
+ const order = res.data[0];
895
+ process.stdout.write(
896
+ `Trailing stop placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
897
+ `
898
+ );
899
+ }
787
900
  async function cmdSwapAlgoCancel(client, instId, algoId, json) {
788
901
  const res = await client.privatePost("/api/v5/trade/cancel-algos", [
789
902
  { algoId, instId }
@@ -804,13 +917,15 @@ async function cmdSwapAlgoOrders(client, opts) {
804
917
  const res = await client.privateGet(endpoint, { ...baseParams, ordType: opts.ordType });
805
918
  orders = res.data ?? [];
806
919
  } else {
807
- const [r1, r2] = await Promise.all([
920
+ const [r1, r2, r3] = await Promise.all([
808
921
  client.privateGet(endpoint, { ...baseParams, ordType: "conditional" }),
809
- client.privateGet(endpoint, { ...baseParams, ordType: "oco" })
922
+ client.privateGet(endpoint, { ...baseParams, ordType: "oco" }),
923
+ client.privateGet(endpoint, { ...baseParams, ordType: "move_order_stop" })
810
924
  ]);
811
925
  orders = [
812
926
  ...r1.data ?? [],
813
- ...r2.data ?? []
927
+ ...r2.data ?? [],
928
+ ...r3.data ?? []
814
929
  ];
815
930
  }
816
931
  if (opts.json) return printJson(orders);
@@ -846,28 +961,33 @@ async function cmdSwapSetLeverage(client, opts) {
846
961
  }
847
962
 
848
963
  // src/config/toml.ts
849
- import { writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
964
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
850
965
  import { stringify } from "smol-toml";
851
966
  function configDir() {
852
967
  return configFilePath().replace(/\/config\.toml$/, "");
853
968
  }
854
969
  function writeCliConfig(config) {
855
970
  const dir = configDir();
856
- if (!existsSync2(dir)) {
857
- mkdirSync(dir, { recursive: true });
971
+ if (!existsSync3(dir)) {
972
+ mkdirSync2(dir, { recursive: true });
858
973
  }
859
- writeFileSync(configFilePath(), stringify(config), "utf-8");
974
+ writeFileSync2(configFilePath(), stringify(config), "utf-8");
860
975
  }
861
976
 
862
977
  // src/commands/config.ts
863
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
864
- import { parse as parse2 } from "smol-toml";
978
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
979
+ import { parse as parse2, stringify as stringify2 } from "smol-toml";
980
+ import { createInterface } from "readline";
981
+ import { spawnSync } from "child_process";
865
982
  function readFullConfig() {
866
- const path = configFilePath();
867
- if (!existsSync3(path)) return { profiles: {} };
868
- const raw = readFileSync2(path, "utf-8");
983
+ const path3 = configFilePath();
984
+ if (!existsSync4(path3)) return { profiles: {} };
985
+ const raw = readFileSync3(path3, "utf-8");
869
986
  return parse2(raw);
870
987
  }
988
+ function prompt(rl, question) {
989
+ return new Promise((resolve) => rl.question(question, resolve));
990
+ }
871
991
  function cmdConfigShow(json) {
872
992
  const config = readFullConfig();
873
993
  if (json) return printJson(config);
@@ -901,8 +1021,188 @@ function cmdConfigSet(key, value) {
901
1021
  process.exitCode = 1;
902
1022
  }
903
1023
  }
1024
+ async function cmdConfigInit() {
1025
+ const apiUrl = "https://www.okx.com/account/my-api";
1026
+ process.stdout.write("OKX Trade CLI \u2014 \u914D\u7F6E\u5411\u5BFC\n\n");
1027
+ process.stdout.write(`\u8BF7\u524D\u5F80 ${apiUrl} \u521B\u5EFA API Key\uFF08\u9700\u8981 trade \u6743\u9650\uFF09
1028
+
1029
+ `);
1030
+ try {
1031
+ const opener = process.platform === "darwin" ? "open" : "xdg-open";
1032
+ spawnSync(opener, [apiUrl], { stdio: "ignore" });
1033
+ } catch {
1034
+ }
1035
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1036
+ try {
1037
+ const profileNameRaw = await prompt(rl, "Profile \u540D\u79F0 (\u9ED8\u8BA4: default): ");
1038
+ const profileName = profileNameRaw.trim() || "default";
1039
+ const apiKey = (await prompt(rl, "API Key: ")).trim();
1040
+ if (!apiKey) {
1041
+ process.stderr.write("\u9519\u8BEF: API Key \u4E0D\u80FD\u4E3A\u7A7A\n");
1042
+ process.exitCode = 1;
1043
+ return;
1044
+ }
1045
+ const secretKey = (await prompt(rl, "Secret Key: ")).trim();
1046
+ if (!secretKey) {
1047
+ process.stderr.write("\u9519\u8BEF: Secret Key \u4E0D\u80FD\u4E3A\u7A7A\n");
1048
+ process.exitCode = 1;
1049
+ return;
1050
+ }
1051
+ const passphrase = (await prompt(rl, "Passphrase: ")).trim();
1052
+ if (!passphrase) {
1053
+ process.stderr.write("\u9519\u8BEF: Passphrase \u4E0D\u80FD\u4E3A\u7A7A\n");
1054
+ process.exitCode = 1;
1055
+ return;
1056
+ }
1057
+ const demoRaw = (await prompt(rl, "\u4F7F\u7528\u6A21\u62DF\u76D8\uFF1F(Y/n) ")).trim().toLowerCase();
1058
+ const demo = demoRaw !== "n";
1059
+ if (demo) {
1060
+ 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");
1061
+ }
1062
+ const config = readFullConfig();
1063
+ config.profiles[profileName] = { api_key: apiKey, secret_key: secretKey, passphrase, demo };
1064
+ const configPath = configFilePath();
1065
+ try {
1066
+ writeCliConfig(config);
1067
+ process.stdout.write(`
1068
+ \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}
1069
+ `);
1070
+ process.stdout.write(`\u4F7F\u7528\u65B9\u5F0F: okx --profile ${profileName} account balance
1071
+ `);
1072
+ if (!config.default_profile) {
1073
+ process.stdout.write(`\u63D0\u793A: \u8FD0\u884C okx config set default_profile ${profileName} \u53EF\u5C06\u5176\u8BBE\u4E3A\u9ED8\u8BA4
1074
+ `);
1075
+ }
1076
+ } catch (err) {
1077
+ const message = err instanceof Error ? err.message : String(err);
1078
+ const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
1079
+ process.stderr.write(`\u5199\u5165\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${message}
1080
+ `);
1081
+ if (isPermission) {
1082
+ process.stderr.write(`\u6743\u9650\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5 ${configPath} \u53CA\u5176\u7236\u76EE\u5F55\u7684\u8BFB\u5199\u6743\u9650\u3002
1083
+ `);
1084
+ }
1085
+ process.stderr.write("\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u5185\u5BB9\u5199\u5165 " + configPath + ":\n\n");
1086
+ process.stdout.write(stringify2(config) + "\n");
1087
+ process.exitCode = 1;
1088
+ }
1089
+ } finally {
1090
+ rl.close();
1091
+ }
1092
+ }
1093
+
1094
+ // src/commands/client-setup.ts
1095
+ import * as fs from "fs";
1096
+ import * as path2 from "path";
1097
+ import * as os2 from "os";
1098
+ import * as readline from "readline";
1099
+ var CLIENTS = [
1100
+ {
1101
+ name: "Claude Desktop",
1102
+ configPath: path2.join(os2.homedir(), "Library/Application Support/Claude/claude_desktop_config.json"),
1103
+ mcpKey: "mcpServers"
1104
+ },
1105
+ {
1106
+ name: "Cursor",
1107
+ configPath: path2.join(os2.homedir(), ".cursor/mcp.json"),
1108
+ mcpKey: "mcpServers"
1109
+ },
1110
+ {
1111
+ name: "Windsurf",
1112
+ configPath: path2.join(os2.homedir(), ".codeium/windsurf/mcp_config.json"),
1113
+ mcpKey: "mcpServers"
1114
+ }
1115
+ ];
1116
+ var MCP_ENTRY = {
1117
+ command: "okx-trade-mcp",
1118
+ args: ["--modules", "all"]
1119
+ };
1120
+ var MCP_SERVER_NAME = "okx-trade-mcp";
1121
+ function prompt2(rl, question) {
1122
+ return new Promise((resolve) => {
1123
+ rl.question(question, (answer) => {
1124
+ resolve(answer);
1125
+ });
1126
+ });
1127
+ }
1128
+ async function cmdSetupClients() {
1129
+ const detected = CLIENTS.filter((c) => fs.existsSync(c.configPath));
1130
+ if (detected.length === 0) {
1131
+ process.stdout.write(
1132
+ "No supported IDE/client installations detected.\nChecked:\n" + CLIENTS.map((c) => ` - ${c.name}: ${c.configPath}`).join("\n") + "\n"
1133
+ );
1134
+ return;
1135
+ }
1136
+ process.stdout.write(`Detected ${detected.length} client(s):
1137
+ `);
1138
+ for (const c of detected) {
1139
+ process.stdout.write(` - ${c.name}
1140
+ `);
1141
+ }
1142
+ process.stdout.write("\n");
1143
+ const rl = readline.createInterface({
1144
+ input: process.stdin,
1145
+ output: process.stdout
1146
+ });
1147
+ try {
1148
+ for (const client of detected) {
1149
+ const answer = await prompt2(rl, `Configure ${client.name}? (y/N) `);
1150
+ if (answer.trim().toLowerCase() !== "y") {
1151
+ process.stdout.write(` Skipped ${client.name}.
1152
+ `);
1153
+ continue;
1154
+ }
1155
+ let data = { [client.mcpKey]: {} };
1156
+ if (fs.existsSync(client.configPath)) {
1157
+ const raw = fs.readFileSync(client.configPath, "utf-8");
1158
+ try {
1159
+ data = JSON.parse(raw);
1160
+ } catch {
1161
+ process.stderr.write(
1162
+ ` Error: Failed to parse JSON for ${client.name} at ${client.configPath}. Skipping.
1163
+ `
1164
+ );
1165
+ continue;
1166
+ }
1167
+ }
1168
+ if (typeof data[client.mcpKey] !== "object" || data[client.mcpKey] === null) {
1169
+ data[client.mcpKey] = {};
1170
+ }
1171
+ const servers = data[client.mcpKey];
1172
+ if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME)) {
1173
+ process.stdout.write(` Already configured in ${client.name}. Skipping.
1174
+ `);
1175
+ continue;
1176
+ }
1177
+ servers[MCP_SERVER_NAME] = MCP_ENTRY;
1178
+ const jsonOutput = JSON.stringify(data, null, 2);
1179
+ try {
1180
+ fs.writeFileSync(client.configPath, jsonOutput, "utf-8");
1181
+ process.stdout.write(` Configured ${client.name} successfully.
1182
+ `);
1183
+ } catch (err) {
1184
+ const reason = err instanceof Error ? err.message : String(err);
1185
+ process.stderr.write(
1186
+ ` Error: Failed to write config for ${client.name}: ${reason}
1187
+ Add the following to "${client.configPath}" manually:
1188
+
1189
+ "${MCP_SERVER_NAME}": ${JSON.stringify(MCP_ENTRY, null, 2).split("\n").join("\n ")}
1190
+
1191
+ `
1192
+ );
1193
+ }
1194
+ }
1195
+ } finally {
1196
+ rl.close();
1197
+ }
1198
+ process.stdout.write(
1199
+ "\nDone. Please restart any configured IDE/client for the changes to take effect.\n"
1200
+ );
1201
+ }
904
1202
 
905
1203
  // src/index.ts
1204
+ var _require = createRequire(import.meta.url);
1205
+ var CLI_VERSION = _require("../package.json").version;
906
1206
  function printHelp() {
907
1207
  process.stdout.write(`
908
1208
  Usage: okx [--profile <name>] [--json] <command> [args]
@@ -939,6 +1239,8 @@ Commands:
939
1239
  swap cancel <instId> --ordId <id>
940
1240
  swap leverage --instId <id> --lever <n> --mgnMode <cross|isolated> [--posSide <side>]
941
1241
  swap algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]
1242
+ swap algo trail --instId <id> --side <buy|sell> --sz <n> --callbackRatio <ratio>
1243
+ [--activePx <price>] [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]
942
1244
  swap algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]
943
1245
  [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]
944
1246
  [--slTriggerPx <price>] [--slOrdPx <price|-1>]
@@ -948,11 +1250,14 @@ Commands:
948
1250
  [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]
949
1251
  swap algo cancel --instId <id> --algoId <id>
950
1252
 
1253
+ config init
951
1254
  config show
952
1255
  config set <key> <value>
1256
+ config setup-clients
953
1257
  `);
954
1258
  }
955
1259
  async function main() {
1260
+ checkForUpdates("okx-trade-cli", CLI_VERSION);
956
1261
  const { values, positionals } = parseArgs({
957
1262
  args: process.argv.slice(2),
958
1263
  options: {
@@ -988,7 +1293,11 @@ async function main() {
988
1293
  newTpTriggerPx: { type: "string" },
989
1294
  newTpOrdPx: { type: "string" },
990
1295
  newSlTriggerPx: { type: "string" },
991
- newSlOrdPx: { type: "string" }
1296
+ newSlOrdPx: { type: "string" },
1297
+ // trailing stop
1298
+ callbackRatio: { type: "string" },
1299
+ callbackSpread: { type: "string" },
1300
+ activePx: { type: "string" }
992
1301
  },
993
1302
  allowPositionals: true
994
1303
  });
@@ -999,14 +1308,16 @@ async function main() {
999
1308
  const [module, action, ...rest] = positionals;
1000
1309
  const json = values.json ?? false;
1001
1310
  if (module === "config") {
1311
+ if (action === "init") return cmdConfigInit();
1002
1312
  if (action === "show") return cmdConfigShow(json);
1003
1313
  if (action === "set") return cmdConfigSet(rest[0], rest[1]);
1314
+ if (action === "setup-clients") return cmdSetupClients();
1004
1315
  process.stderr.write(`Unknown config command: ${action}
1005
1316
  `);
1006
1317
  process.exitCode = 1;
1007
1318
  return;
1008
1319
  }
1009
- const config = loadProfileConfig({ profile: values.profile });
1320
+ const config = loadProfileConfig({ profile: values.profile, userAgent: `okx-trade-cli/${CLI_VERSION}` });
1010
1321
  const client = new OkxRestClient(config);
1011
1322
  if (module === "market") {
1012
1323
  if (action === "ticker") return cmdMarketTicker(client, rest[0], json);
@@ -1111,6 +1422,19 @@ async function main() {
1111
1422
  });
1112
1423
  if (action === "algo") {
1113
1424
  const subAction = rest[0];
1425
+ if (subAction === "trail")
1426
+ return cmdSwapAlgoTrailPlace(client, {
1427
+ instId: values.instId,
1428
+ side: values.side,
1429
+ sz: values.sz,
1430
+ callbackRatio: values.callbackRatio,
1431
+ callbackSpread: values.callbackSpread,
1432
+ activePx: values.activePx,
1433
+ posSide: values.posSide,
1434
+ tdMode: values.tdMode ?? "cross",
1435
+ reduceOnly: values.reduceOnly,
1436
+ json
1437
+ });
1114
1438
  if (subAction === "place")
1115
1439
  return cmdSwapAlgoPlace(client, {
1116
1440
  instId: values.instId,
@@ -1155,8 +1479,12 @@ async function main() {
1155
1479
  main().catch((error) => {
1156
1480
  const payload = toToolErrorPayload(error);
1157
1481
  process.stderr.write(`Error: ${payload.message}
1482
+ `);
1483
+ if (payload.traceId) process.stderr.write(`TraceId: ${payload.traceId}
1158
1484
  `);
1159
1485
  if (payload.suggestion) process.stderr.write(`Hint: ${payload.suggestion}
1486
+ `);
1487
+ process.stderr.write(`Version: okx-trade-cli@${CLI_VERSION}
1160
1488
  `);
1161
1489
  process.exitCode = 1;
1162
1490
  });