@okx_ai/okx-trade-cli 1.3.0-beta.3 → 1.3.0-beta.5
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 +1092 -558
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +2 -111
package/dist/index.js
CHANGED
|
@@ -4,37 +4,25 @@
|
|
|
4
4
|
import { createRequire as createRequire3 } from "module";
|
|
5
5
|
|
|
6
6
|
// ../core/dist/index.js
|
|
7
|
-
import {
|
|
8
|
-
import { execFile } from "child_process";
|
|
9
|
-
import { homedir } from "os";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
import {
|
|
12
|
-
readFileSync,
|
|
13
|
-
writeFileSync,
|
|
14
|
-
mkdirSync,
|
|
15
|
-
unlinkSync,
|
|
16
|
-
renameSync
|
|
17
|
-
} from "fs";
|
|
18
|
-
import { homedir as homedir2 } from "os";
|
|
19
|
-
import { join as join2, dirname } from "path";
|
|
7
|
+
import { ProxyAgent } from "undici";
|
|
20
8
|
import { createHmac } from "crypto";
|
|
21
9
|
import fs from "fs";
|
|
22
10
|
import path from "path";
|
|
23
11
|
import os from "os";
|
|
24
|
-
import { writeFileSync
|
|
25
|
-
import { join
|
|
12
|
+
import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "fs";
|
|
13
|
+
import { join, resolve, basename, sep } from "path";
|
|
26
14
|
import { randomUUID } from "crypto";
|
|
27
15
|
import yauzl from "yauzl";
|
|
28
|
-
import { createWriteStream, mkdirSync as
|
|
29
|
-
import { resolve as resolve2, dirname
|
|
30
|
-
import { readFileSync
|
|
31
|
-
import { join as
|
|
32
|
-
import { readFileSync as
|
|
33
|
-
import { join as
|
|
34
|
-
import { homedir
|
|
35
|
-
import { readFileSync as
|
|
36
|
-
import { join as
|
|
37
|
-
import { homedir as
|
|
16
|
+
import { createWriteStream, mkdirSync as mkdirSync2 } from "fs";
|
|
17
|
+
import { resolve as resolve2, dirname } from "path";
|
|
18
|
+
import { readFileSync, existsSync } from "fs";
|
|
19
|
+
import { join as join2 } from "path";
|
|
20
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
21
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
22
|
+
import { homedir } from "os";
|
|
23
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "fs";
|
|
24
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
25
|
+
import { homedir as homedir2 } from "os";
|
|
38
26
|
|
|
39
27
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
40
28
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -863,137 +851,16 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
863
851
|
}
|
|
864
852
|
|
|
865
853
|
// ../core/dist/index.js
|
|
866
|
-
import { readFileSync as
|
|
867
|
-
import { join as
|
|
868
|
-
import { homedir as
|
|
854
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync4 } from "fs";
|
|
855
|
+
import { join as join5 } from "path";
|
|
856
|
+
import { homedir as homedir3 } from "os";
|
|
857
|
+
import fs2 from "fs";
|
|
858
|
+
import path2 from "path";
|
|
859
|
+
import os2 from "os";
|
|
869
860
|
import * as fs3 from "fs";
|
|
870
861
|
import * as path3 from "path";
|
|
871
862
|
import * as os3 from "os";
|
|
872
863
|
import { execFileSync } from "child_process";
|
|
873
|
-
var EXEC_TIMEOUT_MS = 3e4;
|
|
874
|
-
var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
875
|
-
function getDohBinaryPath() {
|
|
876
|
-
if (process.env.OKX_DOH_BINARY_PATH) {
|
|
877
|
-
return process.env.OKX_DOH_BINARY_PATH;
|
|
878
|
-
}
|
|
879
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
880
|
-
return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
|
|
881
|
-
}
|
|
882
|
-
function execDohBinary(domain, exclude = [], userAgent) {
|
|
883
|
-
const binPath = getDohBinaryPath();
|
|
884
|
-
const args = ["--domain", domain];
|
|
885
|
-
if (exclude.length > 0) {
|
|
886
|
-
args.push("--exclude", exclude.join(","));
|
|
887
|
-
}
|
|
888
|
-
if (userAgent) {
|
|
889
|
-
args.push("--user-agent", userAgent);
|
|
890
|
-
}
|
|
891
|
-
return new Promise((resolve3) => {
|
|
892
|
-
execFile(
|
|
893
|
-
binPath,
|
|
894
|
-
args,
|
|
895
|
-
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
896
|
-
(error, stdout) => {
|
|
897
|
-
if (error) {
|
|
898
|
-
resolve3(null);
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
try {
|
|
902
|
-
const result = JSON.parse(stdout);
|
|
903
|
-
if (result.code === 0 && result.data) {
|
|
904
|
-
resolve3(result.data);
|
|
905
|
-
} else {
|
|
906
|
-
resolve3(null);
|
|
907
|
-
}
|
|
908
|
-
} catch {
|
|
909
|
-
resolve3(null);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
);
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-node-cache.json");
|
|
916
|
-
function readCache(hostname, cachePath = DOH_CACHE_PATH) {
|
|
917
|
-
try {
|
|
918
|
-
const raw = readFileSync(cachePath, "utf-8");
|
|
919
|
-
const file = JSON.parse(raw);
|
|
920
|
-
return file[hostname] ?? null;
|
|
921
|
-
} catch {
|
|
922
|
-
return null;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
|
|
926
|
-
try {
|
|
927
|
-
const dir = dirname(cachePath);
|
|
928
|
-
mkdirSync(dir, { recursive: true });
|
|
929
|
-
let file = {};
|
|
930
|
-
try {
|
|
931
|
-
file = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
932
|
-
} catch {
|
|
933
|
-
}
|
|
934
|
-
file[hostname] = entry;
|
|
935
|
-
const tmpPath = `${cachePath}.tmp`;
|
|
936
|
-
writeFileSync(tmpPath, JSON.stringify(file));
|
|
937
|
-
renameSync(tmpPath, cachePath);
|
|
938
|
-
} catch {
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
|
|
942
|
-
function classifyAndCache(node, hostname, failedNodes) {
|
|
943
|
-
if (!node) {
|
|
944
|
-
if (failedNodes.length > 0) {
|
|
945
|
-
writeCache(hostname, {
|
|
946
|
-
mode: "direct",
|
|
947
|
-
node: null,
|
|
948
|
-
failedNodes,
|
|
949
|
-
updatedAt: Date.now()
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
return { mode: null, node: null };
|
|
953
|
-
}
|
|
954
|
-
if (node.ip === hostname || node.host === hostname) {
|
|
955
|
-
writeCache(hostname, {
|
|
956
|
-
mode: "direct",
|
|
957
|
-
node: null,
|
|
958
|
-
failedNodes,
|
|
959
|
-
updatedAt: Date.now()
|
|
960
|
-
});
|
|
961
|
-
return { mode: "direct", node: null };
|
|
962
|
-
}
|
|
963
|
-
writeCache(hostname, {
|
|
964
|
-
mode: "proxy",
|
|
965
|
-
node,
|
|
966
|
-
failedNodes,
|
|
967
|
-
updatedAt: Date.now()
|
|
968
|
-
});
|
|
969
|
-
return { mode: "proxy", node };
|
|
970
|
-
}
|
|
971
|
-
function getActiveFailedNodes(nodes) {
|
|
972
|
-
if (!nodes || nodes.length === 0) return [];
|
|
973
|
-
const now = Date.now();
|
|
974
|
-
return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
|
|
975
|
-
}
|
|
976
|
-
async function resolveDoh(hostname) {
|
|
977
|
-
const entry = readCache(hostname);
|
|
978
|
-
if (entry) {
|
|
979
|
-
if (entry.mode === "direct") {
|
|
980
|
-
return { mode: "direct", node: null };
|
|
981
|
-
}
|
|
982
|
-
if (entry.mode === "proxy" && entry.node) {
|
|
983
|
-
return { mode: "proxy", node: entry.node };
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
return { mode: null, node: null };
|
|
987
|
-
}
|
|
988
|
-
async function reResolveDoh(hostname, failedIp, userAgent) {
|
|
989
|
-
const entry = readCache(hostname);
|
|
990
|
-
const active = getActiveFailedNodes(entry?.failedNodes);
|
|
991
|
-
const now = Date.now();
|
|
992
|
-
const failedNodes = failedIp ? active.some((n) => n.ip === failedIp) ? active : [...active, { ip: failedIp, failedAt: now }] : active;
|
|
993
|
-
const excludeIps = failedNodes.map((n) => n.ip);
|
|
994
|
-
const node = await execDohBinary(hostname, excludeIps, userAgent);
|
|
995
|
-
return classifyAndCache(node, hostname, failedNodes);
|
|
996
|
-
}
|
|
997
864
|
function getNow() {
|
|
998
865
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
999
866
|
}
|
|
@@ -1212,14 +1079,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1212
1079
|
config;
|
|
1213
1080
|
rateLimiter;
|
|
1214
1081
|
dispatcher;
|
|
1215
|
-
// DoH proxy state (lazy-resolved on first request)
|
|
1216
|
-
dohResolved = false;
|
|
1217
|
-
dohRetried = false;
|
|
1218
|
-
directUnverified = false;
|
|
1219
|
-
// The first direct connection has not yet been verified
|
|
1220
|
-
dohNode = null;
|
|
1221
|
-
dohAgent = null;
|
|
1222
|
-
dohBaseUrl = null;
|
|
1223
1082
|
constructor(config) {
|
|
1224
1083
|
this.config = config;
|
|
1225
1084
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
@@ -1227,97 +1086,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1227
1086
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1228
1087
|
}
|
|
1229
1088
|
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Lazily resolve the DoH proxy node on the first request.
|
|
1232
|
-
* Uses cache-first strategy via the resolver.
|
|
1233
|
-
*/
|
|
1234
|
-
async ensureDoh() {
|
|
1235
|
-
if (this.dohResolved || this.dispatcher) return;
|
|
1236
|
-
this.dohResolved = true;
|
|
1237
|
-
try {
|
|
1238
|
-
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1239
|
-
const result = await resolveDoh(hostname);
|
|
1240
|
-
if (!result.mode) {
|
|
1241
|
-
this.directUnverified = true;
|
|
1242
|
-
if (this.config.verbose) {
|
|
1243
|
-
vlog("DoH: no cache, trying direct connection first");
|
|
1244
|
-
}
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
if (result.mode === "direct") {
|
|
1248
|
-
if (this.config.verbose) {
|
|
1249
|
-
vlog("DoH: mode=direct (overseas or cached), using direct connection");
|
|
1250
|
-
}
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
if (result.node) {
|
|
1254
|
-
this.applyDohNode(result.node, protocol);
|
|
1255
|
-
}
|
|
1256
|
-
} catch (err) {
|
|
1257
|
-
if (this.config.verbose) {
|
|
1258
|
-
const cause = err instanceof Error ? err.message : String(err);
|
|
1259
|
-
vlog(`DoH resolution failed, falling back to direct: ${cause}`);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
/** Apply a DoH node: set up the custom Agent + base URL. */
|
|
1264
|
-
applyDohNode(node, protocol) {
|
|
1265
|
-
this.dohNode = node;
|
|
1266
|
-
this.dohBaseUrl = `${protocol}//${node.host}`;
|
|
1267
|
-
this.dohAgent = new Agent({
|
|
1268
|
-
connect: {
|
|
1269
|
-
lookup: (_hostname, options, callback) => {
|
|
1270
|
-
if (options?.all) {
|
|
1271
|
-
callback(null, [{ address: node.ip, family: 4 }]);
|
|
1272
|
-
} else {
|
|
1273
|
-
callback(null, node.ip, 4);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
});
|
|
1278
|
-
if (this.config.verbose) {
|
|
1279
|
-
vlog(`DoH proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Handle network failure: re-resolve with --exclude and retry once.
|
|
1284
|
-
* Returns true if retry should proceed, false if already retried.
|
|
1285
|
-
*/
|
|
1286
|
-
async handleDohNetworkFailure() {
|
|
1287
|
-
if (this.dohRetried) return false;
|
|
1288
|
-
this.dohRetried = true;
|
|
1289
|
-
const failedIp = this.dohNode?.ip ?? "";
|
|
1290
|
-
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1291
|
-
this.dohNode = null;
|
|
1292
|
-
this.dohAgent = null;
|
|
1293
|
-
this.dohBaseUrl = null;
|
|
1294
|
-
if (!failedIp) this.directUnverified = false;
|
|
1295
|
-
if (this.config.verbose) {
|
|
1296
|
-
vlog(failedIp ? `DoH: proxy node ${failedIp} failed, re-resolving with --exclude` : "DoH: direct connection failed, calling binary for DoH resolution");
|
|
1297
|
-
}
|
|
1298
|
-
try {
|
|
1299
|
-
const result = await reResolveDoh(hostname, failedIp, this.dohUserAgent);
|
|
1300
|
-
if (result.mode === "proxy" && result.node) {
|
|
1301
|
-
this.applyDohNode(result.node, protocol);
|
|
1302
|
-
return true;
|
|
1303
|
-
}
|
|
1304
|
-
} catch {
|
|
1305
|
-
}
|
|
1306
|
-
if (this.config.verbose) {
|
|
1307
|
-
vlog("DoH: re-resolution failed or switched to direct, retrying with direct connection");
|
|
1308
|
-
}
|
|
1309
|
-
return true;
|
|
1310
|
-
}
|
|
1311
|
-
get activeBaseUrl() {
|
|
1312
|
-
return this.dohNode ? this.dohBaseUrl : this.config.baseUrl;
|
|
1313
|
-
}
|
|
1314
|
-
get activeDispatcher() {
|
|
1315
|
-
return this.dispatcher ?? this.dohAgent ?? void 0;
|
|
1316
|
-
}
|
|
1317
|
-
/** User-Agent for DoH proxy requests: OKX/@okx_ai/{packageName}/{version} */
|
|
1318
|
-
get dohUserAgent() {
|
|
1319
|
-
return `OKX/@okx_ai/${this.config.userAgent ?? "unknown"}`;
|
|
1320
|
-
}
|
|
1321
1089
|
logRequest(method, url, auth) {
|
|
1322
1090
|
if (!this.config.verbose) return;
|
|
1323
1091
|
vlog(`\u2192 ${method} ${url}`);
|
|
@@ -1332,13 +1100,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1332
1100
|
vlog(`\u2190 ${status} | code=${code ?? "0"} | ${rawLen}B | ${elapsed}ms | trace=${traceId ?? "-"}`);
|
|
1333
1101
|
}
|
|
1334
1102
|
}
|
|
1335
|
-
async publicGet(path42, query, rateLimit) {
|
|
1103
|
+
async publicGet(path42, query, rateLimit, simulatedTrading) {
|
|
1336
1104
|
return this.request({
|
|
1337
1105
|
method: "GET",
|
|
1338
1106
|
path: path42,
|
|
1339
1107
|
auth: "public",
|
|
1340
1108
|
query,
|
|
1341
|
-
rateLimit
|
|
1109
|
+
rateLimit,
|
|
1110
|
+
simulatedTrading
|
|
1342
1111
|
});
|
|
1343
1112
|
}
|
|
1344
1113
|
async privateGet(path42, query, rateLimit) {
|
|
@@ -1487,17 +1256,13 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1487
1256
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1488
1257
|
*/
|
|
1489
1258
|
async privatePostBinary(path42, body, opts) {
|
|
1490
|
-
await this.ensureDoh();
|
|
1491
1259
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1492
1260
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1493
1261
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1494
1262
|
const endpoint = `POST ${path42}`;
|
|
1495
|
-
this.logRequest("POST", `${this.
|
|
1263
|
+
this.logRequest("POST", `${this.config.baseUrl}${path42}`, "private");
|
|
1496
1264
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1497
1265
|
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1498
|
-
if (this.dohNode) {
|
|
1499
|
-
headers.set("User-Agent", this.dohUserAgent);
|
|
1500
|
-
}
|
|
1501
1266
|
const t0 = Date.now();
|
|
1502
1267
|
const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
|
|
1503
1268
|
const elapsed = Date.now() - t0;
|
|
@@ -1531,10 +1296,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1531
1296
|
method: "POST",
|
|
1532
1297
|
headers,
|
|
1533
1298
|
body: bodyJson || void 0,
|
|
1534
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1535
|
-
dispatcher: this.activeDispatcher
|
|
1299
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1536
1300
|
};
|
|
1537
|
-
|
|
1301
|
+
if (this.dispatcher) fetchOptions.dispatcher = this.dispatcher;
|
|
1302
|
+
return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
|
|
1538
1303
|
} catch (error) {
|
|
1539
1304
|
if (this.config.verbose) {
|
|
1540
1305
|
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1556,7 +1321,8 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1556
1321
|
if (reqConfig.auth === "private") {
|
|
1557
1322
|
this.setAuthHeaders(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1558
1323
|
}
|
|
1559
|
-
|
|
1324
|
+
const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
|
|
1325
|
+
if (useSimulated) {
|
|
1560
1326
|
headers.set("x-simulated-trading", "1");
|
|
1561
1327
|
}
|
|
1562
1328
|
return headers;
|
|
@@ -1565,10 +1331,9 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1565
1331
|
// JSON request
|
|
1566
1332
|
// ---------------------------------------------------------------------------
|
|
1567
1333
|
async request(reqConfig) {
|
|
1568
|
-
await this.ensureDoh();
|
|
1569
1334
|
const queryString = buildQueryString(reqConfig.query);
|
|
1570
1335
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1571
|
-
const url = `${this.
|
|
1336
|
+
const url = `${this.config.baseUrl}${requestPath}`;
|
|
1572
1337
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1573
1338
|
const timestamp = getNow();
|
|
1574
1339
|
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
@@ -1576,9 +1341,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1576
1341
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1577
1342
|
}
|
|
1578
1343
|
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1579
|
-
if (this.dohNode) {
|
|
1580
|
-
headers.set("User-Agent", this.dohUserAgent);
|
|
1581
|
-
}
|
|
1582
1344
|
const t0 = Date.now();
|
|
1583
1345
|
let response;
|
|
1584
1346
|
try {
|
|
@@ -1586,20 +1348,13 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1586
1348
|
method: reqConfig.method,
|
|
1587
1349
|
headers,
|
|
1588
1350
|
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1589
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1590
|
-
dispatcher: this.activeDispatcher
|
|
1351
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1591
1352
|
};
|
|
1353
|
+
if (this.dispatcher) {
|
|
1354
|
+
fetchOptions.dispatcher = this.dispatcher;
|
|
1355
|
+
}
|
|
1592
1356
|
response = await fetch(url, fetchOptions);
|
|
1593
1357
|
} catch (error) {
|
|
1594
|
-
if (!this.dohRetried) {
|
|
1595
|
-
if (this.config.verbose) {
|
|
1596
|
-
vlog(`Network failure, refreshing DoH: ${error instanceof Error ? error.message : String(error)}`);
|
|
1597
|
-
}
|
|
1598
|
-
const shouldRetry = await this.handleDohNetworkFailure();
|
|
1599
|
-
if (shouldRetry && reqConfig.method === "GET") {
|
|
1600
|
-
return this.request(reqConfig);
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
1358
|
if (this.config.verbose) {
|
|
1604
1359
|
const elapsed2 = Date.now() - t0;
|
|
1605
1360
|
const cause = error instanceof Error ? error.message : String(error);
|
|
@@ -1614,19 +1369,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1614
1369
|
const rawText = await response.text();
|
|
1615
1370
|
const elapsed = Date.now() - t0;
|
|
1616
1371
|
const traceId = extractTraceId(response.headers);
|
|
1617
|
-
if (this.directUnverified && !this.dohNode) {
|
|
1618
|
-
this.directUnverified = false;
|
|
1619
|
-
const { hostname: h } = new URL(this.config.baseUrl);
|
|
1620
|
-
writeCache(h, {
|
|
1621
|
-
mode: "direct",
|
|
1622
|
-
node: null,
|
|
1623
|
-
failedNodes: [],
|
|
1624
|
-
updatedAt: Date.now()
|
|
1625
|
-
});
|
|
1626
|
-
if (this.config.verbose) {
|
|
1627
|
-
vlog("DoH: direct connection succeeded, cached mode=direct");
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
1372
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1631
1373
|
}
|
|
1632
1374
|
};
|
|
@@ -1766,14 +1508,137 @@ var INDICATOR_BARS = [
|
|
|
1766
1508
|
"1Wutc"
|
|
1767
1509
|
];
|
|
1768
1510
|
var INDICATOR_CODE_OVERRIDES = {
|
|
1511
|
+
// Aliases
|
|
1512
|
+
"boll": "BB",
|
|
1513
|
+
// server supports BB not BOLL
|
|
1514
|
+
// Names where default rule produces underscores but backend uses no separator
|
|
1769
1515
|
"rainbow": "BTCRAINBOW",
|
|
1770
|
-
|
|
1516
|
+
// default: RAINBOW
|
|
1771
1517
|
"stoch-rsi": "STOCHRSI",
|
|
1772
|
-
|
|
1773
|
-
"
|
|
1774
|
-
//
|
|
1775
|
-
"
|
|
1518
|
+
// default: STOCH_RSI
|
|
1519
|
+
"bull-engulf": "BULLENGULF",
|
|
1520
|
+
// default: BULL_ENGULF
|
|
1521
|
+
"bear-engulf": "BEARENGULF",
|
|
1522
|
+
// default: BEAR_ENGULF
|
|
1523
|
+
"bull-harami": "BULLHARAMI",
|
|
1524
|
+
// default: BULL_HARAMI
|
|
1525
|
+
"bear-harami": "BEARHARAMI",
|
|
1526
|
+
// default: BEAR_HARAMI
|
|
1527
|
+
"bull-harami-cross": "BULLHARAMICROSS",
|
|
1528
|
+
// default: BULL_HARAMI_CROSS
|
|
1529
|
+
"bear-harami-cross": "BEARHARAMICROSS",
|
|
1530
|
+
// default: BEAR_HARAMI_CROSS
|
|
1531
|
+
"three-soldiers": "THREESOLDIERS",
|
|
1532
|
+
// default: THREE_SOLDIERS
|
|
1533
|
+
"three-crows": "THREECROWS",
|
|
1534
|
+
// default: THREE_CROWS
|
|
1535
|
+
"hanging-man": "HANGINGMAN",
|
|
1536
|
+
// default: HANGING_MAN
|
|
1537
|
+
"inverted-hammer": "INVERTEDH",
|
|
1538
|
+
// default: INVERTED_HAMMER (backend uses INVERTEDH)
|
|
1539
|
+
"shooting-star": "SHOOTINGSTAR",
|
|
1540
|
+
// default: SHOOTING_STAR
|
|
1541
|
+
"nvi-pvi": "NVIPVI",
|
|
1542
|
+
// default: NVI_PVI
|
|
1543
|
+
"top-long-short": "TOPLONGSHORT"
|
|
1544
|
+
// default: TOP_LONG_SHORT
|
|
1545
|
+
// Note: range-filter → RANGE_FILTER is correct via the default rule; no override needed.
|
|
1776
1546
|
};
|
|
1547
|
+
var KNOWN_INDICATORS = [
|
|
1548
|
+
// Moving Averages
|
|
1549
|
+
{ name: "ma", description: "Simple Moving Average" },
|
|
1550
|
+
{ name: "ema", description: "Exponential Moving Average" },
|
|
1551
|
+
{ name: "wma", description: "Weighted Moving Average" },
|
|
1552
|
+
{ name: "dema", description: "Double Exponential Moving Average" },
|
|
1553
|
+
{ name: "tema", description: "Triple Exponential Moving Average" },
|
|
1554
|
+
{ name: "zlema", description: "Zero-Lag Exponential Moving Average" },
|
|
1555
|
+
{ name: "hma", description: "Hull Moving Average" },
|
|
1556
|
+
{ name: "kama", description: "Kaufman Adaptive Moving Average" },
|
|
1557
|
+
// Trend
|
|
1558
|
+
{ name: "macd", description: "MACD" },
|
|
1559
|
+
{ name: "sar", description: "Parabolic SAR" },
|
|
1560
|
+
{ name: "adx", description: "Average Directional Index" },
|
|
1561
|
+
{ name: "aroon", description: "Aroon Indicator" },
|
|
1562
|
+
{ name: "cci", description: "Commodity Channel Index" },
|
|
1563
|
+
{ name: "dpo", description: "Detrended Price Oscillator" },
|
|
1564
|
+
{ name: "envelope", description: "Envelope" },
|
|
1565
|
+
{ name: "halftrend", description: "HalfTrend" },
|
|
1566
|
+
{ name: "alphatrend", description: "AlphaTrend" },
|
|
1567
|
+
// Momentum
|
|
1568
|
+
{ name: "rsi", description: "Relative Strength Index" },
|
|
1569
|
+
{ name: "stoch-rsi", description: "Stochastic RSI" },
|
|
1570
|
+
{ name: "stoch", description: "Stochastic Oscillator" },
|
|
1571
|
+
{ name: "roc", description: "Rate of Change" },
|
|
1572
|
+
{ name: "mom", description: "Momentum" },
|
|
1573
|
+
{ name: "ppo", description: "Price Percentage Oscillator" },
|
|
1574
|
+
{ name: "trix", description: "TRIX" },
|
|
1575
|
+
{ name: "ao", description: "Awesome Oscillator" },
|
|
1576
|
+
{ name: "uo", description: "Ultimate Oscillator" },
|
|
1577
|
+
{ name: "wr", description: "Williams %R" },
|
|
1578
|
+
// Volatility
|
|
1579
|
+
{ name: "bb", description: "Bollinger Bands" },
|
|
1580
|
+
{ name: "boll", description: "Bollinger Bands (alias for bb)" },
|
|
1581
|
+
{ name: "bbwidth", description: "Bollinger Band Width" },
|
|
1582
|
+
{ name: "bbpct", description: "Bollinger Band %B" },
|
|
1583
|
+
{ name: "atr", description: "Average True Range" },
|
|
1584
|
+
{ name: "keltner", description: "Keltner Channel" },
|
|
1585
|
+
{ name: "donchian", description: "Donchian Channel" },
|
|
1586
|
+
{ name: "hv", description: "Historical Volatility" },
|
|
1587
|
+
{ name: "stddev", description: "Standard Deviation" },
|
|
1588
|
+
// Volume
|
|
1589
|
+
{ name: "obv", description: "On-Balance Volume" },
|
|
1590
|
+
{ name: "vwap", description: "Volume Weighted Average Price" },
|
|
1591
|
+
{ name: "mvwap", description: "Moving VWAP" },
|
|
1592
|
+
{ name: "cmf", description: "Chaikin Money Flow" },
|
|
1593
|
+
{ name: "mfi", description: "Money Flow Index" },
|
|
1594
|
+
{ name: "ad", description: "Accumulation/Distribution" },
|
|
1595
|
+
// Statistical
|
|
1596
|
+
{ name: "lr", description: "Linear Regression" },
|
|
1597
|
+
{ name: "slope", description: "Linear Regression Slope" },
|
|
1598
|
+
{ name: "angle", description: "Linear Regression Angle" },
|
|
1599
|
+
{ name: "variance", description: "Variance" },
|
|
1600
|
+
{ name: "meandev", description: "Mean Deviation" },
|
|
1601
|
+
{ name: "sigma", description: "Sigma" },
|
|
1602
|
+
{ name: "stderr", description: "Standard Error" },
|
|
1603
|
+
// Custom
|
|
1604
|
+
{ name: "kdj", description: "KDJ Stochastic Oscillator" },
|
|
1605
|
+
{ name: "supertrend", description: "Supertrend" },
|
|
1606
|
+
// Ichimoku
|
|
1607
|
+
{ name: "tenkan", description: "Ichimoku Tenkan-sen (Conversion Line)" },
|
|
1608
|
+
{ name: "kijun", description: "Ichimoku Kijun-sen (Base Line)" },
|
|
1609
|
+
{ name: "senkoa", description: "Ichimoku Senkou Span A (Leading Span A)" },
|
|
1610
|
+
{ name: "senkob", description: "Ichimoku Senkou Span B (Leading Span B)" },
|
|
1611
|
+
{ name: "chikou", description: "Ichimoku Chikou Span (Lagging Span)" },
|
|
1612
|
+
// Candlestick Patterns
|
|
1613
|
+
{ name: "doji", description: "Doji candlestick pattern" },
|
|
1614
|
+
{ name: "bull-engulf", description: "Bullish Engulfing pattern" },
|
|
1615
|
+
{ name: "bear-engulf", description: "Bearish Engulfing pattern" },
|
|
1616
|
+
{ name: "bull-harami", description: "Bullish Harami pattern" },
|
|
1617
|
+
{ name: "bear-harami", description: "Bearish Harami pattern" },
|
|
1618
|
+
{ name: "bull-harami-cross", description: "Bullish Harami Cross pattern" },
|
|
1619
|
+
{ name: "bear-harami-cross", description: "Bearish Harami Cross pattern" },
|
|
1620
|
+
{ name: "three-soldiers", description: "Three White Soldiers pattern" },
|
|
1621
|
+
{ name: "three-crows", description: "Three Black Crows pattern" },
|
|
1622
|
+
{ name: "hanging-man", description: "Hanging Man pattern" },
|
|
1623
|
+
{ name: "inverted-hammer", description: "Inverted Hammer pattern" },
|
|
1624
|
+
{ name: "shooting-star", description: "Shooting Star pattern" },
|
|
1625
|
+
// Bitcoin On-Chain
|
|
1626
|
+
{ name: "ahr999", description: "AHR999 Bitcoin accumulation index" },
|
|
1627
|
+
{ name: "rainbow", description: "Bitcoin Rainbow Chart" },
|
|
1628
|
+
// Other
|
|
1629
|
+
{ name: "fisher", description: "Fisher Transform" },
|
|
1630
|
+
{ name: "nvi-pvi", description: "Negative/Positive Volume Index (returns both)" },
|
|
1631
|
+
{ name: "pmax", description: "PMAX" },
|
|
1632
|
+
{ name: "qqe", description: "QQE Mod" },
|
|
1633
|
+
{ name: "tdi", description: "Traders Dynamic Index" },
|
|
1634
|
+
{ name: "waddah", description: "Waddah Attar Explosion" },
|
|
1635
|
+
{ name: "range-filter", description: "Range Filter" },
|
|
1636
|
+
{ name: "cho", description: "Chande Momentum Oscillator" },
|
|
1637
|
+
{ name: "tr", description: "True Range" },
|
|
1638
|
+
{ name: "tp", description: "Typical Price" },
|
|
1639
|
+
{ name: "mp", description: "Median Price" },
|
|
1640
|
+
{ name: "top-long-short", description: "Top Trader Long/Short Ratio (timeframe-independent)" }
|
|
1641
|
+
];
|
|
1777
1642
|
function resolveIndicatorCode(name) {
|
|
1778
1643
|
const lower = name.toLowerCase();
|
|
1779
1644
|
return INDICATOR_CODE_OVERRIDES[lower] ?? name.toUpperCase().replace(/-/g, "_");
|
|
@@ -1800,7 +1665,7 @@ function registerIndicatorTools() {
|
|
|
1800
1665
|
},
|
|
1801
1666
|
indicator: {
|
|
1802
1667
|
type: "string",
|
|
1803
|
-
description: "Indicator name (case-insensitive).
|
|
1668
|
+
description: "Indicator name (case-insensitive). Call market_list_indicators to see all supported names."
|
|
1804
1669
|
},
|
|
1805
1670
|
bar: {
|
|
1806
1671
|
type: "string",
|
|
@@ -1857,6 +1722,14 @@ function registerIndicatorTools() {
|
|
|
1857
1722
|
);
|
|
1858
1723
|
return normalizeResponse(response);
|
|
1859
1724
|
}
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
name: "market_list_indicators",
|
|
1728
|
+
module: "market",
|
|
1729
|
+
description: "List all supported technical indicator names and descriptions. Call this before market_get_indicator to discover valid indicator names. No credentials required.",
|
|
1730
|
+
isWrite: false,
|
|
1731
|
+
inputSchema: { type: "object", properties: {} },
|
|
1732
|
+
handler: async () => ({ data: KNOWN_INDICATORS })
|
|
1860
1733
|
}
|
|
1861
1734
|
];
|
|
1862
1735
|
}
|
|
@@ -2454,6 +2327,118 @@ function registerAccountTools() {
|
|
|
2454
2327
|
}
|
|
2455
2328
|
];
|
|
2456
2329
|
}
|
|
2330
|
+
function extractInstrumentParams(instId, data) {
|
|
2331
|
+
const instruments = Array.isArray(data) ? data : [];
|
|
2332
|
+
if (instruments.length === 0) {
|
|
2333
|
+
throw new Error(`Failed to fetch instrument info for ${instId}: empty instrument list. Cannot determine ctVal for conversion.`);
|
|
2334
|
+
}
|
|
2335
|
+
const inst = instruments[0];
|
|
2336
|
+
const ctValStr = String(inst.ctVal ?? "");
|
|
2337
|
+
const ctVal = parseFloat(ctValStr);
|
|
2338
|
+
if (!isFinite(ctVal) || ctVal <= 0) {
|
|
2339
|
+
throw new Error(`Invalid ctVal "${ctValStr}" for ${instId}. ctVal must be a positive number for conversion.`);
|
|
2340
|
+
}
|
|
2341
|
+
const minSzStr = String(inst.minSz ?? "1");
|
|
2342
|
+
const minSz = parseFloat(minSzStr);
|
|
2343
|
+
if (!isFinite(minSz) || minSz <= 0) {
|
|
2344
|
+
throw new Error(`Invalid minSz "${minSzStr}" for ${instId}. minSz must be a positive number for conversion.`);
|
|
2345
|
+
}
|
|
2346
|
+
const lotSzStr = String(inst.lotSz ?? "1");
|
|
2347
|
+
const lotSz = parseFloat(lotSzStr);
|
|
2348
|
+
if (!isFinite(lotSz) || lotSz <= 0) {
|
|
2349
|
+
throw new Error(`Invalid lotSz "${lotSzStr}" for ${instId}. lotSz must be a positive number for conversion.`);
|
|
2350
|
+
}
|
|
2351
|
+
return { ctVal, ctValStr, minSz, minSzStr, lotSz, lotSzStr };
|
|
2352
|
+
}
|
|
2353
|
+
function extractLastPx(instId, data) {
|
|
2354
|
+
const tickers = Array.isArray(data) ? data : [];
|
|
2355
|
+
if (tickers.length === 0) {
|
|
2356
|
+
throw new Error(`Failed to fetch ticker price for ${instId}: empty ticker response. Cannot determine last price for conversion.`);
|
|
2357
|
+
}
|
|
2358
|
+
const lastStr = String(tickers[0].last ?? "");
|
|
2359
|
+
const lastPx = parseFloat(lastStr);
|
|
2360
|
+
if (!isFinite(lastPx) || lastPx <= 0) {
|
|
2361
|
+
throw new Error(`Invalid last price "${lastStr}" for ${instId}. Last price must be a positive number for conversion.`);
|
|
2362
|
+
}
|
|
2363
|
+
return { lastPx, lastStr };
|
|
2364
|
+
}
|
|
2365
|
+
function extractLeverage(instId, mgnMode, data) {
|
|
2366
|
+
const leverageData = Array.isArray(data) ? data : [];
|
|
2367
|
+
if (leverageData.length === 0) {
|
|
2368
|
+
throw new Error(
|
|
2369
|
+
`Failed to fetch leverage info for ${instId} (mgnMode=${mgnMode}): empty response. Cannot determine leverage for margin conversion. Please set leverage first using set_leverage.`
|
|
2370
|
+
);
|
|
2371
|
+
}
|
|
2372
|
+
const leverStr = String(leverageData[0].lever ?? "1");
|
|
2373
|
+
const lever = parseFloat(leverStr);
|
|
2374
|
+
if (!isFinite(lever) || lever <= 0) {
|
|
2375
|
+
throw new Error(`Invalid leverage "${leverStr}" for ${instId}. Leverage must be a positive number for margin conversion.`);
|
|
2376
|
+
}
|
|
2377
|
+
return { lever, leverStr };
|
|
2378
|
+
}
|
|
2379
|
+
function computeContracts(p) {
|
|
2380
|
+
const { instId, sz, isMarginMode, inst, lastPx, lastStr, lever, leverStr } = p;
|
|
2381
|
+
const { ctVal, ctValStr, minSz, minSzStr, lotSz, lotSzStr } = inst;
|
|
2382
|
+
const userAmount = parseFloat(sz);
|
|
2383
|
+
const contractValue = ctVal * lastPx;
|
|
2384
|
+
const effectiveNotional = isMarginMode ? userAmount * lever : userAmount;
|
|
2385
|
+
const lotSzDecimals = lotSzStr.includes(".") ? lotSzStr.split(".")[1].length : 0;
|
|
2386
|
+
const precision = 10 ** (lotSzDecimals + 4);
|
|
2387
|
+
const rawContracts = Math.round(effectiveNotional / contractValue * precision) / precision;
|
|
2388
|
+
const rawLots = Math.round(rawContracts / lotSz * precision) / precision;
|
|
2389
|
+
const contractsRounded = parseFloat((Math.floor(rawLots) * lotSz).toFixed(lotSzDecimals));
|
|
2390
|
+
if (contractsRounded < minSz) {
|
|
2391
|
+
const minAmount = isMarginMode ? (minSz * contractValue / lever).toFixed(2) : (minSz * contractValue).toFixed(2);
|
|
2392
|
+
const unit = isMarginMode ? "USDT margin" : "USDT";
|
|
2393
|
+
throw new Error(
|
|
2394
|
+
`sz=${sz} ${unit} is too small for ${instId}. Minimum order size is ${minSzStr} contracts (\u2248 ${minAmount} ${unit}). (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr}` + (isMarginMode ? `, lever=${leverStr}` : "") + `).
|
|
2395
|
+
`
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
const contractsStr = contractsRounded.toFixed(lotSzDecimals);
|
|
2399
|
+
const conversionNote = isMarginMode ? `Converting ${sz} USDT margin (${leverStr}x leverage) \u2192 ${contractsStr} contracts (notional value \u2248 ${(contractsRounded * contractValue).toFixed(2)} USDT, ctVal=${ctValStr}, lastPx=${lastStr}, lever=${leverStr}, minSz=${minSzStr}, lotSz=${lotSzStr})` : `Converting ${sz} USDT \u2192 ${contractsStr} contracts (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr})`;
|
|
2400
|
+
return { contractsStr, conversionNote };
|
|
2401
|
+
}
|
|
2402
|
+
async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
|
|
2403
|
+
if (tgtCcy === void 0 || tgtCcy === "base_ccy") {
|
|
2404
|
+
return { sz, tgtCcy, conversionNote: void 0 };
|
|
2405
|
+
}
|
|
2406
|
+
if (tgtCcy !== "quote_ccy" && tgtCcy !== "margin") {
|
|
2407
|
+
throw new ValidationError(
|
|
2408
|
+
`Unknown tgtCcy value "${tgtCcy}". Valid values: base_ccy, quote_ccy, margin.`,
|
|
2409
|
+
`Check the --tgtCcy flag. Use base_ccy (default, sz in contracts), quote_ccy (sz in USDT notional), or margin (sz in USDT margin cost).`
|
|
2410
|
+
);
|
|
2411
|
+
}
|
|
2412
|
+
const isMarginMode = tgtCcy === "margin";
|
|
2413
|
+
if (isMarginMode && !tdMode) {
|
|
2414
|
+
throw new Error(
|
|
2415
|
+
"tdMode (cross or isolated) is required when tgtCcy=margin. Cannot determine leverage without knowing the margin mode."
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
const mgnMode = tdMode === "cross" ? "cross" : "isolated";
|
|
2419
|
+
const fetchPromises = [
|
|
2420
|
+
client.publicGet("/api/v5/public/instruments", { instType, instId }),
|
|
2421
|
+
client.publicGet("/api/v5/market/ticker", { instId })
|
|
2422
|
+
];
|
|
2423
|
+
if (isMarginMode) {
|
|
2424
|
+
fetchPromises.push(client.privateGet("/api/v5/account/leverage-info", { instId, mgnMode }));
|
|
2425
|
+
}
|
|
2426
|
+
const results = await Promise.all(fetchPromises);
|
|
2427
|
+
const inst = extractInstrumentParams(instId, results[0].data);
|
|
2428
|
+
const { lastPx, lastStr } = extractLastPx(instId, results[1].data);
|
|
2429
|
+
const { lever, leverStr } = isMarginMode ? extractLeverage(instId, mgnMode, results[2].data) : { lever: 1, leverStr: "1" };
|
|
2430
|
+
const { contractsStr, conversionNote } = computeContracts({
|
|
2431
|
+
instId,
|
|
2432
|
+
sz,
|
|
2433
|
+
isMarginMode,
|
|
2434
|
+
inst,
|
|
2435
|
+
lastPx,
|
|
2436
|
+
lastStr,
|
|
2437
|
+
lever,
|
|
2438
|
+
leverStr
|
|
2439
|
+
});
|
|
2440
|
+
return { sz: contractsStr, tgtCcy: void 0, conversionNote };
|
|
2441
|
+
}
|
|
2457
2442
|
function registerAlgoTradeTools() {
|
|
2458
2443
|
return [
|
|
2459
2444
|
{
|
|
@@ -2532,8 +2517,8 @@ function registerAlgoTradeTools() {
|
|
|
2532
2517
|
},
|
|
2533
2518
|
tgtCcy: {
|
|
2534
2519
|
type: "string",
|
|
2535
|
-
enum: ["base_ccy", "quote_ccy"],
|
|
2536
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
2520
|
+
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
2521
|
+
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
2537
2522
|
},
|
|
2538
2523
|
reduceOnly: {
|
|
2539
2524
|
type: "boolean",
|
|
@@ -2549,6 +2534,14 @@ function registerAlgoTradeTools() {
|
|
|
2549
2534
|
handler: async (rawArgs, context) => {
|
|
2550
2535
|
const args = asRecord(rawArgs);
|
|
2551
2536
|
const reduceOnly = args.reduceOnly;
|
|
2537
|
+
const resolved = await resolveQuoteCcySz(
|
|
2538
|
+
requireString(args, "instId"),
|
|
2539
|
+
requireString(args, "sz"),
|
|
2540
|
+
readString(args, "tgtCcy"),
|
|
2541
|
+
"SWAP",
|
|
2542
|
+
context.client,
|
|
2543
|
+
readString(args, "tdMode")
|
|
2544
|
+
);
|
|
2552
2545
|
const response = await context.client.privatePost(
|
|
2553
2546
|
"/api/v5/trade/order-algo",
|
|
2554
2547
|
compactObject({
|
|
@@ -2557,8 +2550,8 @@ function registerAlgoTradeTools() {
|
|
|
2557
2550
|
side: requireString(args, "side"),
|
|
2558
2551
|
posSide: readString(args, "posSide"),
|
|
2559
2552
|
ordType: requireString(args, "ordType"),
|
|
2560
|
-
sz:
|
|
2561
|
-
tgtCcy:
|
|
2553
|
+
sz: resolved.sz,
|
|
2554
|
+
tgtCcy: resolved.tgtCcy,
|
|
2562
2555
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2563
2556
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2564
2557
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2574,7 +2567,11 @@ function registerAlgoTradeTools() {
|
|
|
2574
2567
|
}),
|
|
2575
2568
|
privateRateLimit("swap_place_algo_order", 20)
|
|
2576
2569
|
);
|
|
2577
|
-
|
|
2570
|
+
const result = normalizeResponse(response);
|
|
2571
|
+
if (resolved.conversionNote) {
|
|
2572
|
+
result._conversion = resolved.conversionNote;
|
|
2573
|
+
}
|
|
2574
|
+
return result;
|
|
2578
2575
|
}
|
|
2579
2576
|
},
|
|
2580
2577
|
{
|
|
@@ -2865,8 +2862,8 @@ function registerFuturesAlgoTools() {
|
|
|
2865
2862
|
},
|
|
2866
2863
|
tgtCcy: {
|
|
2867
2864
|
type: "string",
|
|
2868
|
-
enum: ["base_ccy", "quote_ccy"],
|
|
2869
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
2865
|
+
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
2866
|
+
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
2870
2867
|
},
|
|
2871
2868
|
reduceOnly: {
|
|
2872
2869
|
type: "boolean",
|
|
@@ -2882,6 +2879,14 @@ function registerFuturesAlgoTools() {
|
|
|
2882
2879
|
handler: async (rawArgs, context) => {
|
|
2883
2880
|
const args = asRecord(rawArgs);
|
|
2884
2881
|
const reduceOnly = args.reduceOnly;
|
|
2882
|
+
const resolved = await resolveQuoteCcySz(
|
|
2883
|
+
requireString(args, "instId"),
|
|
2884
|
+
requireString(args, "sz"),
|
|
2885
|
+
readString(args, "tgtCcy"),
|
|
2886
|
+
"FUTURES",
|
|
2887
|
+
context.client,
|
|
2888
|
+
readString(args, "tdMode")
|
|
2889
|
+
);
|
|
2885
2890
|
const response = await context.client.privatePost(
|
|
2886
2891
|
"/api/v5/trade/order-algo",
|
|
2887
2892
|
compactObject({
|
|
@@ -2890,8 +2895,8 @@ function registerFuturesAlgoTools() {
|
|
|
2890
2895
|
side: requireString(args, "side"),
|
|
2891
2896
|
posSide: readString(args, "posSide"),
|
|
2892
2897
|
ordType: requireString(args, "ordType"),
|
|
2893
|
-
sz:
|
|
2894
|
-
tgtCcy:
|
|
2898
|
+
sz: resolved.sz,
|
|
2899
|
+
tgtCcy: resolved.tgtCcy,
|
|
2895
2900
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2896
2901
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2897
2902
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2907,7 +2912,11 @@ function registerFuturesAlgoTools() {
|
|
|
2907
2912
|
}),
|
|
2908
2913
|
privateRateLimit("futures_place_algo_order", 20)
|
|
2909
2914
|
);
|
|
2910
|
-
|
|
2915
|
+
const result = normalizeResponse(response);
|
|
2916
|
+
if (resolved.conversionNote) {
|
|
2917
|
+
result._conversion = resolved.conversionNote;
|
|
2918
|
+
}
|
|
2919
|
+
return result;
|
|
2911
2920
|
}
|
|
2912
2921
|
},
|
|
2913
2922
|
{
|
|
@@ -3238,19 +3247,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3238
3247
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3239
3248
|
}
|
|
3240
3249
|
const resolvedDir = resolve(targetDir);
|
|
3241
|
-
const filePath =
|
|
3250
|
+
const filePath = join(resolvedDir, safeName);
|
|
3242
3251
|
const resolvedPath = resolve(filePath);
|
|
3243
3252
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3244
3253
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
3245
3254
|
}
|
|
3246
|
-
|
|
3255
|
+
mkdirSync(resolvedDir, { recursive: true });
|
|
3247
3256
|
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3248
3257
|
try {
|
|
3249
|
-
|
|
3250
|
-
|
|
3258
|
+
writeFileSync(tmpPath, data);
|
|
3259
|
+
renameSync(tmpPath, resolvedPath);
|
|
3251
3260
|
} catch (err) {
|
|
3252
3261
|
try {
|
|
3253
|
-
|
|
3262
|
+
unlinkSync(tmpPath);
|
|
3254
3263
|
} catch {
|
|
3255
3264
|
}
|
|
3256
3265
|
throw err;
|
|
@@ -3266,13 +3275,14 @@ function validateZipEntryPath(targetDir, entryName) {
|
|
|
3266
3275
|
return resolvedEntry;
|
|
3267
3276
|
}
|
|
3268
3277
|
var MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
3269
|
-
async function downloadSkillZip(client, name, targetDir) {
|
|
3278
|
+
async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
3270
3279
|
const result = await client.privatePostBinary(
|
|
3271
3280
|
"/api/v5/skill/download",
|
|
3272
3281
|
{ name },
|
|
3273
3282
|
{ maxBytes: MAX_DOWNLOAD_BYTES }
|
|
3274
3283
|
);
|
|
3275
|
-
const
|
|
3284
|
+
const ext = format === "skill" ? "skill" : "zip";
|
|
3285
|
+
const fileName = `${name}.${ext}`;
|
|
3276
3286
|
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
3277
3287
|
return filePath;
|
|
3278
3288
|
}
|
|
@@ -3315,7 +3325,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3315
3325
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
3316
3326
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
3317
3327
|
const resolvedTarget = resolve2(targetDir);
|
|
3318
|
-
|
|
3328
|
+
mkdirSync2(resolvedTarget, { recursive: true });
|
|
3319
3329
|
return new Promise((resolvePromise, reject) => {
|
|
3320
3330
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
3321
3331
|
if (err) return reject(err);
|
|
@@ -3338,7 +3348,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3338
3348
|
zipfile.close();
|
|
3339
3349
|
return reject(streamErr);
|
|
3340
3350
|
}
|
|
3341
|
-
|
|
3351
|
+
mkdirSync2(dirname(resolvedPath), { recursive: true });
|
|
3342
3352
|
const writeStream = createWriteStream(resolvedPath);
|
|
3343
3353
|
readStream.pipe(writeStream);
|
|
3344
3354
|
writeStream.on("close", () => zipfile.readEntry());
|
|
@@ -3354,11 +3364,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3354
3364
|
});
|
|
3355
3365
|
}
|
|
3356
3366
|
function readMetaJson(contentDir) {
|
|
3357
|
-
const metaPath =
|
|
3367
|
+
const metaPath = join2(contentDir, "_meta.json");
|
|
3358
3368
|
if (!existsSync(metaPath)) {
|
|
3359
3369
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3360
3370
|
}
|
|
3361
|
-
const raw =
|
|
3371
|
+
const raw = readFileSync(metaPath, "utf-8");
|
|
3362
3372
|
let parsed;
|
|
3363
3373
|
try {
|
|
3364
3374
|
parsed = JSON.parse(raw);
|
|
@@ -3380,26 +3390,26 @@ function readMetaJson(contentDir) {
|
|
|
3380
3390
|
};
|
|
3381
3391
|
}
|
|
3382
3392
|
function validateSkillMdExists(contentDir) {
|
|
3383
|
-
const skillMdPath =
|
|
3393
|
+
const skillMdPath = join2(contentDir, "SKILL.md");
|
|
3384
3394
|
if (!existsSync(skillMdPath)) {
|
|
3385
3395
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3386
3396
|
}
|
|
3387
3397
|
}
|
|
3388
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3398
|
+
var DEFAULT_REGISTRY_PATH = join3(homedir(), ".okx", "skills", "registry.json");
|
|
3389
3399
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3390
3400
|
if (!existsSync2(registryPath)) {
|
|
3391
3401
|
return { version: 1, skills: {} };
|
|
3392
3402
|
}
|
|
3393
3403
|
try {
|
|
3394
|
-
const raw =
|
|
3404
|
+
const raw = readFileSync2(registryPath, "utf-8");
|
|
3395
3405
|
return JSON.parse(raw);
|
|
3396
3406
|
} catch {
|
|
3397
3407
|
return { version: 1, skills: {} };
|
|
3398
3408
|
}
|
|
3399
3409
|
}
|
|
3400
3410
|
function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3401
|
-
|
|
3402
|
-
|
|
3411
|
+
mkdirSync3(dirname2(registryPath), { recursive: true });
|
|
3412
|
+
writeFileSync2(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
3403
3413
|
}
|
|
3404
3414
|
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3405
3415
|
const registry = readRegistry(registryPath);
|
|
@@ -3472,7 +3482,7 @@ function registerSkillsTools() {
|
|
|
3472
3482
|
{
|
|
3473
3483
|
name: "skills_download",
|
|
3474
3484
|
module: "skills",
|
|
3475
|
-
description: "Download a skill
|
|
3485
|
+
description: "Download a skill package from OKX Skills Marketplace to a local directory. Always call skills_search first to confirm the skill name exists. Downloads the latest approved version. NOTE: Downloads third-party developer content \u2014 does NOT install to agents. For full installation use CLI: okx skill add <name>. Use when the user wants to inspect or manually install a skill package.",
|
|
3476
3486
|
inputSchema: {
|
|
3477
3487
|
type: "object",
|
|
3478
3488
|
properties: {
|
|
@@ -3482,7 +3492,12 @@ function registerSkillsTools() {
|
|
|
3482
3492
|
},
|
|
3483
3493
|
targetDir: {
|
|
3484
3494
|
type: "string",
|
|
3485
|
-
description: "Directory path where the
|
|
3495
|
+
description: "Directory path where the file will be saved"
|
|
3496
|
+
},
|
|
3497
|
+
format: {
|
|
3498
|
+
type: "string",
|
|
3499
|
+
description: "Output file format: 'zip' or 'skill' (default: 'skill')",
|
|
3500
|
+
enum: ["zip", "skill"]
|
|
3486
3501
|
}
|
|
3487
3502
|
},
|
|
3488
3503
|
required: ["name", "targetDir"],
|
|
@@ -3515,7 +3530,8 @@ async function handleSearch(args, ctx) {
|
|
|
3515
3530
|
async function handleDownload(args, ctx) {
|
|
3516
3531
|
const name = String(args.name);
|
|
3517
3532
|
const targetDir = String(args.targetDir);
|
|
3518
|
-
const
|
|
3533
|
+
const format = args.format === "zip" ? "zip" : "skill";
|
|
3534
|
+
const filePath = await downloadSkillZip(ctx.client, name, targetDir, format);
|
|
3519
3535
|
return {
|
|
3520
3536
|
endpoint: "POST /api/v5/skill/download",
|
|
3521
3537
|
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4078,7 +4094,7 @@ function registerEarnTools() {
|
|
|
4078
4094
|
{
|
|
4079
4095
|
name: "earn_get_savings_balance",
|
|
4080
4096
|
module: "earn.savings",
|
|
4081
|
-
description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings
|
|
4097
|
+
description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history. earn_get_lending_rate_history also returns fixed-term (\u5B9A\u671F) product offers, so one call gives a complete view of both flexible and fixed options. Do NOT use for fixed-term (\u5B9A\u671F) order queries \u2014 use earn_get_fixed_order_list instead.",
|
|
4082
4098
|
isWrite: false,
|
|
4083
4099
|
inputSchema: {
|
|
4084
4100
|
type: "object",
|
|
@@ -4099,6 +4115,43 @@ function registerEarnTools() {
|
|
|
4099
4115
|
return normalizeResponse(response);
|
|
4100
4116
|
}
|
|
4101
4117
|
},
|
|
4118
|
+
{
|
|
4119
|
+
name: "earn_get_fixed_order_list",
|
|
4120
|
+
module: "earn.savings",
|
|
4121
|
+
description: "Get Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) lending order list. Returns orders sorted by creation time descending. Use this to check status of fixed-term lending orders (pending/earning/expired/settled/cancelled). Do NOT use for flexible earn balance \u2014 use earn_get_savings_balance instead. If the result is empty, do NOT display any fixed-term section in the output.",
|
|
4122
|
+
isWrite: false,
|
|
4123
|
+
inputSchema: {
|
|
4124
|
+
type: "object",
|
|
4125
|
+
properties: {
|
|
4126
|
+
ccy: {
|
|
4127
|
+
type: "string",
|
|
4128
|
+
description: "Currency, e.g. USDT. Omit for all."
|
|
4129
|
+
},
|
|
4130
|
+
state: {
|
|
4131
|
+
type: "string",
|
|
4132
|
+
description: "Order state: pending (\u5339\u914D\u4E2D), earning (\u8D5A\u5E01\u4E2D), expired (\u903E\u671F), settled (\u5DF2\u7ED3\u7B97), cancelled (\u5DF2\u64A4\u9500). Omit for all."
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
},
|
|
4136
|
+
handler: async (rawArgs, context) => {
|
|
4137
|
+
const args = asRecord(rawArgs);
|
|
4138
|
+
const response = await context.client.privateGet(
|
|
4139
|
+
"/api/v5/finance/simple-earn-fixed/order-list",
|
|
4140
|
+
compactObject({
|
|
4141
|
+
ccy: readString(args, "ccy"),
|
|
4142
|
+
state: readString(args, "state")
|
|
4143
|
+
}),
|
|
4144
|
+
privateRateLimit("earn_get_fixed_order_list", 3)
|
|
4145
|
+
);
|
|
4146
|
+
const result = normalizeResponse(response);
|
|
4147
|
+
if (Array.isArray(result["data"])) {
|
|
4148
|
+
result["data"] = result["data"].map(
|
|
4149
|
+
({ finalSettlementDate: _, ...rest }) => rest
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
return result;
|
|
4153
|
+
}
|
|
4154
|
+
},
|
|
4102
4155
|
{
|
|
4103
4156
|
name: "earn_savings_purchase",
|
|
4104
4157
|
module: "earn.savings",
|
|
@@ -4243,10 +4296,112 @@ function registerEarnTools() {
|
|
|
4243
4296
|
return normalizeResponse(response);
|
|
4244
4297
|
}
|
|
4245
4298
|
},
|
|
4299
|
+
{
|
|
4300
|
+
name: "earn_fixed_purchase",
|
|
4301
|
+
module: "earn.savings",
|
|
4302
|
+
description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation \u2014 do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity \u2014 no early redemption allowed.",
|
|
4303
|
+
isWrite: true,
|
|
4304
|
+
inputSchema: {
|
|
4305
|
+
type: "object",
|
|
4306
|
+
properties: {
|
|
4307
|
+
ccy: {
|
|
4308
|
+
type: "string",
|
|
4309
|
+
description: "Currency, e.g. USDT"
|
|
4310
|
+
},
|
|
4311
|
+
amt: {
|
|
4312
|
+
type: "string",
|
|
4313
|
+
description: "Purchase amount"
|
|
4314
|
+
},
|
|
4315
|
+
term: {
|
|
4316
|
+
type: "string",
|
|
4317
|
+
description: "Term, e.g. 90D"
|
|
4318
|
+
},
|
|
4319
|
+
confirm: {
|
|
4320
|
+
type: "boolean",
|
|
4321
|
+
description: "Omit or false on the first call to preview the purchase details; set to true on the second call to execute after user confirms."
|
|
4322
|
+
}
|
|
4323
|
+
},
|
|
4324
|
+
required: ["ccy", "amt", "term"]
|
|
4325
|
+
},
|
|
4326
|
+
handler: async (rawArgs, context) => {
|
|
4327
|
+
const args = asRecord(rawArgs);
|
|
4328
|
+
const ccy = requireString(args, "ccy");
|
|
4329
|
+
const amt = requireString(args, "amt");
|
|
4330
|
+
const term = requireString(args, "term");
|
|
4331
|
+
const confirm = readBoolean(args, "confirm") ?? false;
|
|
4332
|
+
if (!confirm) {
|
|
4333
|
+
const [rateResponse, fixedResponse] = await Promise.all([
|
|
4334
|
+
context.client.publicGet(
|
|
4335
|
+
"/api/v5/finance/savings/lending-rate-history",
|
|
4336
|
+
compactObject({ ccy, limit: 1 }),
|
|
4337
|
+
publicRateLimit("earn_get_lending_rate_history", 6)
|
|
4338
|
+
),
|
|
4339
|
+
context.client.privateGet(
|
|
4340
|
+
"/api/v5/finance/simple-earn-fixed/offers",
|
|
4341
|
+
compactObject({ ccy }),
|
|
4342
|
+
privateRateLimit("earn_fixed_purchase_preview_offers", 2)
|
|
4343
|
+
).catch(() => null)
|
|
4344
|
+
]);
|
|
4345
|
+
const rateResult = normalizeResponse(rateResponse);
|
|
4346
|
+
const fixedResult = fixedResponse ? normalizeResponse(fixedResponse) : { data: [] };
|
|
4347
|
+
const rateArr = Array.isArray(rateResult["data"]) ? rateResult["data"] : [];
|
|
4348
|
+
const allOffers = Array.isArray(fixedResult["data"]) ? fixedResult["data"] : [];
|
|
4349
|
+
const matchedOffer = allOffers.find(
|
|
4350
|
+
(o) => o["term"] === term && o["ccy"] === ccy
|
|
4351
|
+
);
|
|
4352
|
+
const { borrowingOrderQuota: _, ...offerWithoutTotal } = matchedOffer ?? {};
|
|
4353
|
+
const offerWithSoldOut = matchedOffer ? { ...offerWithoutTotal, soldOut: offerWithoutTotal["lendQuota"] === "0" } : null;
|
|
4354
|
+
return {
|
|
4355
|
+
preview: true,
|
|
4356
|
+
ccy,
|
|
4357
|
+
amt,
|
|
4358
|
+
term,
|
|
4359
|
+
offer: offerWithSoldOut,
|
|
4360
|
+
currentFlexibleRate: rateArr[0]?.["lendingRate"] ?? null,
|
|
4361
|
+
warning: "\u26A0\uFE0F Orders still in 'pending' state can be cancelled before matching completes. Once the status changes to 'earning', funds are LOCKED until maturity \u2014 early redemption is NOT allowed. Please call again with confirm=true to proceed."
|
|
4362
|
+
};
|
|
4363
|
+
}
|
|
4364
|
+
assertNotDemo(context.config, "earn_fixed_purchase");
|
|
4365
|
+
const response = await context.client.privatePost(
|
|
4366
|
+
"/api/v5/finance/simple-earn-fixed/purchase",
|
|
4367
|
+
{ ccy, amt, term },
|
|
4368
|
+
privateRateLimit("earn_fixed_purchase", 2)
|
|
4369
|
+
);
|
|
4370
|
+
return normalizeResponse(response);
|
|
4371
|
+
}
|
|
4372
|
+
},
|
|
4373
|
+
{
|
|
4374
|
+
name: "earn_fixed_redeem",
|
|
4375
|
+
module: "earn.savings",
|
|
4376
|
+
description: "Redeem Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) order. [CAUTION] Redeems a fixed-term lending order. Always redeems the full order amount. Only orders in 'pending' (\u5339\u914D\u4E2D) state can be redeemed \u2014 orders in 'earning' state are locked until maturity and cannot be redeemed early. Do NOT use for flexible earn redemption \u2014 use earn_savings_redeem instead.",
|
|
4377
|
+
isWrite: true,
|
|
4378
|
+
inputSchema: {
|
|
4379
|
+
type: "object",
|
|
4380
|
+
properties: {
|
|
4381
|
+
reqId: {
|
|
4382
|
+
type: "string",
|
|
4383
|
+
description: "Request ID of the fixed-term order to redeem"
|
|
4384
|
+
}
|
|
4385
|
+
},
|
|
4386
|
+
required: ["reqId"]
|
|
4387
|
+
},
|
|
4388
|
+
handler: async (rawArgs, context) => {
|
|
4389
|
+
assertNotDemo(context.config, "earn_fixed_redeem");
|
|
4390
|
+
const args = asRecord(rawArgs);
|
|
4391
|
+
const response = await context.client.privatePost(
|
|
4392
|
+
"/api/v5/finance/simple-earn-fixed/redeem",
|
|
4393
|
+
{
|
|
4394
|
+
reqId: requireString(args, "reqId")
|
|
4395
|
+
},
|
|
4396
|
+
privateRateLimit("earn_fixed_redeem", 2)
|
|
4397
|
+
);
|
|
4398
|
+
return normalizeResponse(response);
|
|
4399
|
+
}
|
|
4400
|
+
},
|
|
4246
4401
|
{
|
|
4247
4402
|
name: "earn_get_lending_rate_history",
|
|
4248
4403
|
module: "earn.savings",
|
|
4249
|
-
description: "Query Simple Earn lending rates
|
|
4404
|
+
description: "Query Simple Earn lending rates and fixed-term offers. Use this tool when the user asks about Simple Earn products, current or historical lending rates, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns lending rate history (lendingRate field, newest-first) AND available fixed-term (\u5B9A\u671F) offers with APR, term, min amount, and quota \u2014 one call gives a complete view of both flexible and fixed options. In fixedOffers: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). To get current flexible APY: use limit=1 and read lendingRate.",
|
|
4250
4405
|
isWrite: false,
|
|
4251
4406
|
inputSchema: {
|
|
4252
4407
|
type: "object",
|
|
@@ -4265,23 +4420,45 @@ function registerEarnTools() {
|
|
|
4265
4420
|
},
|
|
4266
4421
|
limit: {
|
|
4267
4422
|
type: "number",
|
|
4268
|
-
description: "Max results (default
|
|
4423
|
+
description: "Max results (default 7)"
|
|
4269
4424
|
}
|
|
4270
4425
|
}
|
|
4271
4426
|
},
|
|
4272
4427
|
handler: async (rawArgs, context) => {
|
|
4273
4428
|
const args = asRecord(rawArgs);
|
|
4274
|
-
const
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4429
|
+
const ccy = readString(args, "ccy");
|
|
4430
|
+
const [rateResponse, fixedResponse] = await Promise.all([
|
|
4431
|
+
context.client.publicGet(
|
|
4432
|
+
"/api/v5/finance/savings/lending-rate-history",
|
|
4433
|
+
compactObject({
|
|
4434
|
+
ccy,
|
|
4435
|
+
after: readString(args, "after"),
|
|
4436
|
+
before: readString(args, "before"),
|
|
4437
|
+
limit: readNumber(args, "limit") ?? 7
|
|
4438
|
+
}),
|
|
4439
|
+
publicRateLimit("earn_get_lending_rate_history", 6)
|
|
4440
|
+
),
|
|
4441
|
+
context.client.privateGet(
|
|
4442
|
+
"/api/v5/finance/simple-earn-fixed/offers",
|
|
4443
|
+
compactObject({ ccy }),
|
|
4444
|
+
privateRateLimit("earn_get_lending_rate_history_fixed", 2)
|
|
4445
|
+
).catch(() => null)
|
|
4446
|
+
]);
|
|
4447
|
+
const rateResult = normalizeResponse(rateResponse);
|
|
4448
|
+
const rateData = Array.isArray(rateResult["data"]) ? rateResult["data"].map(
|
|
4449
|
+
({ rate: _, ...rest }) => rest
|
|
4450
|
+
) : [];
|
|
4451
|
+
const fixedResult = fixedResponse ? normalizeResponse(fixedResponse) : { data: [] };
|
|
4452
|
+
const allOffers = fixedResult["data"] ?? [];
|
|
4453
|
+
const fixedOffers = allOffers.map(({ borrowingOrderQuota: _, ...rest }) => ({
|
|
4454
|
+
...rest,
|
|
4455
|
+
soldOut: rest["lendQuota"] === "0"
|
|
4456
|
+
}));
|
|
4457
|
+
return {
|
|
4458
|
+
...rateResult,
|
|
4459
|
+
data: rateData,
|
|
4460
|
+
fixedOffers
|
|
4461
|
+
};
|
|
4285
4462
|
}
|
|
4286
4463
|
}
|
|
4287
4464
|
];
|
|
@@ -4620,7 +4797,7 @@ function registerDcdTools() {
|
|
|
4620
4797
|
{
|
|
4621
4798
|
name: "dcd_get_products",
|
|
4622
4799
|
module: "earn.dcd",
|
|
4623
|
-
description: "Get DCD products with yield and quota info.",
|
|
4800
|
+
description: "Get DCD products with yield and quota info. Yields in response are decimal fractions, not percentages.",
|
|
4624
4801
|
isWrite: false,
|
|
4625
4802
|
inputSchema: {
|
|
4626
4803
|
type: "object",
|
|
@@ -4674,7 +4851,7 @@ function registerDcdTools() {
|
|
|
4674
4851
|
{
|
|
4675
4852
|
name: "dcd_get_orders",
|
|
4676
4853
|
module: "earn.dcd",
|
|
4677
|
-
description: "Get DCD order history.",
|
|
4854
|
+
description: "Get DCD order history. Yields in response are decimal fractions, not percentages.",
|
|
4678
4855
|
isWrite: false,
|
|
4679
4856
|
inputSchema: {
|
|
4680
4857
|
type: "object",
|
|
@@ -4718,7 +4895,7 @@ function registerDcdTools() {
|
|
|
4718
4895
|
{
|
|
4719
4896
|
name: "dcd_subscribe",
|
|
4720
4897
|
module: "earn.dcd",
|
|
4721
|
-
description: "Subscribe to a DCD product: get quote and execute atomically. Confirm product, amount, and currency with user before calling. Optional minAnnualizedYield
|
|
4898
|
+
description: "Subscribe to a DCD product: get quote and execute atomically. Confirm product, amount, and currency with user before calling. Optional minAnnualizedYield rejects the order if quote yield falls below threshold. Returns order result with quote snapshot (minAnnualizedYield is in percent; response yields are decimal fractions).",
|
|
4722
4899
|
isWrite: true,
|
|
4723
4900
|
inputSchema: {
|
|
4724
4901
|
type: "object",
|
|
@@ -4757,13 +4934,23 @@ function registerDcdTools() {
|
|
|
4757
4934
|
});
|
|
4758
4935
|
}
|
|
4759
4936
|
if (minAnnualizedYield !== void 0) {
|
|
4760
|
-
const
|
|
4761
|
-
if (
|
|
4937
|
+
const rawYield = parseFloat(quote["annualizedYield"]);
|
|
4938
|
+
if (isNaN(rawYield)) {
|
|
4939
|
+
throw new OkxApiError(
|
|
4940
|
+
"Quote returned non-numeric annualizedYield, cannot verify minimum yield threshold.",
|
|
4941
|
+
{
|
|
4942
|
+
code: "INVALID_YIELD_VALUE",
|
|
4943
|
+
suggestion: "Order not placed. The quote did not include a valid annualizedYield. Retry or pick a different product."
|
|
4944
|
+
}
|
|
4945
|
+
);
|
|
4946
|
+
}
|
|
4947
|
+
const actualYieldPct = rawYield * 100;
|
|
4948
|
+
if (actualYieldPct < minAnnualizedYield) {
|
|
4762
4949
|
throw new OkxApiError(
|
|
4763
|
-
`Quote yield ${
|
|
4950
|
+
`Quote yield ${actualYieldPct.toFixed(2)}% is below the minimum threshold of ${minAnnualizedYield}%.`,
|
|
4764
4951
|
{
|
|
4765
4952
|
code: "YIELD_BELOW_MIN",
|
|
4766
|
-
suggestion: `Order not placed. Actual: ${
|
|
4953
|
+
suggestion: `Order not placed. Actual: ${actualYieldPct.toFixed(2)}%, required: >= ${minAnnualizedYield}%. Try a different product or lower your minimum yield.`
|
|
4767
4954
|
}
|
|
4768
4955
|
);
|
|
4769
4956
|
}
|
|
@@ -4908,7 +5095,7 @@ function registerAutoEarnTools() {
|
|
|
4908
5095
|
}
|
|
4909
5096
|
var EARN_DEMO_MESSAGE = "Earn features (savings, DCD, on-chain staking, auto-earn) are not available in simulated trading mode.";
|
|
4910
5097
|
var EARN_DEMO_SUGGESTION = "Switch to a live account to use Earn features.";
|
|
4911
|
-
var DEMO_GUARD_SKIP = /* @__PURE__ */ new Set(["dcd_redeem"]);
|
|
5098
|
+
var DEMO_GUARD_SKIP = /* @__PURE__ */ new Set(["dcd_redeem", "earn_fixed_purchase"]);
|
|
4912
5099
|
function withDemoGuard(tool) {
|
|
4913
5100
|
if (!tool.isWrite || DEMO_GUARD_SKIP.has(tool.name)) return tool;
|
|
4914
5101
|
const originalHandler = tool.handler;
|
|
@@ -4969,12 +5156,12 @@ function buildContractTradeTools(cfg) {
|
|
|
4969
5156
|
},
|
|
4970
5157
|
sz: {
|
|
4971
5158
|
type: "string",
|
|
4972
|
-
description: "Number of contracts. Each contract = ctVal units (e.g. 0.1 ETH for ETH-USDT-SWAP). Query market_get_instruments for exact ctVal. Set tgtCcy=quote_ccy to specify sz in USDT
|
|
5159
|
+
description: "Number of contracts. Each contract = ctVal units (e.g. 0.1 ETH for ETH-USDT-SWAP). Query market_get_instruments for exact ctVal. Set tgtCcy=quote_ccy to specify sz in USDT notional value; set tgtCcy=margin to specify sz as margin cost (notional = sz * leverage)."
|
|
4973
5160
|
},
|
|
4974
5161
|
tgtCcy: {
|
|
4975
5162
|
type: "string",
|
|
4976
|
-
enum: ["base_ccy", "quote_ccy"],
|
|
4977
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
5163
|
+
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
5164
|
+
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
4978
5165
|
},
|
|
4979
5166
|
px: { type: "string", description: "Required for limit/post_only/fok/ioc" },
|
|
4980
5167
|
reduceOnly: {
|
|
@@ -4993,6 +5180,14 @@ function buildContractTradeTools(cfg) {
|
|
|
4993
5180
|
const args = asRecord(rawArgs);
|
|
4994
5181
|
const reduceOnly = args.reduceOnly;
|
|
4995
5182
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
5183
|
+
const resolved = await resolveQuoteCcySz(
|
|
5184
|
+
requireString(args, "instId"),
|
|
5185
|
+
requireString(args, "sz"),
|
|
5186
|
+
readString(args, "tgtCcy"),
|
|
5187
|
+
defaultType,
|
|
5188
|
+
context.client,
|
|
5189
|
+
readString(args, "tdMode")
|
|
5190
|
+
);
|
|
4996
5191
|
const response = await context.client.privatePost(
|
|
4997
5192
|
"/api/v5/trade/order",
|
|
4998
5193
|
compactObject({
|
|
@@ -5001,8 +5196,8 @@ function buildContractTradeTools(cfg) {
|
|
|
5001
5196
|
side: requireString(args, "side"),
|
|
5002
5197
|
posSide: readString(args, "posSide"),
|
|
5003
5198
|
ordType: requireString(args, "ordType"),
|
|
5004
|
-
sz:
|
|
5005
|
-
tgtCcy:
|
|
5199
|
+
sz: resolved.sz,
|
|
5200
|
+
tgtCcy: resolved.tgtCcy,
|
|
5006
5201
|
px: readString(args, "px"),
|
|
5007
5202
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
5008
5203
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -5011,7 +5206,11 @@ function buildContractTradeTools(cfg) {
|
|
|
5011
5206
|
}),
|
|
5012
5207
|
privateRateLimit(n("place_order"), 60)
|
|
5013
5208
|
);
|
|
5014
|
-
|
|
5209
|
+
const result = normalizeResponse(response);
|
|
5210
|
+
if (resolved.conversionNote) {
|
|
5211
|
+
result._conversion = resolved.conversionNote;
|
|
5212
|
+
}
|
|
5213
|
+
return result;
|
|
5015
5214
|
}
|
|
5016
5215
|
},
|
|
5017
5216
|
// ── cancel_order ─────────────────────────────────────────────────────────
|
|
@@ -5479,6 +5678,12 @@ function registerFuturesTools() {
|
|
|
5479
5678
|
];
|
|
5480
5679
|
}
|
|
5481
5680
|
var TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
5681
|
+
var DEMO_PROPERTY = {
|
|
5682
|
+
demo: {
|
|
5683
|
+
type: "boolean",
|
|
5684
|
+
description: "Query simulated trading (demo) market data. Default: false (live market data)."
|
|
5685
|
+
}
|
|
5686
|
+
};
|
|
5482
5687
|
function registerMarketTools() {
|
|
5483
5688
|
return [
|
|
5484
5689
|
{
|
|
@@ -5492,7 +5697,8 @@ function registerMarketTools() {
|
|
|
5492
5697
|
instId: {
|
|
5493
5698
|
type: "string",
|
|
5494
5699
|
description: "e.g. BTC-USDT, BTC-USDT-SWAP"
|
|
5495
|
-
}
|
|
5700
|
+
},
|
|
5701
|
+
...DEMO_PROPERTY
|
|
5496
5702
|
},
|
|
5497
5703
|
required: ["instId"]
|
|
5498
5704
|
},
|
|
@@ -5501,7 +5707,8 @@ function registerMarketTools() {
|
|
|
5501
5707
|
const response = await context.client.publicGet(
|
|
5502
5708
|
"/api/v5/market/ticker",
|
|
5503
5709
|
{ instId: requireString(args, "instId") },
|
|
5504
|
-
publicRateLimit("market_get_ticker", 20)
|
|
5710
|
+
publicRateLimit("market_get_ticker", 20),
|
|
5711
|
+
readBoolean(args, "demo") ?? false
|
|
5505
5712
|
);
|
|
5506
5713
|
return normalizeResponse(response);
|
|
5507
5714
|
}
|
|
@@ -5525,7 +5732,8 @@ function registerMarketTools() {
|
|
|
5525
5732
|
instFamily: {
|
|
5526
5733
|
type: "string",
|
|
5527
5734
|
description: "e.g. BTC-USD"
|
|
5528
|
-
}
|
|
5735
|
+
},
|
|
5736
|
+
...DEMO_PROPERTY
|
|
5529
5737
|
},
|
|
5530
5738
|
required: ["instType"]
|
|
5531
5739
|
},
|
|
@@ -5538,7 +5746,8 @@ function registerMarketTools() {
|
|
|
5538
5746
|
uly: readString(args, "uly"),
|
|
5539
5747
|
instFamily: readString(args, "instFamily")
|
|
5540
5748
|
}),
|
|
5541
|
-
publicRateLimit("market_get_tickers", 20)
|
|
5749
|
+
publicRateLimit("market_get_tickers", 20),
|
|
5750
|
+
readBoolean(args, "demo") ?? false
|
|
5542
5751
|
);
|
|
5543
5752
|
return normalizeResponse(response);
|
|
5544
5753
|
}
|
|
@@ -5558,7 +5767,8 @@ function registerMarketTools() {
|
|
|
5558
5767
|
sz: {
|
|
5559
5768
|
type: "number",
|
|
5560
5769
|
description: "Depth per side, default 1, max 400"
|
|
5561
|
-
}
|
|
5770
|
+
},
|
|
5771
|
+
...DEMO_PROPERTY
|
|
5562
5772
|
},
|
|
5563
5773
|
required: ["instId"]
|
|
5564
5774
|
},
|
|
@@ -5570,7 +5780,8 @@ function registerMarketTools() {
|
|
|
5570
5780
|
instId: requireString(args, "instId"),
|
|
5571
5781
|
sz: readNumber(args, "sz")
|
|
5572
5782
|
}),
|
|
5573
|
-
publicRateLimit("market_get_orderbook", 20)
|
|
5783
|
+
publicRateLimit("market_get_orderbook", 20),
|
|
5784
|
+
readBoolean(args, "demo") ?? false
|
|
5574
5785
|
);
|
|
5575
5786
|
return normalizeResponse(response);
|
|
5576
5787
|
}
|
|
@@ -5603,7 +5814,8 @@ function registerMarketTools() {
|
|
|
5603
5814
|
limit: {
|
|
5604
5815
|
type: "number",
|
|
5605
5816
|
description: "Max results (default 100)"
|
|
5606
|
-
}
|
|
5817
|
+
},
|
|
5818
|
+
...DEMO_PROPERTY
|
|
5607
5819
|
},
|
|
5608
5820
|
required: ["instId"]
|
|
5609
5821
|
},
|
|
@@ -5611,6 +5823,7 @@ function registerMarketTools() {
|
|
|
5611
5823
|
const args = asRecord(rawArgs);
|
|
5612
5824
|
const afterTs = readString(args, "after");
|
|
5613
5825
|
const beforeTs = readString(args, "before");
|
|
5826
|
+
const demo = readBoolean(args, "demo") ?? false;
|
|
5614
5827
|
const query = compactObject({
|
|
5615
5828
|
instId: requireString(args, "instId"),
|
|
5616
5829
|
bar: readString(args, "bar"),
|
|
@@ -5622,9 +5835,9 @@ function registerMarketTools() {
|
|
|
5622
5835
|
const hasTimestamp = afterTs !== void 0 || beforeTs !== void 0;
|
|
5623
5836
|
const useHistory = afterTs !== void 0 && Number(afterTs) < Date.now() - TWO_DAYS_MS;
|
|
5624
5837
|
const path42 = useHistory ? "/api/v5/market/history-candles" : "/api/v5/market/candles";
|
|
5625
|
-
const response = await context.client.publicGet(path42, query, rateLimit);
|
|
5838
|
+
const response = await context.client.publicGet(path42, query, rateLimit, demo);
|
|
5626
5839
|
if (!useHistory && hasTimestamp && Array.isArray(response.data) && response.data.length === 0) {
|
|
5627
|
-
return normalizeResponse(await context.client.publicGet("/api/v5/market/history-candles", query, rateLimit));
|
|
5840
|
+
return normalizeResponse(await context.client.publicGet("/api/v5/market/history-candles", query, rateLimit, demo));
|
|
5628
5841
|
}
|
|
5629
5842
|
return normalizeResponse(response);
|
|
5630
5843
|
}
|
|
@@ -5652,7 +5865,8 @@ function registerMarketTools() {
|
|
|
5652
5865
|
instFamily: {
|
|
5653
5866
|
type: "string",
|
|
5654
5867
|
description: "e.g. BTC-USD"
|
|
5655
|
-
}
|
|
5868
|
+
},
|
|
5869
|
+
...DEMO_PROPERTY
|
|
5656
5870
|
},
|
|
5657
5871
|
required: ["instType"]
|
|
5658
5872
|
},
|
|
@@ -5666,7 +5880,8 @@ function registerMarketTools() {
|
|
|
5666
5880
|
uly: readString(args, "uly"),
|
|
5667
5881
|
instFamily: readString(args, "instFamily")
|
|
5668
5882
|
}),
|
|
5669
|
-
publicRateLimit("market_get_instruments", 20)
|
|
5883
|
+
publicRateLimit("market_get_instruments", 20),
|
|
5884
|
+
readBoolean(args, "demo") ?? false
|
|
5670
5885
|
);
|
|
5671
5886
|
return normalizeResponse(response);
|
|
5672
5887
|
}
|
|
@@ -5698,13 +5913,15 @@ function registerMarketTools() {
|
|
|
5698
5913
|
limit: {
|
|
5699
5914
|
type: "number",
|
|
5700
5915
|
description: "History records (default 20, max 100)"
|
|
5701
|
-
}
|
|
5916
|
+
},
|
|
5917
|
+
...DEMO_PROPERTY
|
|
5702
5918
|
},
|
|
5703
5919
|
required: ["instId"]
|
|
5704
5920
|
},
|
|
5705
5921
|
handler: async (rawArgs, context) => {
|
|
5706
5922
|
const args = asRecord(rawArgs);
|
|
5707
5923
|
const isHistory = readBoolean(args, "history") ?? false;
|
|
5924
|
+
const demo = readBoolean(args, "demo") ?? false;
|
|
5708
5925
|
if (isHistory) {
|
|
5709
5926
|
const response2 = await context.client.publicGet(
|
|
5710
5927
|
"/api/v5/public/funding-rate-history",
|
|
@@ -5714,14 +5931,16 @@ function registerMarketTools() {
|
|
|
5714
5931
|
before: readString(args, "before"),
|
|
5715
5932
|
limit: readNumber(args, "limit") ?? 20
|
|
5716
5933
|
}),
|
|
5717
|
-
publicRateLimit("market_get_funding_rate", 20)
|
|
5934
|
+
publicRateLimit("market_get_funding_rate", 20),
|
|
5935
|
+
demo
|
|
5718
5936
|
);
|
|
5719
5937
|
return normalizeResponse(response2);
|
|
5720
5938
|
}
|
|
5721
5939
|
const response = await context.client.publicGet(
|
|
5722
5940
|
"/api/v5/public/funding-rate",
|
|
5723
5941
|
{ instId: requireString(args, "instId") },
|
|
5724
|
-
publicRateLimit("market_get_funding_rate", 20)
|
|
5942
|
+
publicRateLimit("market_get_funding_rate", 20),
|
|
5943
|
+
demo
|
|
5725
5944
|
);
|
|
5726
5945
|
return normalizeResponse(response);
|
|
5727
5946
|
}
|
|
@@ -5748,7 +5967,8 @@ function registerMarketTools() {
|
|
|
5748
5967
|
},
|
|
5749
5968
|
instFamily: {
|
|
5750
5969
|
type: "string"
|
|
5751
|
-
}
|
|
5970
|
+
},
|
|
5971
|
+
...DEMO_PROPERTY
|
|
5752
5972
|
},
|
|
5753
5973
|
required: ["instType"]
|
|
5754
5974
|
},
|
|
@@ -5762,7 +5982,8 @@ function registerMarketTools() {
|
|
|
5762
5982
|
uly: readString(args, "uly"),
|
|
5763
5983
|
instFamily: readString(args, "instFamily")
|
|
5764
5984
|
}),
|
|
5765
|
-
publicRateLimit("market_get_mark_price", 10)
|
|
5985
|
+
publicRateLimit("market_get_mark_price", 10),
|
|
5986
|
+
readBoolean(args, "demo") ?? false
|
|
5766
5987
|
);
|
|
5767
5988
|
return normalizeResponse(response);
|
|
5768
5989
|
}
|
|
@@ -5782,7 +6003,8 @@ function registerMarketTools() {
|
|
|
5782
6003
|
limit: {
|
|
5783
6004
|
type: "number",
|
|
5784
6005
|
description: "Default 20, max 500"
|
|
5785
|
-
}
|
|
6006
|
+
},
|
|
6007
|
+
...DEMO_PROPERTY
|
|
5786
6008
|
},
|
|
5787
6009
|
required: ["instId"]
|
|
5788
6010
|
},
|
|
@@ -5794,7 +6016,8 @@ function registerMarketTools() {
|
|
|
5794
6016
|
instId: requireString(args, "instId"),
|
|
5795
6017
|
limit: readNumber(args, "limit") ?? 20
|
|
5796
6018
|
}),
|
|
5797
|
-
publicRateLimit("market_get_trades", 20)
|
|
6019
|
+
publicRateLimit("market_get_trades", 20),
|
|
6020
|
+
readBoolean(args, "demo") ?? false
|
|
5798
6021
|
);
|
|
5799
6022
|
return normalizeResponse(response);
|
|
5800
6023
|
}
|
|
@@ -5814,7 +6037,8 @@ function registerMarketTools() {
|
|
|
5814
6037
|
quoteCcy: {
|
|
5815
6038
|
type: "string",
|
|
5816
6039
|
description: "e.g. USD or USDT"
|
|
5817
|
-
}
|
|
6040
|
+
},
|
|
6041
|
+
...DEMO_PROPERTY
|
|
5818
6042
|
}
|
|
5819
6043
|
},
|
|
5820
6044
|
handler: async (rawArgs, context) => {
|
|
@@ -5825,7 +6049,8 @@ function registerMarketTools() {
|
|
|
5825
6049
|
instId: readString(args, "instId"),
|
|
5826
6050
|
quoteCcy: readString(args, "quoteCcy")
|
|
5827
6051
|
}),
|
|
5828
|
-
publicRateLimit("market_get_index_ticker", 20)
|
|
6052
|
+
publicRateLimit("market_get_index_ticker", 20),
|
|
6053
|
+
readBoolean(args, "demo") ?? false
|
|
5829
6054
|
);
|
|
5830
6055
|
return normalizeResponse(response);
|
|
5831
6056
|
}
|
|
@@ -5862,7 +6087,8 @@ function registerMarketTools() {
|
|
|
5862
6087
|
history: {
|
|
5863
6088
|
type: "boolean",
|
|
5864
6089
|
description: "true=older historical data"
|
|
5865
|
-
}
|
|
6090
|
+
},
|
|
6091
|
+
...DEMO_PROPERTY
|
|
5866
6092
|
},
|
|
5867
6093
|
required: ["instId"]
|
|
5868
6094
|
},
|
|
@@ -5879,7 +6105,8 @@ function registerMarketTools() {
|
|
|
5879
6105
|
before: readString(args, "before"),
|
|
5880
6106
|
limit: readNumber(args, "limit")
|
|
5881
6107
|
}),
|
|
5882
|
-
publicRateLimit("market_get_index_candles", 20)
|
|
6108
|
+
publicRateLimit("market_get_index_candles", 20),
|
|
6109
|
+
readBoolean(args, "demo") ?? false
|
|
5883
6110
|
);
|
|
5884
6111
|
return normalizeResponse(response);
|
|
5885
6112
|
}
|
|
@@ -5895,7 +6122,8 @@ function registerMarketTools() {
|
|
|
5895
6122
|
instId: {
|
|
5896
6123
|
type: "string",
|
|
5897
6124
|
description: "SWAP or FUTURES ID, e.g. BTC-USDT-SWAP"
|
|
5898
|
-
}
|
|
6125
|
+
},
|
|
6126
|
+
...DEMO_PROPERTY
|
|
5899
6127
|
},
|
|
5900
6128
|
required: ["instId"]
|
|
5901
6129
|
},
|
|
@@ -5904,7 +6132,8 @@ function registerMarketTools() {
|
|
|
5904
6132
|
const response = await context.client.publicGet(
|
|
5905
6133
|
"/api/v5/public/price-limit",
|
|
5906
6134
|
{ instId: requireString(args, "instId") },
|
|
5907
|
-
publicRateLimit("market_get_price_limit", 20)
|
|
6135
|
+
publicRateLimit("market_get_price_limit", 20),
|
|
6136
|
+
readBoolean(args, "demo") ?? false
|
|
5908
6137
|
);
|
|
5909
6138
|
return normalizeResponse(response);
|
|
5910
6139
|
}
|
|
@@ -5931,7 +6160,8 @@ function registerMarketTools() {
|
|
|
5931
6160
|
},
|
|
5932
6161
|
instFamily: {
|
|
5933
6162
|
type: "string"
|
|
5934
|
-
}
|
|
6163
|
+
},
|
|
6164
|
+
...DEMO_PROPERTY
|
|
5935
6165
|
},
|
|
5936
6166
|
required: ["instType"]
|
|
5937
6167
|
},
|
|
@@ -5945,7 +6175,8 @@ function registerMarketTools() {
|
|
|
5945
6175
|
uly: readString(args, "uly"),
|
|
5946
6176
|
instFamily: readString(args, "instFamily")
|
|
5947
6177
|
}),
|
|
5948
|
-
publicRateLimit("market_get_open_interest", 20)
|
|
6178
|
+
publicRateLimit("market_get_open_interest", 20),
|
|
6179
|
+
readBoolean(args, "demo") ?? false
|
|
5949
6180
|
);
|
|
5950
6181
|
return normalizeResponse(response);
|
|
5951
6182
|
}
|
|
@@ -5966,7 +6197,8 @@ function registerMarketTools() {
|
|
|
5966
6197
|
instId: {
|
|
5967
6198
|
type: "string",
|
|
5968
6199
|
description: "Optional: filter by specific instrument ID, e.g. AAPL-USDT-SWAP"
|
|
5969
|
-
}
|
|
6200
|
+
},
|
|
6201
|
+
...DEMO_PROPERTY
|
|
5970
6202
|
},
|
|
5971
6203
|
required: []
|
|
5972
6204
|
},
|
|
@@ -5977,7 +6209,8 @@ function registerMarketTools() {
|
|
|
5977
6209
|
const response = await context.client.publicGet(
|
|
5978
6210
|
"/api/v5/public/instruments",
|
|
5979
6211
|
compactObject({ instType, instId }),
|
|
5980
|
-
publicRateLimit("market_get_stock_tokens", 20)
|
|
6212
|
+
publicRateLimit("market_get_stock_tokens", 20),
|
|
6213
|
+
readBoolean(args, "demo") ?? false
|
|
5981
6214
|
);
|
|
5982
6215
|
const data = response.data;
|
|
5983
6216
|
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === "3") : data;
|
|
@@ -5987,7 +6220,7 @@ function registerMarketTools() {
|
|
|
5987
6220
|
{
|
|
5988
6221
|
name: "market_get_instruments_by_category",
|
|
5989
6222
|
module: "market",
|
|
5990
|
-
description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
|
|
6223
|
+
description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP for crude oil). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
|
|
5991
6224
|
isWrite: false,
|
|
5992
6225
|
inputSchema: {
|
|
5993
6226
|
type: "object",
|
|
@@ -6005,7 +6238,8 @@ function registerMarketTools() {
|
|
|
6005
6238
|
instId: {
|
|
6006
6239
|
type: "string",
|
|
6007
6240
|
description: "Optional: filter by specific instrument ID"
|
|
6008
|
-
}
|
|
6241
|
+
},
|
|
6242
|
+
...DEMO_PROPERTY
|
|
6009
6243
|
},
|
|
6010
6244
|
required: ["instCategory"]
|
|
6011
6245
|
},
|
|
@@ -6017,7 +6251,8 @@ function registerMarketTools() {
|
|
|
6017
6251
|
const response = await context.client.publicGet(
|
|
6018
6252
|
"/api/v5/public/instruments",
|
|
6019
6253
|
compactObject({ instType, instId }),
|
|
6020
|
-
publicRateLimit("market_get_instruments_by_category", 20)
|
|
6254
|
+
publicRateLimit("market_get_instruments_by_category", 20),
|
|
6255
|
+
readBoolean(args, "demo") ?? false
|
|
6021
6256
|
);
|
|
6022
6257
|
const data = response.data;
|
|
6023
6258
|
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === instCategory) : data;
|
|
@@ -6085,8 +6320,8 @@ function registerOptionAlgoTools() {
|
|
|
6085
6320
|
},
|
|
6086
6321
|
tgtCcy: {
|
|
6087
6322
|
type: "string",
|
|
6088
|
-
enum: ["base_ccy", "quote_ccy"],
|
|
6089
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
6323
|
+
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
6324
|
+
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
6090
6325
|
},
|
|
6091
6326
|
reduceOnly: {
|
|
6092
6327
|
type: "boolean",
|
|
@@ -6102,6 +6337,14 @@ function registerOptionAlgoTools() {
|
|
|
6102
6337
|
handler: async (rawArgs, context) => {
|
|
6103
6338
|
const args = asRecord(rawArgs);
|
|
6104
6339
|
const reduceOnly = readBoolean(args, "reduceOnly");
|
|
6340
|
+
const resolved = await resolveQuoteCcySz(
|
|
6341
|
+
requireString(args, "instId"),
|
|
6342
|
+
requireString(args, "sz"),
|
|
6343
|
+
readString(args, "tgtCcy"),
|
|
6344
|
+
"OPTION",
|
|
6345
|
+
context.client,
|
|
6346
|
+
readString(args, "tdMode")
|
|
6347
|
+
);
|
|
6105
6348
|
const response = await context.client.privatePost(
|
|
6106
6349
|
"/api/v5/trade/order-algo",
|
|
6107
6350
|
compactObject({
|
|
@@ -6109,8 +6352,8 @@ function registerOptionAlgoTools() {
|
|
|
6109
6352
|
tdMode: requireString(args, "tdMode"),
|
|
6110
6353
|
side: requireString(args, "side"),
|
|
6111
6354
|
ordType: requireString(args, "ordType"),
|
|
6112
|
-
sz:
|
|
6113
|
-
tgtCcy:
|
|
6355
|
+
sz: resolved.sz,
|
|
6356
|
+
tgtCcy: resolved.tgtCcy,
|
|
6114
6357
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
6115
6358
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
6116
6359
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -6317,7 +6560,12 @@ function registerOptionTools() {
|
|
|
6317
6560
|
},
|
|
6318
6561
|
sz: {
|
|
6319
6562
|
type: "string",
|
|
6320
|
-
description: "Contracts count
|
|
6563
|
+
description: "Contracts count by default. Set tgtCcy=quote_ccy to specify USDT notional value; set tgtCcy=margin to specify USDT margin cost (notional = sz * leverage)."
|
|
6564
|
+
},
|
|
6565
|
+
tgtCcy: {
|
|
6566
|
+
type: "string",
|
|
6567
|
+
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
6568
|
+
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
6321
6569
|
},
|
|
6322
6570
|
px: {
|
|
6323
6571
|
type: "string",
|
|
@@ -6354,6 +6602,14 @@ function registerOptionTools() {
|
|
|
6354
6602
|
const args = asRecord(rawArgs);
|
|
6355
6603
|
const reduceOnly = args.reduceOnly;
|
|
6356
6604
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
6605
|
+
const resolved = await resolveQuoteCcySz(
|
|
6606
|
+
requireString(args, "instId"),
|
|
6607
|
+
requireString(args, "sz"),
|
|
6608
|
+
readString(args, "tgtCcy"),
|
|
6609
|
+
"OPTION",
|
|
6610
|
+
context.client,
|
|
6611
|
+
readString(args, "tdMode")
|
|
6612
|
+
);
|
|
6357
6613
|
const response = await context.client.privatePost(
|
|
6358
6614
|
"/api/v5/trade/order",
|
|
6359
6615
|
compactObject({
|
|
@@ -6361,7 +6617,8 @@ function registerOptionTools() {
|
|
|
6361
6617
|
tdMode: requireString(args, "tdMode"),
|
|
6362
6618
|
side: requireString(args, "side"),
|
|
6363
6619
|
ordType: requireString(args, "ordType"),
|
|
6364
|
-
sz:
|
|
6620
|
+
sz: resolved.sz,
|
|
6621
|
+
tgtCcy: resolved.tgtCcy,
|
|
6365
6622
|
px: readString(args, "px"),
|
|
6366
6623
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
6367
6624
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -7517,12 +7774,12 @@ function createToolRunner(client, config) {
|
|
|
7517
7774
|
};
|
|
7518
7775
|
}
|
|
7519
7776
|
function configFilePath() {
|
|
7520
|
-
return
|
|
7777
|
+
return join4(homedir2(), ".okx", "config.toml");
|
|
7521
7778
|
}
|
|
7522
7779
|
function readFullConfig() {
|
|
7523
7780
|
const path42 = configFilePath();
|
|
7524
7781
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
7525
|
-
const raw =
|
|
7782
|
+
const raw = readFileSync3(path42, "utf-8");
|
|
7526
7783
|
try {
|
|
7527
7784
|
return parse(raw);
|
|
7528
7785
|
} catch (err) {
|
|
@@ -7550,11 +7807,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
|
7550
7807
|
`;
|
|
7551
7808
|
function writeFullConfig(config) {
|
|
7552
7809
|
const path42 = configFilePath();
|
|
7553
|
-
const dir =
|
|
7810
|
+
const dir = dirname3(path42);
|
|
7554
7811
|
if (!existsSync3(dir)) {
|
|
7555
|
-
|
|
7812
|
+
mkdirSync4(dir, { recursive: true });
|
|
7556
7813
|
}
|
|
7557
|
-
|
|
7814
|
+
writeFileSync3(path42, CONFIG_HEADER + stringify(config), "utf-8");
|
|
7558
7815
|
}
|
|
7559
7816
|
function expandShorthand(moduleId) {
|
|
7560
7817
|
if (moduleId === "all") return [...MODULES];
|
|
@@ -7668,21 +7925,21 @@ function loadConfig(cli) {
|
|
|
7668
7925
|
verbose: cli.verbose ?? false
|
|
7669
7926
|
};
|
|
7670
7927
|
}
|
|
7671
|
-
var CACHE_FILE =
|
|
7928
|
+
var CACHE_FILE = join5(homedir3(), ".okx", "update-check.json");
|
|
7672
7929
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7673
|
-
function
|
|
7930
|
+
function readCache() {
|
|
7674
7931
|
try {
|
|
7675
7932
|
if (existsSync4(CACHE_FILE)) {
|
|
7676
|
-
return JSON.parse(
|
|
7933
|
+
return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
7677
7934
|
}
|
|
7678
7935
|
} catch {
|
|
7679
7936
|
}
|
|
7680
7937
|
return {};
|
|
7681
7938
|
}
|
|
7682
|
-
function
|
|
7939
|
+
function writeCache(cache) {
|
|
7683
7940
|
try {
|
|
7684
|
-
|
|
7685
|
-
|
|
7941
|
+
mkdirSync5(join5(homedir3(), ".okx"), { recursive: true });
|
|
7942
|
+
writeFileSync4(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7686
7943
|
} catch {
|
|
7687
7944
|
}
|
|
7688
7945
|
}
|
|
@@ -7729,14 +7986,14 @@ async function fetchLatestVersion(packageName) {
|
|
|
7729
7986
|
function refreshCacheInBackground(packageName) {
|
|
7730
7987
|
fetchLatestVersion(packageName).then((latest) => {
|
|
7731
7988
|
if (!latest) return;
|
|
7732
|
-
const cache =
|
|
7989
|
+
const cache = readCache();
|
|
7733
7990
|
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
7734
|
-
|
|
7991
|
+
writeCache(cache);
|
|
7735
7992
|
}).catch(() => {
|
|
7736
7993
|
});
|
|
7737
7994
|
}
|
|
7738
7995
|
function checkForUpdates(packageName, currentVersion) {
|
|
7739
|
-
const cache =
|
|
7996
|
+
const cache = readCache();
|
|
7740
7997
|
const entry = cache[packageName];
|
|
7741
7998
|
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
7742
7999
|
process.stderr.write(
|
|
@@ -7751,6 +8008,69 @@ Run: npm install -g ${packageName}
|
|
|
7751
8008
|
refreshCacheInBackground(packageName);
|
|
7752
8009
|
}
|
|
7753
8010
|
}
|
|
8011
|
+
var LOG_LEVEL_PRIORITY = {
|
|
8012
|
+
error: 0,
|
|
8013
|
+
warn: 1,
|
|
8014
|
+
info: 2,
|
|
8015
|
+
debug: 3
|
|
8016
|
+
};
|
|
8017
|
+
var SENSITIVE_KEY_PATTERN = /apiKey|secretKey|passphrase|password|secret/i;
|
|
8018
|
+
function sanitize(value) {
|
|
8019
|
+
if (value === null || value === void 0) {
|
|
8020
|
+
return value;
|
|
8021
|
+
}
|
|
8022
|
+
if (Array.isArray(value)) {
|
|
8023
|
+
return value.map(sanitize);
|
|
8024
|
+
}
|
|
8025
|
+
if (typeof value === "object") {
|
|
8026
|
+
const result = {};
|
|
8027
|
+
for (const [k, v] of Object.entries(value)) {
|
|
8028
|
+
if (SENSITIVE_KEY_PATTERN.test(k)) {
|
|
8029
|
+
result[k] = "[REDACTED]";
|
|
8030
|
+
} else {
|
|
8031
|
+
result[k] = sanitize(v);
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
return result;
|
|
8035
|
+
}
|
|
8036
|
+
return value;
|
|
8037
|
+
}
|
|
8038
|
+
var TradeLogger = class {
|
|
8039
|
+
logLevel;
|
|
8040
|
+
logDir;
|
|
8041
|
+
constructor(logLevel = "info", logDir) {
|
|
8042
|
+
this.logLevel = logLevel;
|
|
8043
|
+
this.logDir = logDir ?? path2.join(os2.homedir(), ".okx", "logs");
|
|
8044
|
+
}
|
|
8045
|
+
getLogPath(date) {
|
|
8046
|
+
const d = date ?? /* @__PURE__ */ new Date();
|
|
8047
|
+
const yyyy = d.getUTCFullYear();
|
|
8048
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
8049
|
+
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
8050
|
+
return path2.join(this.logDir, `trade-${yyyy}-${mm}-${dd}.log`);
|
|
8051
|
+
}
|
|
8052
|
+
log(level, tool, params, result, durationMs) {
|
|
8053
|
+
if (LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY[this.logLevel]) {
|
|
8054
|
+
return;
|
|
8055
|
+
}
|
|
8056
|
+
const entry = {
|
|
8057
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8058
|
+
level: level.toUpperCase(),
|
|
8059
|
+
tool,
|
|
8060
|
+
durationMs,
|
|
8061
|
+
params: sanitize(params),
|
|
8062
|
+
result: sanitize(result)
|
|
8063
|
+
};
|
|
8064
|
+
try {
|
|
8065
|
+
fs2.mkdirSync(this.logDir, { recursive: true });
|
|
8066
|
+
fs2.appendFileSync(this.getLogPath(), JSON.stringify(entry) + "\n", "utf8");
|
|
8067
|
+
} catch {
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
static sanitize(params) {
|
|
8071
|
+
return sanitize(params);
|
|
8072
|
+
}
|
|
8073
|
+
};
|
|
7754
8074
|
var CLIENT_NAMES = {
|
|
7755
8075
|
"claude-desktop": "Claude Desktop",
|
|
7756
8076
|
cursor: "Cursor",
|
|
@@ -7900,11 +8220,11 @@ function runSetup(options) {
|
|
|
7900
8220
|
// src/commands/diagnose.ts
|
|
7901
8221
|
import dns from "dns/promises";
|
|
7902
8222
|
import net from "net";
|
|
7903
|
-
import
|
|
8223
|
+
import os5 from "os";
|
|
7904
8224
|
import tls from "tls";
|
|
7905
8225
|
|
|
7906
8226
|
// src/commands/diagnose-utils.ts
|
|
7907
|
-
import
|
|
8227
|
+
import fs4 from "fs";
|
|
7908
8228
|
import { createRequire } from "module";
|
|
7909
8229
|
|
|
7910
8230
|
// src/formatter.ts
|
|
@@ -7917,6 +8237,10 @@ var activeOutput = stdioOutput;
|
|
|
7917
8237
|
function setOutput(impl) {
|
|
7918
8238
|
activeOutput = impl;
|
|
7919
8239
|
}
|
|
8240
|
+
var envContext = null;
|
|
8241
|
+
function setEnvContext(ctx) {
|
|
8242
|
+
envContext = ctx;
|
|
8243
|
+
}
|
|
7920
8244
|
function output(message) {
|
|
7921
8245
|
activeOutput.out(message);
|
|
7922
8246
|
}
|
|
@@ -7929,10 +8253,23 @@ function outputLine(message) {
|
|
|
7929
8253
|
function errorLine(message) {
|
|
7930
8254
|
activeOutput.err(message + EOL);
|
|
7931
8255
|
}
|
|
8256
|
+
var jsonEnvEnabled = false;
|
|
8257
|
+
function setJsonEnvEnabled(enabled) {
|
|
8258
|
+
jsonEnvEnabled = enabled;
|
|
8259
|
+
}
|
|
7932
8260
|
function printJson(data) {
|
|
7933
|
-
|
|
8261
|
+
const payload = jsonEnvEnabled && envContext ? {
|
|
8262
|
+
env: envContext.demo ? "demo" : "live",
|
|
8263
|
+
profile: envContext.profile,
|
|
8264
|
+
data
|
|
8265
|
+
} : data;
|
|
8266
|
+
activeOutput.out(JSON.stringify(payload, null, 2) + EOL);
|
|
7934
8267
|
}
|
|
7935
8268
|
function printTable(rows) {
|
|
8269
|
+
if (envContext) {
|
|
8270
|
+
const envLabel = envContext.demo ? "demo (simulated trading)" : "live";
|
|
8271
|
+
activeOutput.out(`Environment: ${envLabel}` + EOL + EOL);
|
|
8272
|
+
}
|
|
7936
8273
|
if (rows.length === 0) {
|
|
7937
8274
|
activeOutput.out("(no data)" + EOL);
|
|
7938
8275
|
return;
|
|
@@ -8009,7 +8346,7 @@ var Report = class {
|
|
|
8009
8346
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
8010
8347
|
}
|
|
8011
8348
|
lines.push(sep2, "");
|
|
8012
|
-
|
|
8349
|
+
fs4.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
8013
8350
|
return true;
|
|
8014
8351
|
} catch (_e) {
|
|
8015
8352
|
return false;
|
|
@@ -8044,7 +8381,7 @@ function writeReportIfRequested(report, outputPath) {
|
|
|
8044
8381
|
errorLine(` Warning: failed to write report to: ${outputPath}`);
|
|
8045
8382
|
}
|
|
8046
8383
|
}
|
|
8047
|
-
function
|
|
8384
|
+
function sanitize2(value) {
|
|
8048
8385
|
value = value.replace(
|
|
8049
8386
|
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,
|
|
8050
8387
|
"****-uuid-****"
|
|
@@ -8055,14 +8392,14 @@ function sanitize(value) {
|
|
|
8055
8392
|
}
|
|
8056
8393
|
|
|
8057
8394
|
// src/commands/diagnose-mcp.ts
|
|
8058
|
-
import
|
|
8059
|
-
import
|
|
8060
|
-
import
|
|
8395
|
+
import fs5 from "fs";
|
|
8396
|
+
import path4 from "path";
|
|
8397
|
+
import os4 from "os";
|
|
8061
8398
|
import { spawnSync, spawn } from "child_process";
|
|
8062
8399
|
import { createRequire as createRequire2 } from "module";
|
|
8063
8400
|
import { fileURLToPath } from "url";
|
|
8064
8401
|
var _require2 = createRequire2(import.meta.url);
|
|
8065
|
-
var __dirname =
|
|
8402
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
8066
8403
|
function readMcpVersion() {
|
|
8067
8404
|
const candidates = [
|
|
8068
8405
|
// Installed as global or local dependency
|
|
@@ -8115,13 +8452,13 @@ function checkMcpEntryPoint(report) {
|
|
|
8115
8452
|
if (!entryPath) {
|
|
8116
8453
|
const candidates = [
|
|
8117
8454
|
// Installed locally
|
|
8118
|
-
|
|
8455
|
+
path4.join(process.cwd(), "node_modules", ".bin", "okx-trade-mcp"),
|
|
8119
8456
|
// Monorepo workspace (e.g. running from source)
|
|
8120
|
-
|
|
8457
|
+
path4.join(__dirname, "..", "..", "..", "..", "mcp", "dist", "index.js")
|
|
8121
8458
|
];
|
|
8122
8459
|
for (const candidate of candidates) {
|
|
8123
8460
|
try {
|
|
8124
|
-
|
|
8461
|
+
fs5.accessSync(candidate, fs5.constants.X_OK | fs5.constants.R_OK);
|
|
8125
8462
|
entryPath = candidate;
|
|
8126
8463
|
break;
|
|
8127
8464
|
} catch (_e) {
|
|
@@ -8146,9 +8483,9 @@ var CLIENT_LIMITS = {
|
|
|
8146
8483
|
cursor: { perServer: 40, total: 80 }
|
|
8147
8484
|
};
|
|
8148
8485
|
function checkJsonMcpConfig(configPath) {
|
|
8149
|
-
if (!
|
|
8486
|
+
if (!fs5.existsSync(configPath)) return "missing";
|
|
8150
8487
|
try {
|
|
8151
|
-
const raw =
|
|
8488
|
+
const raw = fs5.readFileSync(configPath, "utf8");
|
|
8152
8489
|
const parsed = JSON.parse(raw);
|
|
8153
8490
|
const mcpServers = parsed["mcpServers"];
|
|
8154
8491
|
if (!mcpServers) return "not-configured";
|
|
@@ -8166,15 +8503,15 @@ function checkJsonMcpConfig(configPath) {
|
|
|
8166
8503
|
}
|
|
8167
8504
|
}
|
|
8168
8505
|
function checkClaudeCodeConfig() {
|
|
8169
|
-
const home =
|
|
8506
|
+
const home = os4.homedir();
|
|
8170
8507
|
const candidates = [
|
|
8171
|
-
|
|
8172
|
-
|
|
8508
|
+
path4.join(home, ".claude", "settings.json"),
|
|
8509
|
+
path4.join(home, ".claude.json")
|
|
8173
8510
|
];
|
|
8174
8511
|
let anyFound = false;
|
|
8175
8512
|
let anyParseError = false;
|
|
8176
8513
|
for (const cfgPath of candidates) {
|
|
8177
|
-
if (!
|
|
8514
|
+
if (!fs5.existsSync(cfgPath)) continue;
|
|
8178
8515
|
anyFound = true;
|
|
8179
8516
|
const result = checkJsonMcpConfig(cfgPath);
|
|
8180
8517
|
if (result === "found") return "found";
|
|
@@ -8191,8 +8528,8 @@ function handleJsonClient(clientId, report, configuredClients) {
|
|
|
8191
8528
|
const status = checkJsonMcpConfig(configPath);
|
|
8192
8529
|
if (status === "missing") return false;
|
|
8193
8530
|
if (status === "found") {
|
|
8194
|
-
ok(name, `configured (${
|
|
8195
|
-
report.add(`client_${clientId}`, `OK ${
|
|
8531
|
+
ok(name, `configured (${sanitize2(configPath)})`);
|
|
8532
|
+
report.add(`client_${clientId}`, `OK ${sanitize2(configPath)}`);
|
|
8196
8533
|
configuredClients.push(clientId);
|
|
8197
8534
|
return false;
|
|
8198
8535
|
}
|
|
@@ -8200,8 +8537,8 @@ function handleJsonClient(clientId, report, configuredClients) {
|
|
|
8200
8537
|
fail(name, "okx-trade-mcp not found in mcpServers", [`Run: okx setup --client ${clientId}`]);
|
|
8201
8538
|
report.add(`client_${clientId}`, "NOT_CONFIGURED");
|
|
8202
8539
|
} else {
|
|
8203
|
-
fail(name, `JSON parse error in ${
|
|
8204
|
-
`Check ${
|
|
8540
|
+
fail(name, `JSON parse error in ${sanitize2(configPath)}`, [
|
|
8541
|
+
`Check ${sanitize2(configPath)} for JSON syntax errors`,
|
|
8205
8542
|
`Then run: okx setup --client ${clientId}`
|
|
8206
8543
|
]);
|
|
8207
8544
|
report.add(`client_${clientId}`, "PARSE_ERROR");
|
|
@@ -8294,37 +8631,37 @@ function checkToolCount(report, configuredClients, getSpecs = allToolSpecs) {
|
|
|
8294
8631
|
}
|
|
8295
8632
|
}
|
|
8296
8633
|
function readLogTail(logPath) {
|
|
8297
|
-
const stat =
|
|
8634
|
+
const stat = fs5.statSync(logPath);
|
|
8298
8635
|
const readSize = Math.min(8192, stat.size);
|
|
8299
8636
|
const buffer = Buffer.alloc(readSize);
|
|
8300
|
-
const fd =
|
|
8637
|
+
const fd = fs5.openSync(logPath, "r");
|
|
8301
8638
|
try {
|
|
8302
|
-
|
|
8639
|
+
fs5.readSync(fd, buffer, 0, readSize, Math.max(0, stat.size - readSize));
|
|
8303
8640
|
} finally {
|
|
8304
|
-
|
|
8641
|
+
fs5.closeSync(fd);
|
|
8305
8642
|
}
|
|
8306
8643
|
return buffer.toString("utf8").split("\n").filter((l) => l.trim()).slice(-5);
|
|
8307
8644
|
}
|
|
8308
8645
|
function getMcpLogCandidates() {
|
|
8309
8646
|
if (process.platform === "darwin") {
|
|
8310
|
-
const logsDir =
|
|
8647
|
+
const logsDir = path4.join(os4.homedir(), "Library", "Logs", "Claude");
|
|
8311
8648
|
const candidates = [
|
|
8312
|
-
|
|
8313
|
-
|
|
8649
|
+
path4.join(logsDir, "mcp.log"),
|
|
8650
|
+
path4.join(logsDir, "mcp-server-okx-trade-mcp.log")
|
|
8314
8651
|
];
|
|
8315
8652
|
try {
|
|
8316
|
-
const extra =
|
|
8653
|
+
const extra = fs5.readdirSync(logsDir).filter((f) => f.startsWith("mcp") && f.endsWith(".log")).map((f) => path4.join(logsDir, f));
|
|
8317
8654
|
candidates.push(...extra);
|
|
8318
8655
|
} catch (_e) {
|
|
8319
8656
|
}
|
|
8320
8657
|
return candidates;
|
|
8321
8658
|
}
|
|
8322
8659
|
if (process.platform === "win32") {
|
|
8323
|
-
const appData2 = process.env.APPDATA ??
|
|
8324
|
-
return [
|
|
8660
|
+
const appData2 = process.env.APPDATA ?? path4.join(os4.homedir(), "AppData", "Roaming");
|
|
8661
|
+
return [path4.join(appData2, "Claude", "logs", "mcp.log")];
|
|
8325
8662
|
}
|
|
8326
|
-
const configHome = process.env.XDG_CONFIG_HOME ??
|
|
8327
|
-
return [
|
|
8663
|
+
const configHome = process.env.XDG_CONFIG_HOME ?? path4.join(os4.homedir(), ".config");
|
|
8664
|
+
return [path4.join(configHome, "Claude", "logs", "mcp.log")];
|
|
8328
8665
|
}
|
|
8329
8666
|
function checkMcpLogs(report) {
|
|
8330
8667
|
section("MCP Server Logs (recent)");
|
|
@@ -8338,7 +8675,7 @@ function checkMcpLogs(report) {
|
|
|
8338
8675
|
report.add("mcp_log", logPath);
|
|
8339
8676
|
if (lines.length > 0) {
|
|
8340
8677
|
ok("last lines", `(${lines.length} shown)`);
|
|
8341
|
-
for (const line of lines) outputLine(` ${
|
|
8678
|
+
for (const line of lines) outputLine(` ${sanitize2(line)}`);
|
|
8342
8679
|
} else {
|
|
8343
8680
|
ok("last lines", "(empty log)");
|
|
8344
8681
|
}
|
|
@@ -8464,11 +8801,11 @@ function checkModuleLoading(entryPath, report) {
|
|
|
8464
8801
|
return true;
|
|
8465
8802
|
} else {
|
|
8466
8803
|
const errMsg = result.stderr?.trim() || result.error?.message || "non-zero exit";
|
|
8467
|
-
fail("module load", `failed: ${
|
|
8804
|
+
fail("module load", `failed: ${sanitize2(errMsg)}`, [
|
|
8468
8805
|
"MCP server may have import errors or missing dependencies",
|
|
8469
8806
|
`Try: node ${entryPath} --version`
|
|
8470
8807
|
]);
|
|
8471
|
-
report.add("module_load", `FAIL ${
|
|
8808
|
+
report.add("module_load", `FAIL ${sanitize2(errMsg)}`);
|
|
8472
8809
|
return false;
|
|
8473
8810
|
}
|
|
8474
8811
|
}
|
|
@@ -8479,7 +8816,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8479
8816
|
const report = new Report();
|
|
8480
8817
|
report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
8481
8818
|
report.add("mode", "mcp");
|
|
8482
|
-
report.add("os", `${process.platform} ${process.arch} ${
|
|
8819
|
+
report.add("os", `${process.platform} ${process.arch} ${os4.release()}`);
|
|
8483
8820
|
checkMcpPackageVersion(report);
|
|
8484
8821
|
const nodePassed = checkNodeCompat(report);
|
|
8485
8822
|
const { entryPath, passed: entryPassed } = checkMcpEntryPoint(report);
|
|
@@ -8511,7 +8848,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8511
8848
|
|
|
8512
8849
|
// src/commands/diagnose.ts
|
|
8513
8850
|
var CLI_VERSION = readCliVersion();
|
|
8514
|
-
var GIT_HASH = true ? "
|
|
8851
|
+
var GIT_HASH = true ? "ee71999" : "dev";
|
|
8515
8852
|
function maskKey2(key) {
|
|
8516
8853
|
if (!key) return "(not set)";
|
|
8517
8854
|
if (key.length <= 8) return "****";
|
|
@@ -8591,14 +8928,14 @@ function checkEnvironment(report) {
|
|
|
8591
8928
|
}
|
|
8592
8929
|
ok("CLI", `v${CLI_VERSION} (${GIT_HASH})`);
|
|
8593
8930
|
ok("OS", `${process.platform} ${process.arch}`);
|
|
8594
|
-
ok("OS release",
|
|
8931
|
+
ok("OS release", os5.release());
|
|
8595
8932
|
ok("Shell", process.env.SHELL ?? "(unknown)");
|
|
8596
8933
|
ok("Locale", `${process.env.LANG ?? process.env.LC_ALL ?? "(unknown)"}`);
|
|
8597
8934
|
ok("Timezone", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
8598
8935
|
report.add("cli", `${CLI_VERSION} (${GIT_HASH})`);
|
|
8599
8936
|
report.add("node", `${nodeVersion} ${process.platform} ${process.arch}`);
|
|
8600
|
-
const machine = typeof
|
|
8601
|
-
report.add("os", `${
|
|
8937
|
+
const machine = typeof os5.machine === "function" ? os5.machine() : process.arch;
|
|
8938
|
+
report.add("os", `${os5.type()} ${os5.release()} ${machine}`);
|
|
8602
8939
|
report.add("shell", process.env.SHELL ?? "-");
|
|
8603
8940
|
report.add("locale", process.env.LANG ?? process.env.LC_ALL ?? "-");
|
|
8604
8941
|
report.add("tz", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
@@ -8755,10 +9092,10 @@ async function cmdDiagnose(config, profile, options = {}) {
|
|
|
8755
9092
|
}
|
|
8756
9093
|
function checkConfigFile(report) {
|
|
8757
9094
|
section("Config File");
|
|
8758
|
-
const
|
|
9095
|
+
const path6 = configFilePath();
|
|
8759
9096
|
try {
|
|
8760
9097
|
readFullConfig();
|
|
8761
|
-
ok("Config parse", `${
|
|
9098
|
+
ok("Config parse", `${path6} OK`);
|
|
8762
9099
|
report.add("config_parse", "OK");
|
|
8763
9100
|
return true;
|
|
8764
9101
|
} catch (e) {
|
|
@@ -8808,24 +9145,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
8808
9145
|
|
|
8809
9146
|
// src/commands/upgrade.ts
|
|
8810
9147
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
8811
|
-
import { readFileSync as
|
|
8812
|
-
import { dirname as
|
|
8813
|
-
import { homedir as
|
|
9148
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
9149
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
9150
|
+
import { homedir as homedir5 } from "os";
|
|
8814
9151
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
8815
|
-
var CACHE_FILE2 =
|
|
9152
|
+
var CACHE_FILE2 = join7(homedir5(), ".okx", "last_check");
|
|
8816
9153
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
8817
|
-
var NPM_BIN =
|
|
9154
|
+
var NPM_BIN = join7(dirname5(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
8818
9155
|
function readLastCheck() {
|
|
8819
9156
|
try {
|
|
8820
|
-
return parseInt(
|
|
9157
|
+
return parseInt(readFileSync6(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
8821
9158
|
} catch {
|
|
8822
9159
|
return 0;
|
|
8823
9160
|
}
|
|
8824
9161
|
}
|
|
8825
9162
|
function writeLastCheck() {
|
|
8826
9163
|
try {
|
|
8827
|
-
|
|
8828
|
-
|
|
9164
|
+
mkdirSync7(join7(homedir5(), ".okx"), { recursive: true });
|
|
9165
|
+
writeFileSync6(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
8829
9166
|
} catch {
|
|
8830
9167
|
}
|
|
8831
9168
|
}
|
|
@@ -8928,7 +9265,7 @@ function loadProfileConfig(opts) {
|
|
|
8928
9265
|
import { EOL as EOL2 } from "os";
|
|
8929
9266
|
|
|
8930
9267
|
// src/commands/client-setup.ts
|
|
8931
|
-
import * as
|
|
9268
|
+
import * as fs6 from "fs";
|
|
8932
9269
|
var DETECTABLE_CLIENTS = ["claude-desktop", "cursor", "windsurf"];
|
|
8933
9270
|
function cmdSetupClient(options) {
|
|
8934
9271
|
runSetup(options);
|
|
@@ -8937,14 +9274,14 @@ function cmdSetupClients() {
|
|
|
8937
9274
|
const detected = [];
|
|
8938
9275
|
for (const id of DETECTABLE_CLIENTS) {
|
|
8939
9276
|
const p = getConfigPath(id);
|
|
8940
|
-
if (p &&
|
|
9277
|
+
if (p && fs6.existsSync(p)) {
|
|
8941
9278
|
detected.push({ id, path: p });
|
|
8942
9279
|
}
|
|
8943
9280
|
}
|
|
8944
9281
|
if (detected.length > 0) {
|
|
8945
9282
|
outputLine("Detected clients:");
|
|
8946
|
-
for (const { id, path:
|
|
8947
|
-
outputLine(` ${id.padEnd(16)} ${
|
|
9283
|
+
for (const { id, path: path6 } of detected) {
|
|
9284
|
+
outputLine(` ${id.padEnd(16)} ${path6}`);
|
|
8948
9285
|
}
|
|
8949
9286
|
outputLine("");
|
|
8950
9287
|
}
|
|
@@ -9328,7 +9665,7 @@ var HELP_TREE = {
|
|
|
9328
9665
|
description: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
|
|
9329
9666
|
subgroups: {
|
|
9330
9667
|
savings: {
|
|
9331
|
-
description: "Simple Earn \u2014 flexible savings and lending",
|
|
9668
|
+
description: "Simple Earn \u2014 flexible savings, fixed-term, and lending",
|
|
9332
9669
|
commands: {
|
|
9333
9670
|
balance: {
|
|
9334
9671
|
usage: "okx earn savings balance [<ccy>]",
|
|
@@ -9352,7 +9689,19 @@ var HELP_TREE = {
|
|
|
9352
9689
|
},
|
|
9353
9690
|
"rate-history": {
|
|
9354
9691
|
usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
|
|
9355
|
-
description: "Query Simple Earn lending rates (
|
|
9692
|
+
description: "Query Simple Earn lending rates and fixed-term offers (requires auth)"
|
|
9693
|
+
},
|
|
9694
|
+
"fixed-orders": {
|
|
9695
|
+
usage: "okx earn savings fixed-orders [--ccy <ccy>] [--state <pending|earning|expired|settled|cancelled>]",
|
|
9696
|
+
description: "List fixed-term earn orders"
|
|
9697
|
+
},
|
|
9698
|
+
"fixed-purchase": {
|
|
9699
|
+
usage: "okx earn savings fixed-purchase --ccy <ccy> --amt <n> --term <term> [--confirm]",
|
|
9700
|
+
description: "Purchase Simple Earn Fixed (\u5B9A\u671F). Preview by default; add --confirm to execute. Funds locked until maturity"
|
|
9701
|
+
},
|
|
9702
|
+
"fixed-redeem": {
|
|
9703
|
+
usage: "okx earn savings fixed-redeem <reqId>",
|
|
9704
|
+
description: "Redeem a fixed-term earn order (full amount)"
|
|
9356
9705
|
}
|
|
9357
9706
|
}
|
|
9358
9707
|
},
|
|
@@ -9517,6 +9866,39 @@ var HELP_TREE = {
|
|
|
9517
9866
|
description: "Run network / MCP server diagnostics",
|
|
9518
9867
|
usage: "okx diagnose [--cli | --mcp | --all] [--profile <name>] [--demo | --live] [--output <file>]"
|
|
9519
9868
|
},
|
|
9869
|
+
skill: {
|
|
9870
|
+
description: "OKX Skills Marketplace \u2014 search, install, and manage agent skills",
|
|
9871
|
+
commands: {
|
|
9872
|
+
search: {
|
|
9873
|
+
usage: "okx skill search [--keyword <kw>] [--categories <id>] [--page <n>] [--limit <n>]",
|
|
9874
|
+
description: "Search for skills in the marketplace"
|
|
9875
|
+
},
|
|
9876
|
+
categories: {
|
|
9877
|
+
usage: "okx skill categories",
|
|
9878
|
+
description: "List available skill categories"
|
|
9879
|
+
},
|
|
9880
|
+
add: {
|
|
9881
|
+
usage: "okx skill add <name>",
|
|
9882
|
+
description: "Download and install a skill to detected agents"
|
|
9883
|
+
},
|
|
9884
|
+
download: {
|
|
9885
|
+
usage: "okx skill download <name> [--dir <path>] [--format zip|skill]",
|
|
9886
|
+
description: "Download a skill package without installing"
|
|
9887
|
+
},
|
|
9888
|
+
remove: {
|
|
9889
|
+
usage: "okx skill remove <name>",
|
|
9890
|
+
description: "Remove an installed skill"
|
|
9891
|
+
},
|
|
9892
|
+
check: {
|
|
9893
|
+
usage: "okx skill check <name>",
|
|
9894
|
+
description: "Check if an installed skill has a newer version"
|
|
9895
|
+
},
|
|
9896
|
+
list: {
|
|
9897
|
+
usage: "okx skill list",
|
|
9898
|
+
description: "List all locally installed skills"
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
},
|
|
9520
9902
|
upgrade: {
|
|
9521
9903
|
description: "Upgrade okx CLI and MCP server to the latest stable version",
|
|
9522
9904
|
usage: "okx upgrade [--check] [--beta] [--force] [--json]"
|
|
@@ -9532,6 +9914,7 @@ function printGlobalHelp() {
|
|
|
9532
9914
|
" --demo Use simulated trading (demo) mode",
|
|
9533
9915
|
" --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)",
|
|
9534
9916
|
" --json Output raw JSON",
|
|
9917
|
+
" --env With --json, wrap output as {env, profile, data}",
|
|
9535
9918
|
" --verbose Show detailed network request/response info (stderr)",
|
|
9536
9919
|
" --version, -v Show version",
|
|
9537
9920
|
" --help Show this help",
|
|
@@ -9646,8 +10029,8 @@ function printCommandList(lines, commands) {
|
|
|
9646
10029
|
lines.push("");
|
|
9647
10030
|
}
|
|
9648
10031
|
}
|
|
9649
|
-
function printHelp(...
|
|
9650
|
-
const [moduleName, subgroupName] =
|
|
10032
|
+
function printHelp(...path6) {
|
|
10033
|
+
const [moduleName, subgroupName] = path6;
|
|
9651
10034
|
if (!moduleName) {
|
|
9652
10035
|
printGlobalHelp();
|
|
9653
10036
|
} else if (!subgroupName) {
|
|
@@ -9663,6 +10046,7 @@ var CLI_OPTIONS = {
|
|
|
9663
10046
|
profile: { type: "string" },
|
|
9664
10047
|
demo: { type: "boolean", default: false },
|
|
9665
10048
|
json: { type: "boolean", default: false },
|
|
10049
|
+
env: { type: "boolean", default: false },
|
|
9666
10050
|
help: { type: "boolean", default: false },
|
|
9667
10051
|
version: { type: "boolean", short: "v", default: false },
|
|
9668
10052
|
// setup command
|
|
@@ -9768,6 +10152,8 @@ var CLI_OPTIONS = {
|
|
|
9768
10152
|
orders: { type: "string" },
|
|
9769
10153
|
// earn
|
|
9770
10154
|
rate: { type: "string" },
|
|
10155
|
+
reqId: { type: "string" },
|
|
10156
|
+
confirm: { type: "boolean", default: false },
|
|
9771
10157
|
// audit
|
|
9772
10158
|
since: { type: "string" },
|
|
9773
10159
|
tool: { type: "string" },
|
|
@@ -9808,6 +10194,7 @@ var CLI_OPTIONS = {
|
|
|
9808
10194
|
categories: { type: "string" },
|
|
9809
10195
|
dir: { type: "string" },
|
|
9810
10196
|
page: { type: "string" },
|
|
10197
|
+
format: { type: "string" },
|
|
9811
10198
|
// diagnostics — cli/mcp/all/output are diagnose-specific; verbose is shared
|
|
9812
10199
|
verbose: { type: "boolean", default: false },
|
|
9813
10200
|
mcp: { type: "boolean", default: false },
|
|
@@ -9847,7 +10234,7 @@ function getData(result) {
|
|
|
9847
10234
|
return result.data;
|
|
9848
10235
|
}
|
|
9849
10236
|
async function cmdMarketInstruments(run, opts) {
|
|
9850
|
-
const result = await run("market_get_instruments", { instType: opts.instType, instId: opts.instId });
|
|
10237
|
+
const result = await run("market_get_instruments", { instType: opts.instType, instId: opts.instId, demo: opts.demo ?? false });
|
|
9851
10238
|
const items = getData(result);
|
|
9852
10239
|
if (opts.json) return printJson(items);
|
|
9853
10240
|
printTable(
|
|
@@ -9862,7 +10249,7 @@ async function cmdMarketInstruments(run, opts) {
|
|
|
9862
10249
|
);
|
|
9863
10250
|
}
|
|
9864
10251
|
async function cmdMarketFundingRate(run, instId, opts) {
|
|
9865
|
-
const result = await run("market_get_funding_rate", { instId, history: opts.history, limit: opts.limit });
|
|
10252
|
+
const result = await run("market_get_funding_rate", { instId, history: opts.history, limit: opts.limit, demo: opts.demo ?? false });
|
|
9866
10253
|
const items = getData(result);
|
|
9867
10254
|
if (opts.json) return printJson(items);
|
|
9868
10255
|
if (opts.history) {
|
|
@@ -9890,7 +10277,7 @@ async function cmdMarketFundingRate(run, instId, opts) {
|
|
|
9890
10277
|
}
|
|
9891
10278
|
}
|
|
9892
10279
|
async function cmdMarketMarkPrice(run, opts) {
|
|
9893
|
-
const result = await run("market_get_mark_price", { instType: opts.instType, instId: opts.instId });
|
|
10280
|
+
const result = await run("market_get_mark_price", { instType: opts.instType, instId: opts.instId, demo: opts.demo ?? false });
|
|
9894
10281
|
const items = getData(result);
|
|
9895
10282
|
if (opts.json) return printJson(items);
|
|
9896
10283
|
printTable(
|
|
@@ -9903,7 +10290,7 @@ async function cmdMarketMarkPrice(run, opts) {
|
|
|
9903
10290
|
);
|
|
9904
10291
|
}
|
|
9905
10292
|
async function cmdMarketTrades(run, instId, opts) {
|
|
9906
|
-
const result = await run("market_get_trades", { instId, limit: opts.limit });
|
|
10293
|
+
const result = await run("market_get_trades", { instId, limit: opts.limit, demo: opts.demo ?? false });
|
|
9907
10294
|
const items = getData(result);
|
|
9908
10295
|
if (opts.json) return printJson(items);
|
|
9909
10296
|
printTable(
|
|
@@ -9917,7 +10304,7 @@ async function cmdMarketTrades(run, instId, opts) {
|
|
|
9917
10304
|
);
|
|
9918
10305
|
}
|
|
9919
10306
|
async function cmdMarketIndexTicker(run, opts) {
|
|
9920
|
-
const result = await run("market_get_index_ticker", { instId: opts.instId, quoteCcy: opts.quoteCcy });
|
|
10307
|
+
const result = await run("market_get_index_ticker", { instId: opts.instId, quoteCcy: opts.quoteCcy, demo: opts.demo ?? false });
|
|
9921
10308
|
const items = getData(result);
|
|
9922
10309
|
if (opts.json) return printJson(items);
|
|
9923
10310
|
printTable(
|
|
@@ -9931,7 +10318,7 @@ async function cmdMarketIndexTicker(run, opts) {
|
|
|
9931
10318
|
);
|
|
9932
10319
|
}
|
|
9933
10320
|
async function cmdMarketIndexCandles(run, instId, opts) {
|
|
9934
|
-
const result = await run("market_get_index_candles", { instId, bar: opts.bar, limit: opts.limit, history: opts.history });
|
|
10321
|
+
const result = await run("market_get_index_candles", { instId, bar: opts.bar, limit: opts.limit, history: opts.history, demo: opts.demo ?? false });
|
|
9935
10322
|
const candles = getData(result);
|
|
9936
10323
|
if (opts.json) return printJson(candles);
|
|
9937
10324
|
printTable(
|
|
@@ -9944,8 +10331,8 @@ async function cmdMarketIndexCandles(run, instId, opts) {
|
|
|
9944
10331
|
}))
|
|
9945
10332
|
);
|
|
9946
10333
|
}
|
|
9947
|
-
async function cmdMarketPriceLimit(run, instId, json) {
|
|
9948
|
-
const result = await run("market_get_price_limit", { instId });
|
|
10334
|
+
async function cmdMarketPriceLimit(run, instId, json, demo) {
|
|
10335
|
+
const result = await run("market_get_price_limit", { instId, demo: demo ?? false });
|
|
9949
10336
|
const items = getData(result);
|
|
9950
10337
|
if (json) return printJson(items);
|
|
9951
10338
|
const r = items?.[0];
|
|
@@ -9961,7 +10348,7 @@ async function cmdMarketPriceLimit(run, instId, json) {
|
|
|
9961
10348
|
});
|
|
9962
10349
|
}
|
|
9963
10350
|
async function cmdMarketOpenInterest(run, opts) {
|
|
9964
|
-
const result = await run("market_get_open_interest", { instType: opts.instType, instId: opts.instId });
|
|
10351
|
+
const result = await run("market_get_open_interest", { instType: opts.instType, instId: opts.instId, demo: opts.demo ?? false });
|
|
9965
10352
|
const items = getData(result);
|
|
9966
10353
|
if (opts.json) return printJson(items);
|
|
9967
10354
|
printTable(
|
|
@@ -9973,8 +10360,8 @@ async function cmdMarketOpenInterest(run, opts) {
|
|
|
9973
10360
|
}))
|
|
9974
10361
|
);
|
|
9975
10362
|
}
|
|
9976
|
-
async function cmdMarketTicker(run, instId, json) {
|
|
9977
|
-
const result = await run("market_get_ticker", { instId });
|
|
10363
|
+
async function cmdMarketTicker(run, instId, json, demo) {
|
|
10364
|
+
const result = await run("market_get_ticker", { instId, demo: demo ?? false });
|
|
9978
10365
|
const items = getData(result);
|
|
9979
10366
|
if (json) return printJson(items);
|
|
9980
10367
|
if (!items?.length) {
|
|
@@ -9997,8 +10384,8 @@ async function cmdMarketTicker(run, instId, json) {
|
|
|
9997
10384
|
time: new Date(Number(t["ts"])).toLocaleString()
|
|
9998
10385
|
});
|
|
9999
10386
|
}
|
|
10000
|
-
async function cmdMarketTickers(run, instType, json) {
|
|
10001
|
-
const result = await run("market_get_tickers", { instType });
|
|
10387
|
+
async function cmdMarketTickers(run, instType, json, demo) {
|
|
10388
|
+
const result = await run("market_get_tickers", { instType, demo: demo ?? false });
|
|
10002
10389
|
const items = getData(result);
|
|
10003
10390
|
if (json) return printJson(items);
|
|
10004
10391
|
printTable(
|
|
@@ -10011,8 +10398,8 @@ async function cmdMarketTickers(run, instType, json) {
|
|
|
10011
10398
|
}))
|
|
10012
10399
|
);
|
|
10013
10400
|
}
|
|
10014
|
-
async function cmdMarketOrderbook(run, instId, sz, json) {
|
|
10015
|
-
const result = await run("market_get_orderbook", { instId, sz });
|
|
10401
|
+
async function cmdMarketOrderbook(run, instId, sz, json, demo) {
|
|
10402
|
+
const result = await run("market_get_orderbook", { instId, sz, demo: demo ?? false });
|
|
10016
10403
|
const data = getData(result);
|
|
10017
10404
|
if (json) return printJson(data);
|
|
10018
10405
|
const book = data[0];
|
|
@@ -10029,7 +10416,7 @@ async function cmdMarketOrderbook(run, instId, sz, json) {
|
|
|
10029
10416
|
for (const [p, s] of bids) outputLine(` ${p.padStart(16)} ${s}`);
|
|
10030
10417
|
}
|
|
10031
10418
|
async function cmdMarketCandles(run, instId, opts) {
|
|
10032
|
-
const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit, after: opts.after, before: opts.before });
|
|
10419
|
+
const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit, after: opts.after, before: opts.before, demo: opts.demo ?? false });
|
|
10033
10420
|
const candles = getData(result);
|
|
10034
10421
|
if (opts.json) return printJson(candles);
|
|
10035
10422
|
printTable(
|
|
@@ -10043,6 +10430,10 @@ async function cmdMarketCandles(run, instId, opts) {
|
|
|
10043
10430
|
}))
|
|
10044
10431
|
);
|
|
10045
10432
|
}
|
|
10433
|
+
function cmdMarketIndicatorList(json) {
|
|
10434
|
+
if (json) return printJson(KNOWN_INDICATORS);
|
|
10435
|
+
printTable(KNOWN_INDICATORS.map(({ name, description }) => ({ name, description })));
|
|
10436
|
+
}
|
|
10046
10437
|
async function cmdMarketIndicator(run, indicator, instId, opts) {
|
|
10047
10438
|
const params = opts.params ? opts.params.split(",").map((p) => Number(p.trim())).filter((n) => !Number.isNaN(n)) : void 0;
|
|
10048
10439
|
const result = await run("market_get_indicator", {
|
|
@@ -10095,7 +10486,8 @@ async function cmdMarketInstrumentsByCategory(run, opts) {
|
|
|
10095
10486
|
const result = await run("market_get_instruments_by_category", {
|
|
10096
10487
|
instCategory: opts.instCategory,
|
|
10097
10488
|
instType: opts.instType,
|
|
10098
|
-
instId: opts.instId
|
|
10489
|
+
instId: opts.instId,
|
|
10490
|
+
demo: opts.demo ?? false
|
|
10099
10491
|
});
|
|
10100
10492
|
const items = getData(result);
|
|
10101
10493
|
if (opts.json) return printJson(items);
|
|
@@ -10123,7 +10515,7 @@ async function cmdMarketInstrumentsByCategory(run, opts) {
|
|
|
10123
10515
|
);
|
|
10124
10516
|
}
|
|
10125
10517
|
async function cmdMarketStockTokens(run, opts) {
|
|
10126
|
-
const result = await run("market_get_stock_tokens", { instType: opts.instType, instId: opts.instId });
|
|
10518
|
+
const result = await run("market_get_stock_tokens", { instType: opts.instType, instId: opts.instId, demo: opts.demo ?? false });
|
|
10127
10519
|
const items = getData(result);
|
|
10128
10520
|
if (opts.json) return printJson(items);
|
|
10129
10521
|
printTable(
|
|
@@ -10140,9 +10532,9 @@ async function cmdMarketStockTokens(run, opts) {
|
|
|
10140
10532
|
}
|
|
10141
10533
|
|
|
10142
10534
|
// src/commands/account.ts
|
|
10143
|
-
import * as
|
|
10144
|
-
import * as
|
|
10145
|
-
import * as
|
|
10535
|
+
import * as fs7 from "fs";
|
|
10536
|
+
import * as path5 from "path";
|
|
10537
|
+
import * as os6 from "os";
|
|
10146
10538
|
function getData2(result) {
|
|
10147
10539
|
return result.data;
|
|
10148
10540
|
}
|
|
@@ -10353,10 +10745,10 @@ function readAuditLogs(logDir, days = 7) {
|
|
|
10353
10745
|
const yyyy = d.getUTCFullYear();
|
|
10354
10746
|
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
10355
10747
|
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
10356
|
-
const filePath =
|
|
10748
|
+
const filePath = path5.join(logDir, `trade-${yyyy}-${mm}-${dd}.log`);
|
|
10357
10749
|
let content;
|
|
10358
10750
|
try {
|
|
10359
|
-
content =
|
|
10751
|
+
content = fs7.readFileSync(filePath, "utf8");
|
|
10360
10752
|
} catch {
|
|
10361
10753
|
continue;
|
|
10362
10754
|
}
|
|
@@ -10372,7 +10764,7 @@ function readAuditLogs(logDir, days = 7) {
|
|
|
10372
10764
|
return entries;
|
|
10373
10765
|
}
|
|
10374
10766
|
function cmdAccountAudit(opts) {
|
|
10375
|
-
const logDir =
|
|
10767
|
+
const logDir = path5.join(os6.homedir(), ".okx", "logs");
|
|
10376
10768
|
const limit = Math.min(Number(opts.limit) || 20, 100);
|
|
10377
10769
|
let entries = readAuditLogs(logDir);
|
|
10378
10770
|
if (opts.tool) entries = entries.filter((e) => e.tool === opts.tool);
|
|
@@ -11360,6 +11752,7 @@ async function cmdOptionPlace(run, opts) {
|
|
|
11360
11752
|
side: opts.side,
|
|
11361
11753
|
ordType: opts.ordType,
|
|
11362
11754
|
sz: opts.sz,
|
|
11755
|
+
tgtCcy: opts.tgtCcy,
|
|
11363
11756
|
px: opts.px,
|
|
11364
11757
|
reduceOnly: opts.reduceOnly,
|
|
11365
11758
|
clOrdId: opts.clOrdId,
|
|
@@ -11827,6 +12220,22 @@ async function cmdEarnSavingsBalance(run, ccy, json) {
|
|
|
11827
12220
|
pendingAmt: r["pendingAmt"]
|
|
11828
12221
|
}));
|
|
11829
12222
|
}
|
|
12223
|
+
async function cmdEarnFixedOrderList(run, opts) {
|
|
12224
|
+
const data = extractData(await run("earn_get_fixed_order_list", {
|
|
12225
|
+
ccy: opts.ccy,
|
|
12226
|
+
state: opts.state
|
|
12227
|
+
}));
|
|
12228
|
+
printDataList(data, opts.json, "No fixed earn orders", (r) => ({
|
|
12229
|
+
reqId: r["reqId"],
|
|
12230
|
+
ccy: r["ccy"],
|
|
12231
|
+
amt: r["amt"],
|
|
12232
|
+
rate: r["rate"],
|
|
12233
|
+
term: r["term"],
|
|
12234
|
+
state: r["state"],
|
|
12235
|
+
accruedInterest: r["accruedInterest"],
|
|
12236
|
+
cTime: new Date(Number(r["cTime"])).toLocaleString()
|
|
12237
|
+
}));
|
|
12238
|
+
}
|
|
11830
12239
|
async function cmdEarnSavingsPurchase(run, opts) {
|
|
11831
12240
|
const data = extractData(await run("earn_savings_purchase", { ccy: opts.ccy, amt: opts.amt, rate: opts.rate }));
|
|
11832
12241
|
if (opts.json) {
|
|
@@ -11872,14 +12281,107 @@ async function cmdEarnLendingHistory(run, opts) {
|
|
|
11872
12281
|
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
11873
12282
|
}));
|
|
11874
12283
|
}
|
|
12284
|
+
function printFixedPurchasePreview(rec) {
|
|
12285
|
+
const offer = rec["offer"];
|
|
12286
|
+
outputLine("");
|
|
12287
|
+
outputLine("\u{1F4CB} Fixed Earn Purchase Preview");
|
|
12288
|
+
outputLine(` Currency: ${rec["ccy"]}`);
|
|
12289
|
+
outputLine(` Amount: ${rec["amt"]}`);
|
|
12290
|
+
outputLine(` Term: ${rec["term"]}`);
|
|
12291
|
+
if (rec["currentFlexibleRate"]) {
|
|
12292
|
+
outputLine(` Current flexible rate: ${rec["currentFlexibleRate"]}`);
|
|
12293
|
+
}
|
|
12294
|
+
if (offer) {
|
|
12295
|
+
printKv({
|
|
12296
|
+
rate: offer["rate"],
|
|
12297
|
+
minLend: offer["minLend"],
|
|
12298
|
+
remainingQuota: offer["lendQuota"],
|
|
12299
|
+
soldOut: offer["soldOut"] ? "Yes" : "No"
|
|
12300
|
+
});
|
|
12301
|
+
} else {
|
|
12302
|
+
outputLine(" \u26A0\uFE0F No matching offer found for this term.");
|
|
12303
|
+
}
|
|
12304
|
+
outputLine("");
|
|
12305
|
+
outputLine(rec["warning"] ?? "");
|
|
12306
|
+
outputLine("");
|
|
12307
|
+
outputLine("Re-run with --confirm to execute.");
|
|
12308
|
+
}
|
|
12309
|
+
async function cmdEarnFixedPurchase(run, opts) {
|
|
12310
|
+
const result = await run("earn_fixed_purchase", {
|
|
12311
|
+
ccy: opts.ccy,
|
|
12312
|
+
amt: opts.amt,
|
|
12313
|
+
term: opts.term,
|
|
12314
|
+
confirm: opts.confirm
|
|
12315
|
+
});
|
|
12316
|
+
if (!result || typeof result !== "object") {
|
|
12317
|
+
outputLine("No response data");
|
|
12318
|
+
return;
|
|
12319
|
+
}
|
|
12320
|
+
const rec = result;
|
|
12321
|
+
if (rec["preview"]) {
|
|
12322
|
+
if (opts.json) {
|
|
12323
|
+
printJson(rec);
|
|
12324
|
+
return;
|
|
12325
|
+
}
|
|
12326
|
+
printFixedPurchasePreview(rec);
|
|
12327
|
+
return;
|
|
12328
|
+
}
|
|
12329
|
+
const data = extractData(result);
|
|
12330
|
+
if (opts.json) {
|
|
12331
|
+
printJson(data);
|
|
12332
|
+
return;
|
|
12333
|
+
}
|
|
12334
|
+
const r = data[0];
|
|
12335
|
+
if (!r) {
|
|
12336
|
+
outputLine("No response data");
|
|
12337
|
+
return;
|
|
12338
|
+
}
|
|
12339
|
+
printKv({ reqId: r["reqId"], ccy: r["ccy"], amt: r["amt"], term: r["term"] });
|
|
12340
|
+
}
|
|
12341
|
+
async function cmdEarnFixedRedeem(run, opts) {
|
|
12342
|
+
const data = extractData(await run("earn_fixed_redeem", { reqId: opts.reqId }));
|
|
12343
|
+
if (opts.json) {
|
|
12344
|
+
printJson(data);
|
|
12345
|
+
return;
|
|
12346
|
+
}
|
|
12347
|
+
if (!data.length) {
|
|
12348
|
+
outputLine("No response data");
|
|
12349
|
+
return;
|
|
12350
|
+
}
|
|
12351
|
+
printTable(data.map((r) => ({ reqId: r["reqId"] })));
|
|
12352
|
+
}
|
|
11875
12353
|
async function cmdEarnLendingRateHistory(run, opts) {
|
|
11876
|
-
const
|
|
11877
|
-
|
|
12354
|
+
const result = await run("earn_get_lending_rate_history", { ccy: opts.ccy, limit: opts.limit });
|
|
12355
|
+
const data = extractData(result);
|
|
12356
|
+
const fixedOffers = extractFixedOffers(result);
|
|
12357
|
+
if (opts.json) {
|
|
12358
|
+
printJson({ data, fixedOffers });
|
|
12359
|
+
return;
|
|
12360
|
+
}
|
|
12361
|
+
printDataList(data, false, "No rate history data", (r) => ({
|
|
11878
12362
|
ccy: r["ccy"],
|
|
11879
12363
|
lendingRate: r["lendingRate"],
|
|
11880
|
-
rate: r["rate"],
|
|
11881
12364
|
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
11882
12365
|
}));
|
|
12366
|
+
if (fixedOffers.length > 0) {
|
|
12367
|
+
outputLine("");
|
|
12368
|
+
outputLine("Fixed-term offers:");
|
|
12369
|
+
printTable(fixedOffers.map((r) => ({
|
|
12370
|
+
ccy: r["ccy"],
|
|
12371
|
+
term: r["term"],
|
|
12372
|
+
rate: r["rate"],
|
|
12373
|
+
minLend: r["minLend"],
|
|
12374
|
+
remainingQuota: r["lendQuota"],
|
|
12375
|
+
soldOut: r["soldOut"] ? "Yes" : "No"
|
|
12376
|
+
})));
|
|
12377
|
+
}
|
|
12378
|
+
}
|
|
12379
|
+
function extractFixedOffers(result) {
|
|
12380
|
+
if (result && typeof result === "object") {
|
|
12381
|
+
const offers = result["fixedOffers"];
|
|
12382
|
+
if (Array.isArray(offers)) return offers;
|
|
12383
|
+
}
|
|
12384
|
+
return [];
|
|
11883
12385
|
}
|
|
11884
12386
|
|
|
11885
12387
|
// src/commands/auto-earn.ts
|
|
@@ -12406,7 +12908,7 @@ async function cmdDcdRedeemExecute(run, opts) {
|
|
|
12406
12908
|
ordId: r["ordId"],
|
|
12407
12909
|
state: r["state"],
|
|
12408
12910
|
redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "\u2014",
|
|
12409
|
-
termRate: q["termRate"] ? `${q["termRate"]}%` : "\u2014"
|
|
12911
|
+
termRate: q["termRate"] ? `${(parseFloat(q["termRate"]) * 100).toFixed(2)}%` : "\u2014"
|
|
12410
12912
|
});
|
|
12411
12913
|
}
|
|
12412
12914
|
async function cmdDcdOrderState(run, opts) {
|
|
@@ -12459,7 +12961,7 @@ async function cmdDcdOrders(run, opts) {
|
|
|
12459
12961
|
quoteCcy: r["quoteCcy"],
|
|
12460
12962
|
strike: r["strike"],
|
|
12461
12963
|
notionalSz: r["notionalSz"],
|
|
12462
|
-
annualizedYield: r["annualizedYield"],
|
|
12964
|
+
annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
|
|
12463
12965
|
yieldSz: r["yieldSz"],
|
|
12464
12966
|
settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : "",
|
|
12465
12967
|
// scheduled settlement time
|
|
@@ -12499,7 +13001,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12499
13001
|
outputLine("Quote:");
|
|
12500
13002
|
printKv({
|
|
12501
13003
|
quoteId: q["quoteId"],
|
|
12502
|
-
annualizedYield: q["annualizedYield"] ? `${q["annualizedYield"]}%` : "\u2014",
|
|
13004
|
+
annualizedYield: q["annualizedYield"] ? `${(parseFloat(q["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
|
|
12503
13005
|
absYield: q["absYield"],
|
|
12504
13006
|
notionalSz: q["notionalSz"],
|
|
12505
13007
|
notionalCcy: q["notionalCcy"]
|
|
@@ -12523,16 +13025,17 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12523
13025
|
}
|
|
12524
13026
|
|
|
12525
13027
|
// src/commands/skill.ts
|
|
12526
|
-
import { tmpdir, homedir as
|
|
12527
|
-
import { join as
|
|
12528
|
-
import { mkdirSync as
|
|
13028
|
+
import { tmpdir, homedir as homedir7 } from "os";
|
|
13029
|
+
import { join as join9, dirname as dirname6 } from "path";
|
|
13030
|
+
import { mkdirSync as mkdirSync8, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
|
|
12529
13031
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12530
13032
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
12531
13033
|
function resolveNpx() {
|
|
12532
|
-
const sibling =
|
|
13034
|
+
const sibling = join9(dirname6(process.execPath), "npx");
|
|
12533
13035
|
if (existsSync7(sibling)) return sibling;
|
|
12534
13036
|
return "npx";
|
|
12535
13037
|
}
|
|
13038
|
+
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
12536
13039
|
async function cmdSkillSearch(run, opts) {
|
|
12537
13040
|
const args = {};
|
|
12538
13041
|
if (opts.keyword) args.keyword = opts.keyword;
|
|
@@ -12582,13 +13085,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
12582
13085
|
outputLine("");
|
|
12583
13086
|
}
|
|
12584
13087
|
async function cmdSkillAdd(name, config, json) {
|
|
12585
|
-
const tmpBase =
|
|
12586
|
-
|
|
13088
|
+
const tmpBase = join9(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
13089
|
+
mkdirSync8(tmpBase, { recursive: true });
|
|
12587
13090
|
try {
|
|
12588
13091
|
outputLine(`Downloading ${name}...`);
|
|
12589
13092
|
const client = new OkxRestClient(config);
|
|
12590
13093
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
12591
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
13094
|
+
const contentDir = await extractSkillZip(zipPath, join9(tmpBase, "content"));
|
|
12592
13095
|
const meta = readMetaJson(contentDir);
|
|
12593
13096
|
validateSkillMdExists(contentDir);
|
|
12594
13097
|
outputLine("Installing to detected agents...");
|
|
@@ -12598,7 +13101,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
12598
13101
|
timeout: 6e4
|
|
12599
13102
|
});
|
|
12600
13103
|
} catch (e) {
|
|
12601
|
-
const savedZip =
|
|
13104
|
+
const savedZip = join9(process.cwd(), `${name}.zip`);
|
|
12602
13105
|
try {
|
|
12603
13106
|
copyFileSync2(zipPath, savedZip);
|
|
12604
13107
|
} catch {
|
|
@@ -12608,23 +13111,19 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
12608
13111
|
throw e;
|
|
12609
13112
|
}
|
|
12610
13113
|
upsertSkillRecord(meta);
|
|
12611
|
-
|
|
12612
|
-
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
12613
|
-
} else {
|
|
12614
|
-
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
12615
|
-
}
|
|
13114
|
+
printSkillInstallResult(meta, json);
|
|
12616
13115
|
} finally {
|
|
12617
13116
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
12618
13117
|
}
|
|
12619
13118
|
}
|
|
12620
|
-
async function cmdSkillDownload(name, targetDir, config, json) {
|
|
13119
|
+
async function cmdSkillDownload(name, targetDir, config, json, format = "zip") {
|
|
12621
13120
|
outputLine(`Downloading ${name}...`);
|
|
12622
13121
|
const client = new OkxRestClient(config);
|
|
12623
|
-
const filePath = await downloadSkillZip(client, name, targetDir);
|
|
13122
|
+
const filePath = await downloadSkillZip(client, name, targetDir, format);
|
|
12624
13123
|
if (json) {
|
|
12625
13124
|
outputLine(JSON.stringify({ name, filePath }, null, 2));
|
|
12626
13125
|
} else {
|
|
12627
|
-
outputLine(`\u2713 Downloaded ${name}
|
|
13126
|
+
outputLine(`\u2713 Downloaded ${name}.${format}`);
|
|
12628
13127
|
outputLine(` Path: ${filePath}`);
|
|
12629
13128
|
}
|
|
12630
13129
|
}
|
|
@@ -12641,7 +13140,7 @@ function cmdSkillRemove(name, json) {
|
|
|
12641
13140
|
timeout: 6e4
|
|
12642
13141
|
});
|
|
12643
13142
|
} catch {
|
|
12644
|
-
const agentsPath =
|
|
13143
|
+
const agentsPath = join9(homedir7(), ".agents", "skills", name);
|
|
12645
13144
|
try {
|
|
12646
13145
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
12647
13146
|
} catch {
|
|
@@ -12705,11 +13204,19 @@ function cmdSkillList(json) {
|
|
|
12705
13204
|
outputLine("");
|
|
12706
13205
|
outputLine(`${skills.length} skills installed.`);
|
|
12707
13206
|
}
|
|
13207
|
+
function printSkillInstallResult(meta, json) {
|
|
13208
|
+
if (json) {
|
|
13209
|
+
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
13210
|
+
} else {
|
|
13211
|
+
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
13212
|
+
outputLine(` ${THIRD_PARTY_INSTALL_NOTICE}`);
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
12708
13215
|
|
|
12709
13216
|
// src/index.ts
|
|
12710
13217
|
var _require3 = createRequire3(import.meta.url);
|
|
12711
13218
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
12712
|
-
var GIT_HASH2 = true ? "
|
|
13219
|
+
var GIT_HASH2 = true ? "ee71999" : "dev";
|
|
12713
13220
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
12714
13221
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
12715
13222
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -12739,51 +13246,54 @@ function handleSetupCommand(v) {
|
|
|
12739
13246
|
});
|
|
12740
13247
|
}
|
|
12741
13248
|
function handleMarketPublicCommand(run, action, rest, v, json) {
|
|
12742
|
-
if (action === "ticker") return cmdMarketTicker(run, rest[0], json);
|
|
12743
|
-
if (action === "tickers") return cmdMarketTickers(run, rest[0], json);
|
|
13249
|
+
if (action === "ticker") return cmdMarketTicker(run, rest[0], json, v.demo);
|
|
13250
|
+
if (action === "tickers") return cmdMarketTickers(run, rest[0], json, v.demo);
|
|
12744
13251
|
if (action === "instruments")
|
|
12745
|
-
return cmdMarketInstruments(run, { instType: v.instType, instId: v.instId, json });
|
|
13252
|
+
return cmdMarketInstruments(run, { instType: v.instType, instId: v.instId, json, demo: v.demo });
|
|
12746
13253
|
if (action === "mark-price")
|
|
12747
|
-
return cmdMarketMarkPrice(run, { instType: v.instType, instId: v.instId, json });
|
|
13254
|
+
return cmdMarketMarkPrice(run, { instType: v.instType, instId: v.instId, json, demo: v.demo });
|
|
12748
13255
|
if (action === "index-ticker")
|
|
12749
|
-
return cmdMarketIndexTicker(run, { instId: v.instId, quoteCcy: v.quoteCcy, json });
|
|
12750
|
-
if (action === "price-limit") return cmdMarketPriceLimit(run, rest[0], json);
|
|
13256
|
+
return cmdMarketIndexTicker(run, { instId: v.instId, quoteCcy: v.quoteCcy, json, demo: v.demo });
|
|
13257
|
+
if (action === "price-limit") return cmdMarketPriceLimit(run, rest[0], json, v.demo);
|
|
12751
13258
|
if (action === "open-interest")
|
|
12752
|
-
return cmdMarketOpenInterest(run, { instType: v.instType, instId: v.instId, json });
|
|
13259
|
+
return cmdMarketOpenInterest(run, { instType: v.instType, instId: v.instId, json, demo: v.demo });
|
|
12753
13260
|
if (action === "stock-tokens")
|
|
12754
|
-
return cmdMarketStockTokens(run, { instType: v.instType, instId: v.instId, json });
|
|
13261
|
+
return cmdMarketStockTokens(run, { instType: v.instType, instId: v.instId, json, demo: v.demo });
|
|
12755
13262
|
if (action === "instruments-by-category")
|
|
12756
13263
|
return cmdMarketInstrumentsByCategory(run, {
|
|
12757
13264
|
instCategory: v.instCategory,
|
|
12758
13265
|
instType: v.instType,
|
|
12759
13266
|
instId: v.instId,
|
|
12760
|
-
json
|
|
13267
|
+
json,
|
|
13268
|
+
demo: v.demo
|
|
12761
13269
|
});
|
|
12762
|
-
if (action === "indicator")
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
13270
|
+
if (action === "indicator") return handleIndicatorAction(run, rest, v, json);
|
|
13271
|
+
}
|
|
13272
|
+
function handleIndicatorAction(run, rest, v, json) {
|
|
13273
|
+
if (rest[0] === "list") return cmdMarketIndicatorList(json);
|
|
13274
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
13275
|
+
const backtestTime = v["backtest-time"] !== void 0 ? Number(v["backtest-time"]) : void 0;
|
|
13276
|
+
return cmdMarketIndicator(run, rest[0], rest[1], {
|
|
13277
|
+
bar: v.bar,
|
|
13278
|
+
params: v.params,
|
|
13279
|
+
list: v.list,
|
|
13280
|
+
limit,
|
|
13281
|
+
backtestTime,
|
|
13282
|
+
json
|
|
13283
|
+
});
|
|
12774
13284
|
}
|
|
12775
13285
|
function handleMarketDataCommand(run, action, rest, v, json) {
|
|
12776
13286
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
12777
13287
|
if (action === "orderbook")
|
|
12778
|
-
return cmdMarketOrderbook(run, rest[0], v.sz !== void 0 ? Number(v.sz) : void 0, json);
|
|
13288
|
+
return cmdMarketOrderbook(run, rest[0], v.sz !== void 0 ? Number(v.sz) : void 0, json, v.demo);
|
|
12779
13289
|
if (action === "candles")
|
|
12780
|
-
return cmdMarketCandles(run, rest[0], { bar: v.bar, limit, after: v.after, before: v.before, json });
|
|
13290
|
+
return cmdMarketCandles(run, rest[0], { bar: v.bar, limit, after: v.after, before: v.before, json, demo: v.demo });
|
|
12781
13291
|
if (action === "funding-rate")
|
|
12782
|
-
return cmdMarketFundingRate(run, rest[0], { history: v.history ?? false, limit, json });
|
|
13292
|
+
return cmdMarketFundingRate(run, rest[0], { history: v.history ?? false, limit, json, demo: v.demo });
|
|
12783
13293
|
if (action === "trades")
|
|
12784
|
-
return cmdMarketTrades(run, rest[0], { limit, json });
|
|
13294
|
+
return cmdMarketTrades(run, rest[0], { limit, json, demo: v.demo });
|
|
12785
13295
|
if (action === "index-candles")
|
|
12786
|
-
return cmdMarketIndexCandles(run, rest[0], { bar: v.bar, limit, history: v.history ?? false, json });
|
|
13296
|
+
return cmdMarketIndexCandles(run, rest[0], { bar: v.bar, limit, history: v.history ?? false, json, demo: v.demo });
|
|
12787
13297
|
}
|
|
12788
13298
|
function handleMarketCommand(run, action, rest, v, json) {
|
|
12789
13299
|
return handleMarketPublicCommand(run, action, rest, v, json) ?? handleMarketDataCommand(run, action, rest, v, json);
|
|
@@ -13116,6 +13626,7 @@ function handleOptionCommand(run, action, rest, v, json) {
|
|
|
13116
13626
|
side: v.side,
|
|
13117
13627
|
ordType: v.ordType,
|
|
13118
13628
|
sz: v.sz,
|
|
13629
|
+
tgtCcy: v.tgtCcy,
|
|
13119
13630
|
px: v.px,
|
|
13120
13631
|
reduceOnly: v.reduceOnly,
|
|
13121
13632
|
clOrdId: v.clOrdId,
|
|
@@ -13406,6 +13917,9 @@ function handleEarnSavingsCommand(run, action, rest, v, json) {
|
|
|
13406
13917
|
if (action === "set-rate") return cmdEarnSetLendingRate(run, { ccy: v.ccy, rate: v.rate, json });
|
|
13407
13918
|
if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
|
|
13408
13919
|
if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
|
|
13920
|
+
if (action === "fixed-orders") return cmdEarnFixedOrderList(run, { ccy: v.ccy, state: v.state, json });
|
|
13921
|
+
if (action === "fixed-purchase") return cmdEarnFixedPurchase(run, { ccy: v.ccy, amt: v.amt, term: v.term, confirm: v.confirm ?? false, json });
|
|
13922
|
+
if (action === "fixed-redeem") return cmdEarnFixedRedeem(run, { reqId: v.reqId, json });
|
|
13409
13923
|
errorLine(`Unknown earn savings command: ${action}`);
|
|
13410
13924
|
process.exitCode = 1;
|
|
13411
13925
|
}
|
|
@@ -13488,8 +14002,9 @@ function handleSkillAdd(rest, config, json) {
|
|
|
13488
14002
|
if (n) return cmdSkillAdd(n, config, json);
|
|
13489
14003
|
}
|
|
13490
14004
|
function handleSkillDownload(rest, v, config, json) {
|
|
13491
|
-
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>]");
|
|
13492
|
-
|
|
14005
|
+
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>] [--format zip|skill]");
|
|
14006
|
+
const format = v.format === "skill" ? "skill" : "zip";
|
|
14007
|
+
if (n) return cmdSkillDownload(n, v.dir ?? process.cwd(), config, json, format);
|
|
13493
14008
|
}
|
|
13494
14009
|
function handleSkillRemove(rest, json) {
|
|
13495
14010
|
const n = requireSkillName(rest, "Usage: okx skill remove <name>");
|
|
@@ -13524,6 +14039,35 @@ function printHelpForLevel(positionals) {
|
|
|
13524
14039
|
else if (!subgroup) printHelp(module);
|
|
13525
14040
|
else printHelp(module, subgroup);
|
|
13526
14041
|
}
|
|
14042
|
+
function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
|
|
14043
|
+
const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
|
|
14044
|
+
return async (toolName, args) => {
|
|
14045
|
+
const startTime = Date.now();
|
|
14046
|
+
try {
|
|
14047
|
+
const result = await baseRunner(toolName, args);
|
|
14048
|
+
if (writeToolNames.has(toolName)) {
|
|
14049
|
+
markFailedIfSCodeError(result.data);
|
|
14050
|
+
}
|
|
14051
|
+
const elapsed = Date.now() - startTime;
|
|
14052
|
+
logger.log("info", toolName, args, { status: "ok" }, elapsed);
|
|
14053
|
+
if (verbose) {
|
|
14054
|
+
logger.log("debug", toolName, args, result, elapsed);
|
|
14055
|
+
}
|
|
14056
|
+
return result;
|
|
14057
|
+
} catch (error) {
|
|
14058
|
+
logger.log("error", toolName, args, error, Date.now() - startTime);
|
|
14059
|
+
throw error;
|
|
14060
|
+
}
|
|
14061
|
+
};
|
|
14062
|
+
}
|
|
14063
|
+
async function runDiagnose(v) {
|
|
14064
|
+
let config;
|
|
14065
|
+
try {
|
|
14066
|
+
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
14067
|
+
} catch {
|
|
14068
|
+
}
|
|
14069
|
+
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
14070
|
+
}
|
|
13527
14071
|
async function main() {
|
|
13528
14072
|
setOutput({
|
|
13529
14073
|
out: (m) => process.stdout.write(m),
|
|
@@ -13545,25 +14089,14 @@ async function main() {
|
|
|
13545
14089
|
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
13546
14090
|
if (module === "setup") return handleSetupCommand(v);
|
|
13547
14091
|
if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
|
|
13548
|
-
if (module === "diagnose")
|
|
13549
|
-
let config2;
|
|
13550
|
-
try {
|
|
13551
|
-
config2 = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13552
|
-
} catch {
|
|
13553
|
-
}
|
|
13554
|
-
return cmdDiagnose(config2, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
13555
|
-
}
|
|
14092
|
+
if (module === "diagnose") return runDiagnose(v);
|
|
13556
14093
|
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
14094
|
+
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
14095
|
+
setJsonEnvEnabled(v.env ?? false);
|
|
13557
14096
|
const client = new OkxRestClient(config);
|
|
13558
14097
|
const baseRunner = createToolRunner(client, config);
|
|
13559
|
-
const
|
|
13560
|
-
const run =
|
|
13561
|
-
const result = await baseRunner(toolName, args);
|
|
13562
|
-
if (writeToolNames.has(toolName)) {
|
|
13563
|
-
markFailedIfSCodeError(result.data);
|
|
13564
|
-
}
|
|
13565
|
-
return result;
|
|
13566
|
-
};
|
|
14098
|
+
const logger = new TradeLogger(v.verbose ? "debug" : "info");
|
|
14099
|
+
const run = wrapRunnerWithLogger(baseRunner, logger, v.verbose ?? false);
|
|
13567
14100
|
const moduleHandlers = {
|
|
13568
14101
|
market: () => handleMarketCommand(run, action, rest, v, json),
|
|
13569
14102
|
account: () => handleAccountCommand(run, action, rest, v, json),
|
|
@@ -13608,7 +14141,8 @@ export {
|
|
|
13608
14141
|
handleSpotCommand,
|
|
13609
14142
|
handleSwapAlgoCommand,
|
|
13610
14143
|
handleSwapCommand,
|
|
13611
|
-
printHelp
|
|
14144
|
+
printHelp,
|
|
14145
|
+
wrapRunnerWithLogger
|
|
13612
14146
|
};
|
|
13613
14147
|
/*! Bundled license information:
|
|
13614
14148
|
|