@okx_ai/okx-trade-cli 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1423 -218
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
4
|
+
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
|
|
6
6
|
// ../core/dist/index.js
|
|
7
|
+
import { ProxyAgent } from "undici";
|
|
7
8
|
import { createHmac } from "crypto";
|
|
8
9
|
import fs from "fs";
|
|
9
10
|
import path from "path";
|
|
@@ -933,8 +934,10 @@ function sleep(ms) {
|
|
|
933
934
|
var RateLimiter = class {
|
|
934
935
|
buckets = /* @__PURE__ */ new Map();
|
|
935
936
|
maxWaitMs;
|
|
936
|
-
|
|
937
|
+
verbose;
|
|
938
|
+
constructor(maxWaitMs = 3e4, verbose = false) {
|
|
937
939
|
this.maxWaitMs = maxWaitMs;
|
|
940
|
+
this.verbose = verbose;
|
|
938
941
|
}
|
|
939
942
|
async consume(config, amount = 1) {
|
|
940
943
|
const bucket = this.getBucket(config);
|
|
@@ -952,6 +955,10 @@ var RateLimiter = class {
|
|
|
952
955
|
"Reduce tool call frequency or retry later."
|
|
953
956
|
);
|
|
954
957
|
}
|
|
958
|
+
if (this.verbose) {
|
|
959
|
+
process.stderr.write(`[verbose] \u23F3 rate-limit: waiting ${waitMs}ms for "${config.key}"
|
|
960
|
+
`);
|
|
961
|
+
}
|
|
955
962
|
await sleep(waitMs);
|
|
956
963
|
this.refill(bucket);
|
|
957
964
|
if (bucket.tokens < amount) {
|
|
@@ -1046,11 +1053,38 @@ function buildQueryString(query) {
|
|
|
1046
1053
|
}
|
|
1047
1054
|
return params.toString();
|
|
1048
1055
|
}
|
|
1056
|
+
function maskKey(key) {
|
|
1057
|
+
if (key.length <= 8) return "***";
|
|
1058
|
+
return `${key.slice(0, 3)}***${key.slice(-3)}`;
|
|
1059
|
+
}
|
|
1060
|
+
function vlog(message) {
|
|
1061
|
+
process.stderr.write(`[verbose] ${message}
|
|
1062
|
+
`);
|
|
1063
|
+
}
|
|
1049
1064
|
var OkxRestClient = class {
|
|
1050
1065
|
config;
|
|
1051
|
-
rateLimiter
|
|
1066
|
+
rateLimiter;
|
|
1067
|
+
dispatcher;
|
|
1052
1068
|
constructor(config) {
|
|
1053
1069
|
this.config = config;
|
|
1070
|
+
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
1071
|
+
if (config.proxyUrl) {
|
|
1072
|
+
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
logRequest(method, url, auth) {
|
|
1076
|
+
if (!this.config.verbose) return;
|
|
1077
|
+
vlog(`\u2192 ${method} ${url}`);
|
|
1078
|
+
const authInfo = auth === "private" && this.config.apiKey ? `auth=\u2713(${maskKey(this.config.apiKey)})` : `auth=${auth}`;
|
|
1079
|
+
vlog(` ${authInfo} demo=${this.config.demo} timeout=${this.config.timeoutMs}ms`);
|
|
1080
|
+
}
|
|
1081
|
+
logResponse(status, rawLen, elapsed, traceId, code, msg) {
|
|
1082
|
+
if (!this.config.verbose) return;
|
|
1083
|
+
if (code && code !== "0" && code !== "1") {
|
|
1084
|
+
vlog(`\u2717 ${status} | code=${code} | msg=${msg ?? "-"} | ${rawLen}B | ${elapsed}ms | trace=${traceId ?? "-"}`);
|
|
1085
|
+
} else {
|
|
1086
|
+
vlog(`\u2190 ${status} | code=${code ?? "0"} | ${rawLen}B | ${elapsed}ms | trace=${traceId ?? "-"}`);
|
|
1087
|
+
}
|
|
1054
1088
|
}
|
|
1055
1089
|
async publicGet(path4, query, rateLimit) {
|
|
1056
1090
|
return this.request({
|
|
@@ -1079,126 +1113,150 @@ var OkxRestClient = class {
|
|
|
1079
1113
|
rateLimit
|
|
1080
1114
|
});
|
|
1081
1115
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
if (config.rateLimit) {
|
|
1089
|
-
await this.rateLimiter.consume(config.rateLimit);
|
|
1090
|
-
}
|
|
1091
|
-
const headers = new Headers({
|
|
1092
|
-
"Content-Type": "application/json",
|
|
1093
|
-
Accept: "application/json"
|
|
1094
|
-
});
|
|
1095
|
-
if (this.config.userAgent) {
|
|
1096
|
-
headers.set("User-Agent", this.config.userAgent);
|
|
1097
|
-
}
|
|
1098
|
-
if (config.auth === "private") {
|
|
1099
|
-
if (!this.config.hasAuth) {
|
|
1100
|
-
throw new ConfigError(
|
|
1101
|
-
"Private endpoint requires API credentials.",
|
|
1102
|
-
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
1106
|
-
throw new ConfigError(
|
|
1107
|
-
"Invalid private API credentials state.",
|
|
1108
|
-
"Ensure all OKX credentials are set."
|
|
1109
|
-
);
|
|
1110
|
-
}
|
|
1111
|
-
const payload = `${timestamp}${config.method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
1112
|
-
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
1113
|
-
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
1114
|
-
headers.set("OK-ACCESS-SIGN", signature);
|
|
1115
|
-
headers.set("OK-ACCESS-PASSPHRASE", this.config.passphrase);
|
|
1116
|
-
headers.set("OK-ACCESS-TIMESTAMP", timestamp);
|
|
1116
|
+
setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
|
|
1117
|
+
if (!this.config.hasAuth) {
|
|
1118
|
+
throw new ConfigError(
|
|
1119
|
+
"Private endpoint requires API credentials.",
|
|
1120
|
+
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
1121
|
+
);
|
|
1117
1122
|
}
|
|
1118
|
-
if (this.config.
|
|
1119
|
-
|
|
1123
|
+
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
1124
|
+
throw new ConfigError(
|
|
1125
|
+
"Invalid private API credentials state.",
|
|
1126
|
+
"Ensure all OKX credentials are set."
|
|
1127
|
+
);
|
|
1120
1128
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1129
|
+
const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
1130
|
+
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
1131
|
+
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
1132
|
+
headers.set("OK-ACCESS-SIGN", signature);
|
|
1133
|
+
headers.set("OK-ACCESS-PASSPHRASE", this.config.passphrase);
|
|
1134
|
+
headers.set("OK-ACCESS-TIMESTAMP", timestamp);
|
|
1135
|
+
}
|
|
1136
|
+
throwOkxError(code, msg, reqConfig, traceId) {
|
|
1137
|
+
const message = msg || "OKX API request failed.";
|
|
1138
|
+
const endpoint = `${reqConfig.method} ${reqConfig.path}`;
|
|
1139
|
+
if (code === "50111" || code === "50112" || code === "50113") {
|
|
1140
|
+
throw new AuthenticationError(
|
|
1141
|
+
message,
|
|
1142
|
+
"Check API key, secret, passphrase and permissions.",
|
|
1143
|
+
endpoint,
|
|
1144
|
+
traceId
|
|
1134
1145
|
);
|
|
1135
1146
|
}
|
|
1136
|
-
const
|
|
1137
|
-
const
|
|
1147
|
+
const behavior = OKX_CODE_BEHAVIORS[code];
|
|
1148
|
+
const suggestion = behavior?.suggestion?.replace("{site}", this.config.site);
|
|
1149
|
+
if (code === "50011" || code === "50061") {
|
|
1150
|
+
throw new RateLimitError(message, suggestion, endpoint, traceId);
|
|
1151
|
+
}
|
|
1152
|
+
throw new OkxApiError(message, {
|
|
1153
|
+
code,
|
|
1154
|
+
endpoint,
|
|
1155
|
+
suggestion,
|
|
1156
|
+
traceId
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath) {
|
|
1138
1160
|
let parsed;
|
|
1139
1161
|
try {
|
|
1140
1162
|
parsed = rawText ? JSON.parse(rawText) : {};
|
|
1141
1163
|
} catch (error) {
|
|
1164
|
+
this.logResponse(response.status, rawText.length, elapsed, traceId, "non-JSON");
|
|
1142
1165
|
if (!response.ok) {
|
|
1143
1166
|
const messagePreview = rawText.slice(0, 160).replace(/\s+/g, " ").trim();
|
|
1144
1167
|
throw new OkxApiError(
|
|
1145
1168
|
`HTTP ${response.status} from OKX: ${messagePreview || "Non-JSON response body"}`,
|
|
1146
1169
|
{
|
|
1147
1170
|
code: String(response.status),
|
|
1148
|
-
endpoint: `${
|
|
1171
|
+
endpoint: `${reqConfig.method} ${reqConfig.path}`,
|
|
1149
1172
|
suggestion: "Verify endpoint path and request parameters.",
|
|
1150
1173
|
traceId
|
|
1151
1174
|
}
|
|
1152
1175
|
);
|
|
1153
1176
|
}
|
|
1154
1177
|
throw new NetworkError(
|
|
1155
|
-
`OKX returned non-JSON response for ${
|
|
1156
|
-
`${
|
|
1178
|
+
`OKX returned non-JSON response for ${reqConfig.method} ${requestPath}.`,
|
|
1179
|
+
`${reqConfig.method} ${requestPath}`,
|
|
1157
1180
|
error
|
|
1158
1181
|
);
|
|
1159
1182
|
}
|
|
1160
1183
|
if (!response.ok) {
|
|
1184
|
+
this.logResponse(response.status, rawText.length, elapsed, traceId, parsed.code ?? "-", parsed.msg);
|
|
1161
1185
|
throw new OkxApiError(
|
|
1162
1186
|
`HTTP ${response.status} from OKX: ${parsed.msg ?? "Unknown error"}`,
|
|
1163
1187
|
{
|
|
1164
1188
|
code: String(response.status),
|
|
1165
|
-
endpoint: `${
|
|
1189
|
+
endpoint: `${reqConfig.method} ${reqConfig.path}`,
|
|
1166
1190
|
suggestion: "Retry later or verify endpoint parameters.",
|
|
1167
1191
|
traceId
|
|
1168
1192
|
}
|
|
1169
1193
|
);
|
|
1170
1194
|
}
|
|
1171
1195
|
const responseCode = parsed.code;
|
|
1196
|
+
this.logResponse(response.status, rawText.length, elapsed, traceId, responseCode, parsed.msg);
|
|
1172
1197
|
if (responseCode && responseCode !== "0" && responseCode !== "1") {
|
|
1173
|
-
|
|
1174
|
-
const endpoint = `${config.method} ${config.path}`;
|
|
1175
|
-
if (responseCode === "50111" || responseCode === "50112" || responseCode === "50113") {
|
|
1176
|
-
throw new AuthenticationError(
|
|
1177
|
-
message,
|
|
1178
|
-
"Check API key, secret, passphrase and permissions.",
|
|
1179
|
-
endpoint,
|
|
1180
|
-
traceId
|
|
1181
|
-
);
|
|
1182
|
-
}
|
|
1183
|
-
const behavior = OKX_CODE_BEHAVIORS[responseCode];
|
|
1184
|
-
const suggestion = behavior?.suggestion?.replace("{site}", this.config.site);
|
|
1185
|
-
if (responseCode === "50011" || responseCode === "50061") {
|
|
1186
|
-
throw new RateLimitError(message, suggestion, endpoint, traceId);
|
|
1187
|
-
}
|
|
1188
|
-
throw new OkxApiError(message, {
|
|
1189
|
-
code: responseCode,
|
|
1190
|
-
endpoint,
|
|
1191
|
-
suggestion,
|
|
1192
|
-
traceId
|
|
1193
|
-
});
|
|
1198
|
+
this.throwOkxError(responseCode, parsed.msg, reqConfig, traceId);
|
|
1194
1199
|
}
|
|
1195
1200
|
return {
|
|
1196
|
-
endpoint: `${
|
|
1201
|
+
endpoint: `${reqConfig.method} ${reqConfig.path}`,
|
|
1197
1202
|
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1198
1203
|
data: parsed.data ?? null,
|
|
1199
1204
|
raw: parsed
|
|
1200
1205
|
};
|
|
1201
1206
|
}
|
|
1207
|
+
async request(reqConfig) {
|
|
1208
|
+
const queryString = buildQueryString(reqConfig.query);
|
|
1209
|
+
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1210
|
+
const url = `${this.config.baseUrl}${requestPath}`;
|
|
1211
|
+
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1212
|
+
const timestamp = getNow();
|
|
1213
|
+
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
1214
|
+
if (reqConfig.rateLimit) {
|
|
1215
|
+
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1216
|
+
}
|
|
1217
|
+
const headers = new Headers({
|
|
1218
|
+
"Content-Type": "application/json",
|
|
1219
|
+
Accept: "application/json"
|
|
1220
|
+
});
|
|
1221
|
+
if (this.config.userAgent) {
|
|
1222
|
+
headers.set("User-Agent", this.config.userAgent);
|
|
1223
|
+
}
|
|
1224
|
+
if (reqConfig.auth === "private") {
|
|
1225
|
+
this.setAuthHeaders(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1226
|
+
}
|
|
1227
|
+
if (this.config.demo) {
|
|
1228
|
+
headers.set("x-simulated-trading", "1");
|
|
1229
|
+
}
|
|
1230
|
+
const t0 = Date.now();
|
|
1231
|
+
let response;
|
|
1232
|
+
try {
|
|
1233
|
+
const fetchOptions = {
|
|
1234
|
+
method: reqConfig.method,
|
|
1235
|
+
headers,
|
|
1236
|
+
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1237
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1238
|
+
};
|
|
1239
|
+
if (this.dispatcher) {
|
|
1240
|
+
fetchOptions.dispatcher = this.dispatcher;
|
|
1241
|
+
}
|
|
1242
|
+
response = await fetch(url, fetchOptions);
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
if (this.config.verbose) {
|
|
1245
|
+
const elapsed2 = Date.now() - t0;
|
|
1246
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
1247
|
+
vlog(`\u2717 NetworkError after ${elapsed2}ms: ${cause}`);
|
|
1248
|
+
}
|
|
1249
|
+
throw new NetworkError(
|
|
1250
|
+
`Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
|
|
1251
|
+
`${reqConfig.method} ${requestPath}`,
|
|
1252
|
+
error
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
const rawText = await response.text();
|
|
1256
|
+
const elapsed = Date.now() - t0;
|
|
1257
|
+
const traceId = extractTraceId(response.headers);
|
|
1258
|
+
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1259
|
+
}
|
|
1202
1260
|
};
|
|
1203
1261
|
var DEFAULT_SOURCE_TAG = "MCP";
|
|
1204
1262
|
var OKX_SITES = {
|
|
@@ -1224,6 +1282,10 @@ var BOT_SUB_MODULE_IDS = [
|
|
|
1224
1282
|
"bot.dca"
|
|
1225
1283
|
];
|
|
1226
1284
|
var BOT_DEFAULT_SUB_MODULES = ["bot.grid"];
|
|
1285
|
+
var EARN_SUB_MODULE_IDS = [
|
|
1286
|
+
"earn.savings",
|
|
1287
|
+
"earn.onchain"
|
|
1288
|
+
];
|
|
1227
1289
|
var MODULES = [
|
|
1228
1290
|
"market",
|
|
1229
1291
|
"spot",
|
|
@@ -1231,6 +1293,7 @@ var MODULES = [
|
|
|
1231
1293
|
"futures",
|
|
1232
1294
|
"option",
|
|
1233
1295
|
"account",
|
|
1296
|
+
...EARN_SUB_MODULE_IDS,
|
|
1234
1297
|
...BOT_SUB_MODULE_IDS
|
|
1235
1298
|
];
|
|
1236
1299
|
var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES];
|
|
@@ -1296,6 +1359,13 @@ function compactObject(object) {
|
|
|
1296
1359
|
}
|
|
1297
1360
|
return next;
|
|
1298
1361
|
}
|
|
1362
|
+
function normalizeResponse(response) {
|
|
1363
|
+
return {
|
|
1364
|
+
endpoint: response.endpoint,
|
|
1365
|
+
requestTime: response.requestTime,
|
|
1366
|
+
data: response.data
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1299
1369
|
var OKX_CANDLE_BARS = [
|
|
1300
1370
|
"1m",
|
|
1301
1371
|
"3m",
|
|
@@ -1335,6 +1405,14 @@ function privateRateLimit(key, rps = 10) {
|
|
|
1335
1405
|
refillPerSecond: rps
|
|
1336
1406
|
};
|
|
1337
1407
|
}
|
|
1408
|
+
function assertNotDemo(config, endpoint) {
|
|
1409
|
+
if (config.demo) {
|
|
1410
|
+
throw new ConfigError(
|
|
1411
|
+
`"${endpoint}" is not supported in simulated trading mode.`,
|
|
1412
|
+
"Disable demo mode (remove OKX_DEMO=1 or --demo flag) to use this endpoint."
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1338
1416
|
function normalize(response) {
|
|
1339
1417
|
return {
|
|
1340
1418
|
endpoint: response.endpoint,
|
|
@@ -1913,7 +1991,7 @@ function registerAlgoTradeTools() {
|
|
|
1913
1991
|
},
|
|
1914
1992
|
sz: {
|
|
1915
1993
|
type: "string",
|
|
1916
|
-
description: "
|
|
1994
|
+
description: "Number of contracts to close (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
1917
1995
|
},
|
|
1918
1996
|
tpTriggerPx: {
|
|
1919
1997
|
type: "string",
|
|
@@ -2008,7 +2086,7 @@ function registerAlgoTradeTools() {
|
|
|
2008
2086
|
},
|
|
2009
2087
|
sz: {
|
|
2010
2088
|
type: "string",
|
|
2011
|
-
description: "
|
|
2089
|
+
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
2012
2090
|
},
|
|
2013
2091
|
callbackRatio: {
|
|
2014
2092
|
type: "string",
|
|
@@ -2821,6 +2899,246 @@ function registerBotTools() {
|
|
|
2821
2899
|
...registerDcaTools()
|
|
2822
2900
|
];
|
|
2823
2901
|
}
|
|
2902
|
+
function registerEarnTools() {
|
|
2903
|
+
return [
|
|
2904
|
+
{
|
|
2905
|
+
name: "earn_get_savings_balance",
|
|
2906
|
+
module: "earn.savings",
|
|
2907
|
+
description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. Private endpoint. Rate limit: 6 req/s.",
|
|
2908
|
+
isWrite: false,
|
|
2909
|
+
inputSchema: {
|
|
2910
|
+
type: "object",
|
|
2911
|
+
properties: {
|
|
2912
|
+
ccy: {
|
|
2913
|
+
type: "string",
|
|
2914
|
+
description: "e.g. USDT or BTC. Omit for all."
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
},
|
|
2918
|
+
handler: async (rawArgs, context) => {
|
|
2919
|
+
const args = asRecord(rawArgs);
|
|
2920
|
+
const response = await context.client.privateGet(
|
|
2921
|
+
"/api/v5/finance/savings/balance",
|
|
2922
|
+
compactObject({ ccy: readString(args, "ccy") }),
|
|
2923
|
+
privateRateLimit("earn_get_savings_balance", 6)
|
|
2924
|
+
);
|
|
2925
|
+
return normalizeResponse(response);
|
|
2926
|
+
}
|
|
2927
|
+
},
|
|
2928
|
+
{
|
|
2929
|
+
name: "earn_savings_purchase",
|
|
2930
|
+
module: "earn.savings",
|
|
2931
|
+
description: "Purchase Simple Earn (savings/flexible earn). [CAUTION] Moves real funds into earn product. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 6 req/s.",
|
|
2932
|
+
isWrite: true,
|
|
2933
|
+
inputSchema: {
|
|
2934
|
+
type: "object",
|
|
2935
|
+
properties: {
|
|
2936
|
+
ccy: {
|
|
2937
|
+
type: "string",
|
|
2938
|
+
description: "Currency to purchase, e.g. USDT"
|
|
2939
|
+
},
|
|
2940
|
+
amt: {
|
|
2941
|
+
type: "string",
|
|
2942
|
+
description: "Purchase amount"
|
|
2943
|
+
},
|
|
2944
|
+
rate: {
|
|
2945
|
+
type: "string",
|
|
2946
|
+
description: "Lending rate. Annual rate in decimal, e.g. 0.01 = 1%. Defaults to 0.01 (1%, minimum rate, easiest to match)."
|
|
2947
|
+
}
|
|
2948
|
+
},
|
|
2949
|
+
required: ["ccy", "amt"]
|
|
2950
|
+
},
|
|
2951
|
+
handler: async (rawArgs, context) => {
|
|
2952
|
+
assertNotDemo(context.config, "earn_savings_purchase");
|
|
2953
|
+
const args = asRecord(rawArgs);
|
|
2954
|
+
const response = await context.client.privatePost(
|
|
2955
|
+
"/api/v5/finance/savings/purchase-redempt",
|
|
2956
|
+
compactObject({
|
|
2957
|
+
ccy: requireString(args, "ccy"),
|
|
2958
|
+
amt: requireString(args, "amt"),
|
|
2959
|
+
side: "purchase",
|
|
2960
|
+
rate: readString(args, "rate") ?? "0.01"
|
|
2961
|
+
}),
|
|
2962
|
+
privateRateLimit("earn_savings_purchase", 6)
|
|
2963
|
+
);
|
|
2964
|
+
return normalizeResponse(response);
|
|
2965
|
+
}
|
|
2966
|
+
},
|
|
2967
|
+
{
|
|
2968
|
+
name: "earn_savings_redeem",
|
|
2969
|
+
module: "earn.savings",
|
|
2970
|
+
description: "Redeem Simple Earn (savings/flexible earn). [CAUTION] Withdraws funds from earn product. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 6 req/s.",
|
|
2971
|
+
isWrite: true,
|
|
2972
|
+
inputSchema: {
|
|
2973
|
+
type: "object",
|
|
2974
|
+
properties: {
|
|
2975
|
+
ccy: {
|
|
2976
|
+
type: "string",
|
|
2977
|
+
description: "Currency to redeem, e.g. USDT"
|
|
2978
|
+
},
|
|
2979
|
+
amt: {
|
|
2980
|
+
type: "string",
|
|
2981
|
+
description: "Redemption amount"
|
|
2982
|
+
}
|
|
2983
|
+
},
|
|
2984
|
+
required: ["ccy", "amt"]
|
|
2985
|
+
},
|
|
2986
|
+
handler: async (rawArgs, context) => {
|
|
2987
|
+
assertNotDemo(context.config, "earn_savings_redeem");
|
|
2988
|
+
const args = asRecord(rawArgs);
|
|
2989
|
+
const response = await context.client.privatePost(
|
|
2990
|
+
"/api/v5/finance/savings/purchase-redempt",
|
|
2991
|
+
compactObject({
|
|
2992
|
+
ccy: requireString(args, "ccy"),
|
|
2993
|
+
amt: requireString(args, "amt"),
|
|
2994
|
+
side: "redempt"
|
|
2995
|
+
}),
|
|
2996
|
+
privateRateLimit("earn_savings_redeem", 6)
|
|
2997
|
+
);
|
|
2998
|
+
return normalizeResponse(response);
|
|
2999
|
+
}
|
|
3000
|
+
},
|
|
3001
|
+
{
|
|
3002
|
+
name: "earn_set_lending_rate",
|
|
3003
|
+
module: "earn.savings",
|
|
3004
|
+
description: "Set lending rate for Simple Earn. [CAUTION] Changes your lending rate preference. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 6 req/s.",
|
|
3005
|
+
isWrite: true,
|
|
3006
|
+
inputSchema: {
|
|
3007
|
+
type: "object",
|
|
3008
|
+
properties: {
|
|
3009
|
+
ccy: {
|
|
3010
|
+
type: "string",
|
|
3011
|
+
description: "Currency, e.g. USDT"
|
|
3012
|
+
},
|
|
3013
|
+
rate: {
|
|
3014
|
+
type: "string",
|
|
3015
|
+
description: "Lending rate. Annual rate in decimal, e.g. 0.01 = 1%"
|
|
3016
|
+
}
|
|
3017
|
+
},
|
|
3018
|
+
required: ["ccy", "rate"]
|
|
3019
|
+
},
|
|
3020
|
+
handler: async (rawArgs, context) => {
|
|
3021
|
+
assertNotDemo(context.config, "earn_set_lending_rate");
|
|
3022
|
+
const args = asRecord(rawArgs);
|
|
3023
|
+
const response = await context.client.privatePost(
|
|
3024
|
+
"/api/v5/finance/savings/set-lending-rate",
|
|
3025
|
+
{
|
|
3026
|
+
ccy: requireString(args, "ccy"),
|
|
3027
|
+
rate: requireString(args, "rate")
|
|
3028
|
+
},
|
|
3029
|
+
privateRateLimit("earn_set_lending_rate", 6)
|
|
3030
|
+
);
|
|
3031
|
+
return normalizeResponse(response);
|
|
3032
|
+
}
|
|
3033
|
+
},
|
|
3034
|
+
{
|
|
3035
|
+
name: "earn_get_lending_history",
|
|
3036
|
+
module: "earn.savings",
|
|
3037
|
+
description: "Get lending history for Simple Earn. Returns lending records with details like amount, rate, and earnings. Private endpoint. Rate limit: 6 req/s.",
|
|
3038
|
+
isWrite: false,
|
|
3039
|
+
inputSchema: {
|
|
3040
|
+
type: "object",
|
|
3041
|
+
properties: {
|
|
3042
|
+
ccy: {
|
|
3043
|
+
type: "string",
|
|
3044
|
+
description: "e.g. USDT. Omit for all."
|
|
3045
|
+
},
|
|
3046
|
+
after: {
|
|
3047
|
+
type: "string",
|
|
3048
|
+
description: "Pagination: before this record ID"
|
|
3049
|
+
},
|
|
3050
|
+
before: {
|
|
3051
|
+
type: "string",
|
|
3052
|
+
description: "Pagination: after this record ID"
|
|
3053
|
+
},
|
|
3054
|
+
limit: {
|
|
3055
|
+
type: "number",
|
|
3056
|
+
description: "Max results (default 100)"
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
},
|
|
3060
|
+
handler: async (rawArgs, context) => {
|
|
3061
|
+
const args = asRecord(rawArgs);
|
|
3062
|
+
const response = await context.client.privateGet(
|
|
3063
|
+
"/api/v5/finance/savings/lending-history",
|
|
3064
|
+
compactObject({
|
|
3065
|
+
ccy: readString(args, "ccy"),
|
|
3066
|
+
after: readString(args, "after"),
|
|
3067
|
+
before: readString(args, "before"),
|
|
3068
|
+
limit: readNumber(args, "limit")
|
|
3069
|
+
}),
|
|
3070
|
+
privateRateLimit("earn_get_lending_history", 6)
|
|
3071
|
+
);
|
|
3072
|
+
return normalizeResponse(response);
|
|
3073
|
+
}
|
|
3074
|
+
},
|
|
3075
|
+
{
|
|
3076
|
+
name: "earn_get_lending_rate_summary",
|
|
3077
|
+
module: "earn.savings",
|
|
3078
|
+
description: "Get market lending rate summary for Simple Earn. Public endpoint (no API key required). Returns current lending rates, estimated APY, and available amounts. Rate limit: 6 req/s.",
|
|
3079
|
+
isWrite: false,
|
|
3080
|
+
inputSchema: {
|
|
3081
|
+
type: "object",
|
|
3082
|
+
properties: {
|
|
3083
|
+
ccy: {
|
|
3084
|
+
type: "string",
|
|
3085
|
+
description: "e.g. USDT. Omit for all."
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
},
|
|
3089
|
+
handler: async (rawArgs, context) => {
|
|
3090
|
+
const args = asRecord(rawArgs);
|
|
3091
|
+
const response = await context.client.publicGet(
|
|
3092
|
+
"/api/v5/finance/savings/lending-rate-summary",
|
|
3093
|
+
compactObject({ ccy: readString(args, "ccy") }),
|
|
3094
|
+
publicRateLimit("earn_get_lending_rate_summary", 6)
|
|
3095
|
+
);
|
|
3096
|
+
return normalizeResponse(response);
|
|
3097
|
+
}
|
|
3098
|
+
},
|
|
3099
|
+
{
|
|
3100
|
+
name: "earn_get_lending_rate_history",
|
|
3101
|
+
module: "earn.savings",
|
|
3102
|
+
description: "Get historical lending rates for Simple Earn. Public endpoint (no API key required). Returns past lending rate data for trend analysis. Rate limit: 6 req/s.",
|
|
3103
|
+
isWrite: false,
|
|
3104
|
+
inputSchema: {
|
|
3105
|
+
type: "object",
|
|
3106
|
+
properties: {
|
|
3107
|
+
ccy: {
|
|
3108
|
+
type: "string",
|
|
3109
|
+
description: "e.g. USDT. Omit for all."
|
|
3110
|
+
},
|
|
3111
|
+
after: {
|
|
3112
|
+
type: "string",
|
|
3113
|
+
description: "Pagination: before this timestamp (ms)"
|
|
3114
|
+
},
|
|
3115
|
+
before: {
|
|
3116
|
+
type: "string",
|
|
3117
|
+
description: "Pagination: after this timestamp (ms)"
|
|
3118
|
+
},
|
|
3119
|
+
limit: {
|
|
3120
|
+
type: "number",
|
|
3121
|
+
description: "Max results (default 100)"
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
},
|
|
3125
|
+
handler: async (rawArgs, context) => {
|
|
3126
|
+
const args = asRecord(rawArgs);
|
|
3127
|
+
const response = await context.client.publicGet(
|
|
3128
|
+
"/api/v5/finance/savings/lending-rate-history",
|
|
3129
|
+
compactObject({
|
|
3130
|
+
ccy: readString(args, "ccy"),
|
|
3131
|
+
after: readString(args, "after"),
|
|
3132
|
+
before: readString(args, "before"),
|
|
3133
|
+
limit: readNumber(args, "limit")
|
|
3134
|
+
}),
|
|
3135
|
+
publicRateLimit("earn_get_lending_rate_history", 6)
|
|
3136
|
+
);
|
|
3137
|
+
return normalizeResponse(response);
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
];
|
|
3141
|
+
}
|
|
2824
3142
|
var FUTURES_INST_TYPES = ["FUTURES", "SWAP"];
|
|
2825
3143
|
function normalize5(response) {
|
|
2826
3144
|
return {
|
|
@@ -2865,7 +3183,7 @@ function registerFuturesTools() {
|
|
|
2865
3183
|
},
|
|
2866
3184
|
sz: {
|
|
2867
3185
|
type: "string",
|
|
2868
|
-
description: "
|
|
3186
|
+
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
2869
3187
|
},
|
|
2870
3188
|
px: {
|
|
2871
3189
|
type: "string",
|
|
@@ -3089,99 +3407,371 @@ function registerFuturesTools() {
|
|
|
3089
3407
|
enum: [...FUTURES_INST_TYPES],
|
|
3090
3408
|
description: "FUTURES (default) or SWAP"
|
|
3091
3409
|
},
|
|
3092
|
-
instId: {
|
|
3410
|
+
instId: {
|
|
3411
|
+
type: "string",
|
|
3412
|
+
description: "e.g. BTC-USDT-240329"
|
|
3413
|
+
},
|
|
3414
|
+
posId: {
|
|
3415
|
+
type: "string"
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
},
|
|
3419
|
+
handler: async (rawArgs, context) => {
|
|
3420
|
+
const args = asRecord(rawArgs);
|
|
3421
|
+
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3422
|
+
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3423
|
+
const response = await context.client.privateGet(
|
|
3424
|
+
"/api/v5/account/positions",
|
|
3425
|
+
compactObject({
|
|
3426
|
+
instType,
|
|
3427
|
+
instId: readString(args, "instId"),
|
|
3428
|
+
posId: readString(args, "posId")
|
|
3429
|
+
}),
|
|
3430
|
+
privateRateLimit("futures_get_positions", 10)
|
|
3431
|
+
);
|
|
3432
|
+
return normalize5(response);
|
|
3433
|
+
}
|
|
3434
|
+
},
|
|
3435
|
+
{
|
|
3436
|
+
name: "futures_get_fills",
|
|
3437
|
+
module: "futures",
|
|
3438
|
+
description: "Get FUTURES fill details. archive=false: last 3 days. archive=true: up to 3 months. Private. Rate limit: 20 req/s.",
|
|
3439
|
+
isWrite: false,
|
|
3440
|
+
inputSchema: {
|
|
3441
|
+
type: "object",
|
|
3442
|
+
properties: {
|
|
3443
|
+
archive: {
|
|
3444
|
+
type: "boolean",
|
|
3445
|
+
description: "true=up to 3 months; false=last 3 days (default)"
|
|
3446
|
+
},
|
|
3447
|
+
instType: {
|
|
3448
|
+
type: "string",
|
|
3449
|
+
enum: [...FUTURES_INST_TYPES],
|
|
3450
|
+
description: "FUTURES (default) or SWAP"
|
|
3451
|
+
},
|
|
3452
|
+
instId: {
|
|
3453
|
+
type: "string",
|
|
3454
|
+
description: "Instrument ID filter"
|
|
3455
|
+
},
|
|
3456
|
+
ordId: {
|
|
3457
|
+
type: "string",
|
|
3458
|
+
description: "Order ID filter"
|
|
3459
|
+
},
|
|
3460
|
+
after: {
|
|
3461
|
+
type: "string",
|
|
3462
|
+
description: "Pagination: before this bill ID"
|
|
3463
|
+
},
|
|
3464
|
+
before: {
|
|
3465
|
+
type: "string",
|
|
3466
|
+
description: "Pagination: after this bill ID"
|
|
3467
|
+
},
|
|
3468
|
+
begin: {
|
|
3469
|
+
type: "string",
|
|
3470
|
+
description: "Start time (ms)"
|
|
3471
|
+
},
|
|
3472
|
+
end: {
|
|
3473
|
+
type: "string",
|
|
3474
|
+
description: "End time (ms)"
|
|
3475
|
+
},
|
|
3476
|
+
limit: {
|
|
3477
|
+
type: "number",
|
|
3478
|
+
description: "Max results (default 100 or 20 for archive)"
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
},
|
|
3482
|
+
handler: async (rawArgs, context) => {
|
|
3483
|
+
const args = asRecord(rawArgs);
|
|
3484
|
+
const archive = readBoolean(args, "archive") ?? false;
|
|
3485
|
+
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3486
|
+
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3487
|
+
const path4 = archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
3488
|
+
const response = await context.client.privateGet(
|
|
3489
|
+
path4,
|
|
3490
|
+
compactObject({
|
|
3491
|
+
instType,
|
|
3492
|
+
instId: readString(args, "instId"),
|
|
3493
|
+
ordId: readString(args, "ordId"),
|
|
3494
|
+
after: readString(args, "after"),
|
|
3495
|
+
before: readString(args, "before"),
|
|
3496
|
+
begin: readString(args, "begin"),
|
|
3497
|
+
end: readString(args, "end"),
|
|
3498
|
+
limit: readNumber(args, "limit") ?? (archive ? 20 : void 0)
|
|
3499
|
+
}),
|
|
3500
|
+
privateRateLimit("futures_get_fills", 20)
|
|
3501
|
+
);
|
|
3502
|
+
return normalize5(response);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
];
|
|
3506
|
+
}
|
|
3507
|
+
function registerOnchainEarnTools() {
|
|
3508
|
+
return [
|
|
3509
|
+
// -------------------------------------------------------------------------
|
|
3510
|
+
// Get Offers
|
|
3511
|
+
// -------------------------------------------------------------------------
|
|
3512
|
+
{
|
|
3513
|
+
name: "onchain_earn_get_offers",
|
|
3514
|
+
module: "earn.onchain",
|
|
3515
|
+
description: "Get available on-chain earn (staking/DeFi) offers. Returns investment products with APY, terms, and limits. Private endpoint. Rate limit: 3 req/s.",
|
|
3516
|
+
isWrite: false,
|
|
3517
|
+
inputSchema: {
|
|
3518
|
+
type: "object",
|
|
3519
|
+
properties: {
|
|
3520
|
+
productId: {
|
|
3521
|
+
type: "string",
|
|
3522
|
+
description: "Specific product ID to query. Omit for all offers."
|
|
3523
|
+
},
|
|
3524
|
+
protocolType: {
|
|
3525
|
+
type: "string",
|
|
3526
|
+
description: "Protocol type filter: staking, defi. Omit for all types."
|
|
3527
|
+
},
|
|
3528
|
+
ccy: {
|
|
3529
|
+
type: "string",
|
|
3530
|
+
description: "Currency filter, e.g. ETH. Omit for all currencies."
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
},
|
|
3534
|
+
handler: async (rawArgs, context) => {
|
|
3535
|
+
const args = asRecord(rawArgs);
|
|
3536
|
+
const response = await context.client.privateGet(
|
|
3537
|
+
"/api/v5/finance/staking-defi/offers",
|
|
3538
|
+
compactObject({
|
|
3539
|
+
productId: readString(args, "productId"),
|
|
3540
|
+
protocolType: readString(args, "protocolType"),
|
|
3541
|
+
ccy: readString(args, "ccy")
|
|
3542
|
+
}),
|
|
3543
|
+
privateRateLimit("onchain_earn_get_offers", 3)
|
|
3544
|
+
);
|
|
3545
|
+
return normalizeResponse(response);
|
|
3546
|
+
}
|
|
3547
|
+
},
|
|
3548
|
+
// -------------------------------------------------------------------------
|
|
3549
|
+
// Purchase
|
|
3550
|
+
// -------------------------------------------------------------------------
|
|
3551
|
+
{
|
|
3552
|
+
name: "onchain_earn_purchase",
|
|
3553
|
+
module: "earn.onchain",
|
|
3554
|
+
description: "Purchase on-chain earn (staking/DeFi) product. [CAUTION] Moves real funds into staking/DeFi product. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3555
|
+
isWrite: true,
|
|
3556
|
+
inputSchema: {
|
|
3557
|
+
type: "object",
|
|
3558
|
+
properties: {
|
|
3559
|
+
productId: {
|
|
3560
|
+
type: "string",
|
|
3561
|
+
description: "Product ID to purchase"
|
|
3562
|
+
},
|
|
3563
|
+
investData: {
|
|
3564
|
+
type: "array",
|
|
3565
|
+
description: "Investment data array: [{ccy, amt}]. Each item specifies currency and amount.",
|
|
3566
|
+
items: {
|
|
3567
|
+
type: "object",
|
|
3568
|
+
properties: {
|
|
3569
|
+
ccy: { type: "string", description: "Currency, e.g. ETH" },
|
|
3570
|
+
amt: { type: "string", description: "Amount to invest" }
|
|
3571
|
+
},
|
|
3572
|
+
required: ["ccy", "amt"]
|
|
3573
|
+
}
|
|
3574
|
+
},
|
|
3575
|
+
term: {
|
|
3576
|
+
type: "string",
|
|
3577
|
+
description: "Investment term in days. Required for fixed-term products."
|
|
3578
|
+
},
|
|
3579
|
+
tag: {
|
|
3580
|
+
type: "string",
|
|
3581
|
+
description: "Order tag for tracking (optional)."
|
|
3582
|
+
}
|
|
3583
|
+
},
|
|
3584
|
+
required: ["productId", "investData"]
|
|
3585
|
+
},
|
|
3586
|
+
handler: async (rawArgs, context) => {
|
|
3587
|
+
assertNotDemo(context.config, "onchain_earn_purchase");
|
|
3588
|
+
const args = asRecord(rawArgs);
|
|
3589
|
+
const response = await context.client.privatePost(
|
|
3590
|
+
"/api/v5/finance/staking-defi/purchase",
|
|
3591
|
+
compactObject({
|
|
3592
|
+
productId: requireString(args, "productId"),
|
|
3593
|
+
investData: args.investData,
|
|
3594
|
+
term: readString(args, "term"),
|
|
3595
|
+
tag: readString(args, "tag")
|
|
3596
|
+
}),
|
|
3597
|
+
privateRateLimit("onchain_earn_purchase", 2)
|
|
3598
|
+
);
|
|
3599
|
+
return normalizeResponse(response);
|
|
3600
|
+
}
|
|
3601
|
+
},
|
|
3602
|
+
// -------------------------------------------------------------------------
|
|
3603
|
+
// Redeem
|
|
3604
|
+
// -------------------------------------------------------------------------
|
|
3605
|
+
{
|
|
3606
|
+
name: "onchain_earn_redeem",
|
|
3607
|
+
module: "earn.onchain",
|
|
3608
|
+
description: "Redeem on-chain earn (staking/DeFi) investment. [CAUTION] Withdraws funds from staking/DeFi product. Some products may have lock periods. Not supported in demo mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3609
|
+
isWrite: true,
|
|
3610
|
+
inputSchema: {
|
|
3611
|
+
type: "object",
|
|
3612
|
+
properties: {
|
|
3613
|
+
ordId: {
|
|
3614
|
+
type: "string",
|
|
3615
|
+
description: "Order ID to redeem"
|
|
3616
|
+
},
|
|
3617
|
+
protocolType: {
|
|
3618
|
+
type: "string",
|
|
3619
|
+
description: "Protocol type: staking, defi"
|
|
3620
|
+
},
|
|
3621
|
+
allowEarlyRedeem: {
|
|
3622
|
+
type: "boolean",
|
|
3623
|
+
description: "Allow early redemption for fixed-term products (may incur penalties). Default false."
|
|
3624
|
+
}
|
|
3625
|
+
},
|
|
3626
|
+
required: ["ordId", "protocolType"]
|
|
3627
|
+
},
|
|
3628
|
+
handler: async (rawArgs, context) => {
|
|
3629
|
+
assertNotDemo(context.config, "onchain_earn_redeem");
|
|
3630
|
+
const args = asRecord(rawArgs);
|
|
3631
|
+
const response = await context.client.privatePost(
|
|
3632
|
+
"/api/v5/finance/staking-defi/redeem",
|
|
3633
|
+
compactObject({
|
|
3634
|
+
ordId: requireString(args, "ordId"),
|
|
3635
|
+
protocolType: requireString(args, "protocolType"),
|
|
3636
|
+
allowEarlyRedeem: readBoolean(args, "allowEarlyRedeem")
|
|
3637
|
+
}),
|
|
3638
|
+
privateRateLimit("onchain_earn_redeem", 2)
|
|
3639
|
+
);
|
|
3640
|
+
return normalizeResponse(response);
|
|
3641
|
+
}
|
|
3642
|
+
},
|
|
3643
|
+
// -------------------------------------------------------------------------
|
|
3644
|
+
// Cancel
|
|
3645
|
+
// -------------------------------------------------------------------------
|
|
3646
|
+
{
|
|
3647
|
+
name: "onchain_earn_cancel",
|
|
3648
|
+
module: "earn.onchain",
|
|
3649
|
+
description: "Cancel pending on-chain earn purchase. [CAUTION] Cancels a pending investment order. Not supported in demo mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3650
|
+
isWrite: true,
|
|
3651
|
+
inputSchema: {
|
|
3652
|
+
type: "object",
|
|
3653
|
+
properties: {
|
|
3654
|
+
ordId: {
|
|
3655
|
+
type: "string",
|
|
3656
|
+
description: "Order ID to cancel"
|
|
3657
|
+
},
|
|
3658
|
+
protocolType: {
|
|
3659
|
+
type: "string",
|
|
3660
|
+
description: "Protocol type: staking, defi"
|
|
3661
|
+
}
|
|
3662
|
+
},
|
|
3663
|
+
required: ["ordId", "protocolType"]
|
|
3664
|
+
},
|
|
3665
|
+
handler: async (rawArgs, context) => {
|
|
3666
|
+
assertNotDemo(context.config, "onchain_earn_cancel");
|
|
3667
|
+
const args = asRecord(rawArgs);
|
|
3668
|
+
const response = await context.client.privatePost(
|
|
3669
|
+
"/api/v5/finance/staking-defi/cancel",
|
|
3670
|
+
{
|
|
3671
|
+
ordId: requireString(args, "ordId"),
|
|
3672
|
+
protocolType: requireString(args, "protocolType")
|
|
3673
|
+
},
|
|
3674
|
+
privateRateLimit("onchain_earn_cancel", 2)
|
|
3675
|
+
);
|
|
3676
|
+
return normalizeResponse(response);
|
|
3677
|
+
}
|
|
3678
|
+
},
|
|
3679
|
+
// -------------------------------------------------------------------------
|
|
3680
|
+
// Get Active Orders
|
|
3681
|
+
// -------------------------------------------------------------------------
|
|
3682
|
+
{
|
|
3683
|
+
name: "onchain_earn_get_active_orders",
|
|
3684
|
+
module: "earn.onchain",
|
|
3685
|
+
description: "Get active on-chain earn orders. Returns current staking/DeFi investments. Private endpoint. Rate limit: 3 req/s.",
|
|
3686
|
+
isWrite: false,
|
|
3687
|
+
inputSchema: {
|
|
3688
|
+
type: "object",
|
|
3689
|
+
properties: {
|
|
3690
|
+
productId: {
|
|
3691
|
+
type: "string",
|
|
3692
|
+
description: "Filter by product ID. Omit for all."
|
|
3693
|
+
},
|
|
3694
|
+
protocolType: {
|
|
3695
|
+
type: "string",
|
|
3696
|
+
description: "Filter by protocol type: staking, defi. Omit for all."
|
|
3697
|
+
},
|
|
3698
|
+
ccy: {
|
|
3093
3699
|
type: "string",
|
|
3094
|
-
description: "e.g.
|
|
3700
|
+
description: "Filter by currency, e.g. ETH. Omit for all."
|
|
3095
3701
|
},
|
|
3096
|
-
|
|
3097
|
-
type: "string"
|
|
3702
|
+
state: {
|
|
3703
|
+
type: "string",
|
|
3704
|
+
description: "Filter by state: 8 (pending), 13 (cancelling), 9 (onchain), 1 (earning), 2 (redeeming). Omit for all."
|
|
3098
3705
|
}
|
|
3099
3706
|
}
|
|
3100
3707
|
},
|
|
3101
3708
|
handler: async (rawArgs, context) => {
|
|
3102
3709
|
const args = asRecord(rawArgs);
|
|
3103
|
-
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3104
|
-
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3105
3710
|
const response = await context.client.privateGet(
|
|
3106
|
-
"/api/v5/
|
|
3711
|
+
"/api/v5/finance/staking-defi/orders-active",
|
|
3107
3712
|
compactObject({
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3713
|
+
productId: readString(args, "productId"),
|
|
3714
|
+
protocolType: readString(args, "protocolType"),
|
|
3715
|
+
ccy: readString(args, "ccy"),
|
|
3716
|
+
state: readString(args, "state")
|
|
3111
3717
|
}),
|
|
3112
|
-
privateRateLimit("
|
|
3718
|
+
privateRateLimit("onchain_earn_get_active_orders", 3)
|
|
3113
3719
|
);
|
|
3114
|
-
return
|
|
3720
|
+
return normalizeResponse(response);
|
|
3115
3721
|
}
|
|
3116
3722
|
},
|
|
3723
|
+
// -------------------------------------------------------------------------
|
|
3724
|
+
// Get Order History
|
|
3725
|
+
// -------------------------------------------------------------------------
|
|
3117
3726
|
{
|
|
3118
|
-
name: "
|
|
3119
|
-
module: "
|
|
3120
|
-
description: "Get
|
|
3727
|
+
name: "onchain_earn_get_order_history",
|
|
3728
|
+
module: "earn.onchain",
|
|
3729
|
+
description: "Get on-chain earn order history. Returns past staking/DeFi investments including redeemed orders. Private endpoint. Rate limit: 3 req/s.",
|
|
3121
3730
|
isWrite: false,
|
|
3122
3731
|
inputSchema: {
|
|
3123
3732
|
type: "object",
|
|
3124
3733
|
properties: {
|
|
3125
|
-
|
|
3126
|
-
type: "boolean",
|
|
3127
|
-
description: "true=up to 3 months; false=last 3 days (default)"
|
|
3128
|
-
},
|
|
3129
|
-
instType: {
|
|
3734
|
+
productId: {
|
|
3130
3735
|
type: "string",
|
|
3131
|
-
|
|
3132
|
-
description: "FUTURES (default) or SWAP"
|
|
3736
|
+
description: "Filter by product ID. Omit for all."
|
|
3133
3737
|
},
|
|
3134
|
-
|
|
3738
|
+
protocolType: {
|
|
3135
3739
|
type: "string",
|
|
3136
|
-
description: "
|
|
3740
|
+
description: "Filter by protocol type: staking, defi. Omit for all."
|
|
3137
3741
|
},
|
|
3138
|
-
|
|
3742
|
+
ccy: {
|
|
3139
3743
|
type: "string",
|
|
3140
|
-
description: "
|
|
3744
|
+
description: "Filter by currency, e.g. ETH. Omit for all."
|
|
3141
3745
|
},
|
|
3142
3746
|
after: {
|
|
3143
3747
|
type: "string",
|
|
3144
|
-
description: "Pagination: before this
|
|
3748
|
+
description: "Pagination: return results before this order ID"
|
|
3145
3749
|
},
|
|
3146
3750
|
before: {
|
|
3147
3751
|
type: "string",
|
|
3148
|
-
description: "Pagination: after this
|
|
3149
|
-
},
|
|
3150
|
-
begin: {
|
|
3151
|
-
type: "string",
|
|
3152
|
-
description: "Start time (ms)"
|
|
3153
|
-
},
|
|
3154
|
-
end: {
|
|
3155
|
-
type: "string",
|
|
3156
|
-
description: "End time (ms)"
|
|
3752
|
+
description: "Pagination: return results after this order ID"
|
|
3157
3753
|
},
|
|
3158
3754
|
limit: {
|
|
3159
|
-
type: "
|
|
3160
|
-
description: "Max results (default 100
|
|
3755
|
+
type: "string",
|
|
3756
|
+
description: "Max results to return (default 100, max 100)"
|
|
3161
3757
|
}
|
|
3162
3758
|
}
|
|
3163
3759
|
},
|
|
3164
3760
|
handler: async (rawArgs, context) => {
|
|
3165
3761
|
const args = asRecord(rawArgs);
|
|
3166
|
-
const archive = readBoolean(args, "archive") ?? false;
|
|
3167
|
-
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3168
|
-
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3169
|
-
const path4 = archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
3170
3762
|
const response = await context.client.privateGet(
|
|
3171
|
-
|
|
3763
|
+
"/api/v5/finance/staking-defi/orders-history",
|
|
3172
3764
|
compactObject({
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3765
|
+
productId: readString(args, "productId"),
|
|
3766
|
+
protocolType: readString(args, "protocolType"),
|
|
3767
|
+
ccy: readString(args, "ccy"),
|
|
3176
3768
|
after: readString(args, "after"),
|
|
3177
3769
|
before: readString(args, "before"),
|
|
3178
|
-
|
|
3179
|
-
end: readString(args, "end"),
|
|
3180
|
-
limit: readNumber(args, "limit") ?? (archive ? 20 : void 0)
|
|
3770
|
+
limit: readString(args, "limit")
|
|
3181
3771
|
}),
|
|
3182
|
-
privateRateLimit("
|
|
3772
|
+
privateRateLimit("onchain_earn_get_order_history", 3)
|
|
3183
3773
|
);
|
|
3184
|
-
return
|
|
3774
|
+
return normalizeResponse(response);
|
|
3185
3775
|
}
|
|
3186
3776
|
}
|
|
3187
3777
|
];
|
|
@@ -3703,7 +4293,7 @@ function registerOptionTools() {
|
|
|
3703
4293
|
},
|
|
3704
4294
|
sz: {
|
|
3705
4295
|
type: "string",
|
|
3706
|
-
description: "Number of contracts"
|
|
4296
|
+
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
3707
4297
|
},
|
|
3708
4298
|
px: {
|
|
3709
4299
|
type: "string",
|
|
@@ -3810,7 +4400,7 @@ function registerOptionTools() {
|
|
|
3810
4400
|
instId: { type: "string", description: "e.g. BTC-USD-241227-50000-C" },
|
|
3811
4401
|
ordId: { type: "string" },
|
|
3812
4402
|
clOrdId: { type: "string" },
|
|
3813
|
-
newSz: { type: "string", description: "New
|
|
4403
|
+
newSz: { type: "string", description: "New number of contracts (NOT USDT amount)" },
|
|
3814
4404
|
newPx: { type: "string", description: "New price" }
|
|
3815
4405
|
},
|
|
3816
4406
|
required: ["instId"]
|
|
@@ -4772,7 +5362,7 @@ function registerSwapTradeTools() {
|
|
|
4772
5362
|
},
|
|
4773
5363
|
sz: {
|
|
4774
5364
|
type: "string",
|
|
4775
|
-
description: "
|
|
5365
|
+
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
4776
5366
|
},
|
|
4777
5367
|
px: {
|
|
4778
5368
|
type: "string",
|
|
@@ -5037,7 +5627,7 @@ function registerSwapTradeTools() {
|
|
|
5037
5627
|
properties: {
|
|
5038
5628
|
instId: { type: "string", description: "e.g. BTC-USDT-SWAP" },
|
|
5039
5629
|
algoId: { type: "string", description: "Algo order ID" },
|
|
5040
|
-
newSz: { type: "string", description: "New
|
|
5630
|
+
newSz: { type: "string", description: "New number of contracts (NOT USDT amount)" },
|
|
5041
5631
|
newTpTriggerPx: { type: "string", description: "New TP trigger price" },
|
|
5042
5632
|
newTpOrdPx: { type: "string", description: "New TP order price; -1=market" },
|
|
5043
5633
|
newSlTriggerPx: { type: "string", description: "New SL trigger price" },
|
|
@@ -5390,6 +5980,8 @@ function allToolSpecs() {
|
|
|
5390
5980
|
...registerAlgoTradeTools(),
|
|
5391
5981
|
...registerAccountTools(),
|
|
5392
5982
|
...registerBotTools(),
|
|
5983
|
+
...registerEarnTools(),
|
|
5984
|
+
...registerOnchainEarnTools(),
|
|
5393
5985
|
...registerAuditTools()
|
|
5394
5986
|
];
|
|
5395
5987
|
}
|
|
@@ -5427,8 +6019,15 @@ function writeFullConfig(config) {
|
|
|
5427
6019
|
writeFileSync(path4, stringify(config), "utf-8");
|
|
5428
6020
|
}
|
|
5429
6021
|
var BASE_MODULES = MODULES.filter(
|
|
5430
|
-
(m) => !BOT_SUB_MODULE_IDS.includes(m)
|
|
6022
|
+
(m) => !BOT_SUB_MODULE_IDS.includes(m) && !EARN_SUB_MODULE_IDS.includes(m)
|
|
5431
6023
|
);
|
|
6024
|
+
function expandShorthand(moduleId) {
|
|
6025
|
+
if (moduleId === "all") return [...BASE_MODULES, ...BOT_SUB_MODULE_IDS];
|
|
6026
|
+
if (moduleId === "earn" || moduleId === "earn.all") return [...EARN_SUB_MODULE_IDS];
|
|
6027
|
+
if (moduleId === "bot") return [...BOT_DEFAULT_SUB_MODULES];
|
|
6028
|
+
if (moduleId === "bot.all") return [...BOT_SUB_MODULE_IDS];
|
|
6029
|
+
return null;
|
|
6030
|
+
}
|
|
5432
6031
|
function parseModuleList(rawModules) {
|
|
5433
6032
|
if (!rawModules || rawModules.trim().length === 0) {
|
|
5434
6033
|
return [...DEFAULT_MODULES];
|
|
@@ -5437,32 +6036,28 @@ function parseModuleList(rawModules) {
|
|
|
5437
6036
|
if (trimmed === "all") {
|
|
5438
6037
|
return [...BASE_MODULES, ...BOT_SUB_MODULE_IDS];
|
|
5439
6038
|
}
|
|
5440
|
-
const requested = trimmed.split(",").map((
|
|
6039
|
+
const requested = trimmed.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5441
6040
|
if (requested.length === 0) {
|
|
5442
6041
|
return [...DEFAULT_MODULES];
|
|
5443
6042
|
}
|
|
5444
6043
|
const deduped = /* @__PURE__ */ new Set();
|
|
5445
6044
|
for (const moduleId of requested) {
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
}
|
|
5450
|
-
if (moduleId === "bot.all") {
|
|
5451
|
-
for (const sub of BOT_SUB_MODULE_IDS) deduped.add(sub);
|
|
6045
|
+
const expanded = expandShorthand(moduleId);
|
|
6046
|
+
if (expanded) {
|
|
6047
|
+
expanded.forEach((sub) => deduped.add(sub));
|
|
5452
6048
|
continue;
|
|
5453
6049
|
}
|
|
5454
6050
|
if (!MODULES.includes(moduleId)) {
|
|
5455
6051
|
throw new ConfigError(
|
|
5456
6052
|
`Unknown module "${moduleId}".`,
|
|
5457
|
-
`Use one of: ${MODULES.join(", ")}, "bot", "bot.all", or "all".`
|
|
6053
|
+
`Use one of: ${MODULES.join(", ")}, "earn", "earn.all", "bot", "bot.all", or "all".`
|
|
5458
6054
|
);
|
|
5459
6055
|
}
|
|
5460
6056
|
deduped.add(moduleId);
|
|
5461
6057
|
}
|
|
5462
6058
|
return Array.from(deduped);
|
|
5463
6059
|
}
|
|
5464
|
-
function
|
|
5465
|
-
const toml = readTomlProfile(cli.profile);
|
|
6060
|
+
function loadCredentials(toml) {
|
|
5466
6061
|
const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
|
|
5467
6062
|
const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
5468
6063
|
const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
|
|
@@ -5474,23 +6069,34 @@ function loadConfig(cli) {
|
|
|
5474
6069
|
"Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
|
|
5475
6070
|
);
|
|
5476
6071
|
}
|
|
5477
|
-
|
|
5478
|
-
|
|
6072
|
+
return { apiKey, secretKey, passphrase, hasAuth };
|
|
6073
|
+
}
|
|
6074
|
+
function resolveSite(cliSite, tomlSite) {
|
|
6075
|
+
const rawSite = cliSite?.trim() ?? process.env.OKX_SITE?.trim() ?? tomlSite ?? "global";
|
|
5479
6076
|
if (!SITE_IDS.includes(rawSite)) {
|
|
5480
6077
|
throw new ConfigError(
|
|
5481
6078
|
`Unknown site "${rawSite}".`,
|
|
5482
6079
|
`Use one of: ${SITE_IDS.join(", ")}.`
|
|
5483
6080
|
);
|
|
5484
6081
|
}
|
|
5485
|
-
|
|
5486
|
-
|
|
6082
|
+
return rawSite;
|
|
6083
|
+
}
|
|
6084
|
+
function resolveBaseUrl(site, tomlBaseUrl) {
|
|
6085
|
+
const rawBaseUrl = process.env.OKX_API_BASE_URL?.trim() ?? tomlBaseUrl ?? OKX_SITES[site].apiBaseUrl;
|
|
5487
6086
|
if (!rawBaseUrl.startsWith("http://") && !rawBaseUrl.startsWith("https://")) {
|
|
5488
6087
|
throw new ConfigError(
|
|
5489
6088
|
`Invalid base URL "${rawBaseUrl}".`,
|
|
5490
6089
|
"OKX_API_BASE_URL must start with http:// or https://"
|
|
5491
6090
|
);
|
|
5492
6091
|
}
|
|
5493
|
-
|
|
6092
|
+
return rawBaseUrl.replace(/\/+$/, "");
|
|
6093
|
+
}
|
|
6094
|
+
function loadConfig(cli) {
|
|
6095
|
+
const toml = readTomlProfile(cli.profile);
|
|
6096
|
+
const creds = loadCredentials(toml);
|
|
6097
|
+
const demo = cli.demo || process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
6098
|
+
const site = resolveSite(cli.site, toml.site);
|
|
6099
|
+
const baseUrl = resolveBaseUrl(site, toml.base_url);
|
|
5494
6100
|
const rawTimeout = process.env.OKX_TIMEOUT_MS ? Number(process.env.OKX_TIMEOUT_MS) : toml.timeout_ms ?? 15e3;
|
|
5495
6101
|
if (!Number.isFinite(rawTimeout) || rawTimeout <= 0) {
|
|
5496
6102
|
throw new ConfigError(
|
|
@@ -5498,11 +6104,15 @@ function loadConfig(cli) {
|
|
|
5498
6104
|
"Set OKX_TIMEOUT_MS as a positive integer in milliseconds."
|
|
5499
6105
|
);
|
|
5500
6106
|
}
|
|
6107
|
+
const rawProxyUrl = toml.proxy_url?.trim();
|
|
6108
|
+
if (rawProxyUrl && !rawProxyUrl.startsWith("http://") && !rawProxyUrl.startsWith("https://")) {
|
|
6109
|
+
throw new ConfigError(
|
|
6110
|
+
`Invalid proxy URL "${rawProxyUrl}".`,
|
|
6111
|
+
"proxy_url must start with http:// or https://. SOCKS proxies are not supported."
|
|
6112
|
+
);
|
|
6113
|
+
}
|
|
5501
6114
|
return {
|
|
5502
|
-
|
|
5503
|
-
secretKey,
|
|
5504
|
-
passphrase,
|
|
5505
|
-
hasAuth,
|
|
6115
|
+
...creds,
|
|
5506
6116
|
baseUrl,
|
|
5507
6117
|
timeoutMs: Math.floor(rawTimeout),
|
|
5508
6118
|
modules: parseModuleList(cli.modules),
|
|
@@ -5510,7 +6120,9 @@ function loadConfig(cli) {
|
|
|
5510
6120
|
demo,
|
|
5511
6121
|
site,
|
|
5512
6122
|
userAgent: cli.userAgent,
|
|
5513
|
-
sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG
|
|
6123
|
+
sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG,
|
|
6124
|
+
proxyUrl: rawProxyUrl || void 0,
|
|
6125
|
+
verbose: cli.verbose ?? false
|
|
5514
6126
|
};
|
|
5515
6127
|
}
|
|
5516
6128
|
var CACHE_FILE = join2(homedir2(), ".okx", "update-check.json");
|
|
@@ -5726,6 +6338,313 @@ function runSetup(options) {
|
|
|
5726
6338
|
}
|
|
5727
6339
|
}
|
|
5728
6340
|
|
|
6341
|
+
// src/commands/diagnose.ts
|
|
6342
|
+
import dns from "dns/promises";
|
|
6343
|
+
import net from "net";
|
|
6344
|
+
import os2 from "os";
|
|
6345
|
+
import tls from "tls";
|
|
6346
|
+
import { createRequire } from "module";
|
|
6347
|
+
var _require = createRequire(import.meta.url);
|
|
6348
|
+
function readCliVersion() {
|
|
6349
|
+
for (const rel of ["../package.json", "../../package.json"]) {
|
|
6350
|
+
try {
|
|
6351
|
+
return _require(rel).version;
|
|
6352
|
+
} catch (_err) {
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
return "0.0.0";
|
|
6356
|
+
}
|
|
6357
|
+
var CLI_VERSION = readCliVersion();
|
|
6358
|
+
var GIT_HASH = true ? "4427916" : "dev";
|
|
6359
|
+
var Report = class {
|
|
6360
|
+
lines = [];
|
|
6361
|
+
add(key, value) {
|
|
6362
|
+
this.lines.push({ key, value });
|
|
6363
|
+
}
|
|
6364
|
+
print() {
|
|
6365
|
+
const w = process.stdout.write.bind(process.stdout);
|
|
6366
|
+
const sep = "\u2500".repeat(52);
|
|
6367
|
+
w(`
|
|
6368
|
+
\u2500\u2500 Diagnostic Report (copy & share) ${sep.slice(35)}
|
|
6369
|
+
`);
|
|
6370
|
+
for (const { key, value } of this.lines) {
|
|
6371
|
+
w(` ${key.padEnd(14)} ${value}
|
|
6372
|
+
`);
|
|
6373
|
+
}
|
|
6374
|
+
w(` ${sep}
|
|
6375
|
+
|
|
6376
|
+
`);
|
|
6377
|
+
}
|
|
6378
|
+
};
|
|
6379
|
+
function ok(label, detail) {
|
|
6380
|
+
process.stdout.write(` \u2713 ${label.padEnd(14)} ${detail}
|
|
6381
|
+
`);
|
|
6382
|
+
}
|
|
6383
|
+
function fail(label, detail, hints) {
|
|
6384
|
+
process.stdout.write(` \u2717 ${label.padEnd(14)} ${detail}
|
|
6385
|
+
`);
|
|
6386
|
+
for (const hint of hints) {
|
|
6387
|
+
process.stdout.write(` \u2192 ${hint}
|
|
6388
|
+
`);
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
function section(title) {
|
|
6392
|
+
process.stdout.write(`
|
|
6393
|
+
${title}
|
|
6394
|
+
`);
|
|
6395
|
+
}
|
|
6396
|
+
function maskKey2(key) {
|
|
6397
|
+
if (!key) return "(not set)";
|
|
6398
|
+
if (key.length <= 8) return "****";
|
|
6399
|
+
return `${key.slice(0, 2)}****${key.slice(-2)}`;
|
|
6400
|
+
}
|
|
6401
|
+
async function checkDns(hostname) {
|
|
6402
|
+
const t0 = Date.now();
|
|
6403
|
+
try {
|
|
6404
|
+
const addresses = await dns.resolve4(hostname);
|
|
6405
|
+
return { ok: true, ip: addresses[0], ms: Date.now() - t0 };
|
|
6406
|
+
} catch (e) {
|
|
6407
|
+
return { ok: false, ms: Date.now() - t0, error: e instanceof Error ? e.message : String(e) };
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
6411
|
+
const t0 = Date.now();
|
|
6412
|
+
return new Promise((resolve) => {
|
|
6413
|
+
const socket = createFn();
|
|
6414
|
+
const cleanup = () => {
|
|
6415
|
+
socket.removeAllListeners();
|
|
6416
|
+
socket.destroy();
|
|
6417
|
+
};
|
|
6418
|
+
socket.once(successEvent, () => {
|
|
6419
|
+
cleanup();
|
|
6420
|
+
resolve({ ok: true, ms: Date.now() - t0 });
|
|
6421
|
+
});
|
|
6422
|
+
socket.once("timeout", () => {
|
|
6423
|
+
cleanup();
|
|
6424
|
+
resolve({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
|
|
6425
|
+
});
|
|
6426
|
+
socket.once("error", (err) => {
|
|
6427
|
+
cleanup();
|
|
6428
|
+
resolve({ ok: false, ms: Date.now() - t0, error: err.message });
|
|
6429
|
+
});
|
|
6430
|
+
});
|
|
6431
|
+
}
|
|
6432
|
+
async function checkTcp(hostname, port, timeoutMs = 5e3) {
|
|
6433
|
+
return checkSocket(
|
|
6434
|
+
() => net.createConnection({ host: hostname, port, timeout: timeoutMs }),
|
|
6435
|
+
"connect",
|
|
6436
|
+
timeoutMs
|
|
6437
|
+
);
|
|
6438
|
+
}
|
|
6439
|
+
async function checkTls(hostname, port, timeoutMs = 5e3) {
|
|
6440
|
+
return checkSocket(
|
|
6441
|
+
() => tls.connect({ host: hostname, port, timeout: timeoutMs, servername: hostname }),
|
|
6442
|
+
"secureConnect",
|
|
6443
|
+
timeoutMs
|
|
6444
|
+
);
|
|
6445
|
+
}
|
|
6446
|
+
function checkProxyEnv(report) {
|
|
6447
|
+
const httpProxy = process.env.HTTP_PROXY ?? process.env.http_proxy;
|
|
6448
|
+
const httpsProxy = process.env.HTTPS_PROXY ?? process.env.https_proxy;
|
|
6449
|
+
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy;
|
|
6450
|
+
if (httpProxy || httpsProxy) {
|
|
6451
|
+
ok("HTTP_PROXY", httpProxy ?? "(not set)");
|
|
6452
|
+
ok("HTTPS_PROXY", httpsProxy ?? "(not set)");
|
|
6453
|
+
if (noProxy) ok("NO_PROXY", noProxy);
|
|
6454
|
+
report.add("http_proxy", httpProxy ?? "-");
|
|
6455
|
+
report.add("https_proxy", httpsProxy ?? "-");
|
|
6456
|
+
if (noProxy) report.add("no_proxy", noProxy);
|
|
6457
|
+
} else {
|
|
6458
|
+
ok("Proxy", "(none)");
|
|
6459
|
+
report.add("proxy", "none");
|
|
6460
|
+
}
|
|
6461
|
+
}
|
|
6462
|
+
function checkEnvironment(report) {
|
|
6463
|
+
let passed = true;
|
|
6464
|
+
section("Environment");
|
|
6465
|
+
const nodeVersion = process.version;
|
|
6466
|
+
const nodeMajor = parseInt(nodeVersion.slice(1), 10);
|
|
6467
|
+
if (nodeMajor >= 18) {
|
|
6468
|
+
ok("Node.js", `${nodeVersion} (>= 18 required)`);
|
|
6469
|
+
} else {
|
|
6470
|
+
fail("Node.js", `${nodeVersion} (>= 18 required)`, ["Upgrade Node.js to v18 or later"]);
|
|
6471
|
+
passed = false;
|
|
6472
|
+
}
|
|
6473
|
+
ok("CLI", `v${CLI_VERSION} (${GIT_HASH})`);
|
|
6474
|
+
ok("OS", `${process.platform} ${process.arch}`);
|
|
6475
|
+
ok("OS release", os2.release());
|
|
6476
|
+
ok("Shell", process.env.SHELL ?? "(unknown)");
|
|
6477
|
+
ok("Locale", `${process.env.LANG ?? process.env.LC_ALL ?? "(unknown)"}`);
|
|
6478
|
+
ok("Timezone", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
6479
|
+
report.add("cli", `${CLI_VERSION} (${GIT_HASH})`);
|
|
6480
|
+
report.add("node", `${nodeVersion} ${process.platform} ${process.arch}`);
|
|
6481
|
+
const machine = typeof os2.machine === "function" ? os2.machine() : process.arch;
|
|
6482
|
+
report.add("os", `${os2.type()} ${os2.release()} ${machine}`);
|
|
6483
|
+
report.add("shell", process.env.SHELL ?? "-");
|
|
6484
|
+
report.add("locale", process.env.LANG ?? process.env.LC_ALL ?? "-");
|
|
6485
|
+
report.add("tz", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
6486
|
+
checkProxyEnv(report);
|
|
6487
|
+
return passed;
|
|
6488
|
+
}
|
|
6489
|
+
function checkConfig(config, profile, report) {
|
|
6490
|
+
let passed = true;
|
|
6491
|
+
section(`Config (profile: ${profile})`);
|
|
6492
|
+
if (config.hasAuth) {
|
|
6493
|
+
ok("API key", maskKey2(config.apiKey));
|
|
6494
|
+
ok("Secret", "****");
|
|
6495
|
+
ok("Passphrase", "****");
|
|
6496
|
+
} else {
|
|
6497
|
+
fail("Credentials", "not configured", [
|
|
6498
|
+
"Set OKX_API_KEY, OKX_SECRET_KEY, OKX_PASSPHRASE env vars",
|
|
6499
|
+
"Or run: okx config init"
|
|
6500
|
+
]);
|
|
6501
|
+
passed = false;
|
|
6502
|
+
}
|
|
6503
|
+
ok("Demo mode", String(config.demo));
|
|
6504
|
+
ok("Site", config.site);
|
|
6505
|
+
ok("Base URL", config.baseUrl);
|
|
6506
|
+
ok("Timeout", `${config.timeoutMs}ms`);
|
|
6507
|
+
report.add("profile", profile);
|
|
6508
|
+
report.add("site", config.site);
|
|
6509
|
+
report.add("base", config.baseUrl);
|
|
6510
|
+
report.add("auth", config.hasAuth ? `true (key=${maskKey2(config.apiKey)})` : "false");
|
|
6511
|
+
report.add("demo", String(config.demo));
|
|
6512
|
+
report.add("timeout", `${config.timeoutMs}ms`);
|
|
6513
|
+
return passed;
|
|
6514
|
+
}
|
|
6515
|
+
async function checkTcpTls(hostname, port, protocol, report) {
|
|
6516
|
+
let passed = true;
|
|
6517
|
+
const tcpResult = await checkTcp(hostname, port);
|
|
6518
|
+
if (tcpResult.ok) {
|
|
6519
|
+
ok("TCP connect", `port ${port} (${tcpResult.ms}ms)`);
|
|
6520
|
+
report.add("tcp", `${port} OK (${tcpResult.ms}ms)`);
|
|
6521
|
+
} else {
|
|
6522
|
+
fail("TCP connect", `port ${port} \u2014 ${tcpResult.error}`, [
|
|
6523
|
+
"Check firewall/proxy/VPN settings",
|
|
6524
|
+
`Try: nc -zv ${hostname} ${port}`
|
|
6525
|
+
]);
|
|
6526
|
+
report.add("tcp", `FAIL ${port} ${tcpResult.error} (${tcpResult.ms}ms)`);
|
|
6527
|
+
return false;
|
|
6528
|
+
}
|
|
6529
|
+
if (protocol === "https:") {
|
|
6530
|
+
const tlsResult = await checkTls(hostname, port);
|
|
6531
|
+
if (tlsResult.ok) {
|
|
6532
|
+
ok("TLS handshake", `(${tlsResult.ms}ms)`);
|
|
6533
|
+
report.add("tls", `OK (${tlsResult.ms}ms)`);
|
|
6534
|
+
} else {
|
|
6535
|
+
fail("TLS handshake", tlsResult.error ?? "failed", [
|
|
6536
|
+
"Check system certificates or proxy MITM settings"
|
|
6537
|
+
]);
|
|
6538
|
+
passed = false;
|
|
6539
|
+
report.add("tls", `FAIL ${tlsResult.error} (${tlsResult.ms}ms)`);
|
|
6540
|
+
}
|
|
6541
|
+
}
|
|
6542
|
+
return passed;
|
|
6543
|
+
}
|
|
6544
|
+
async function checkNetwork(config, client, report) {
|
|
6545
|
+
let passed = true;
|
|
6546
|
+
section("Network");
|
|
6547
|
+
const url = new URL(config.baseUrl);
|
|
6548
|
+
const hostname = url.hostname;
|
|
6549
|
+
const defaultPort = url.protocol === "https:" ? 443 : 80;
|
|
6550
|
+
const port = url.port ? parseInt(url.port, 10) : defaultPort;
|
|
6551
|
+
const dnsResult = await checkDns(hostname);
|
|
6552
|
+
if (dnsResult.ok) {
|
|
6553
|
+
ok("DNS resolve", `${hostname} \u2192 ${dnsResult.ip} (${dnsResult.ms}ms)`);
|
|
6554
|
+
report.add("dns", `${hostname} \u2192 ${dnsResult.ip} (${dnsResult.ms}ms)`);
|
|
6555
|
+
} else {
|
|
6556
|
+
fail("DNS resolve", `${hostname} \u2014 ${dnsResult.error}`, [
|
|
6557
|
+
"Check DNS settings or network connection",
|
|
6558
|
+
`Try: nslookup ${hostname}`
|
|
6559
|
+
]);
|
|
6560
|
+
passed = false;
|
|
6561
|
+
report.add("dns", `FAIL ${hostname} ${dnsResult.error} (${dnsResult.ms}ms)`);
|
|
6562
|
+
}
|
|
6563
|
+
if (dnsResult.ok) {
|
|
6564
|
+
const tcpTlsPassed = await checkTcpTls(hostname, port, url.protocol, report);
|
|
6565
|
+
if (!tcpTlsPassed) passed = false;
|
|
6566
|
+
}
|
|
6567
|
+
const t0 = Date.now();
|
|
6568
|
+
try {
|
|
6569
|
+
await client.publicGet("/api/v5/public/time");
|
|
6570
|
+
const ms = Date.now() - t0;
|
|
6571
|
+
ok("API /public/time", `200 (${ms}ms)`);
|
|
6572
|
+
report.add("api", `/public/time 200 (${ms}ms)`);
|
|
6573
|
+
} catch (e) {
|
|
6574
|
+
const ms = Date.now() - t0;
|
|
6575
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
6576
|
+
fail("API /public/time", msg, [
|
|
6577
|
+
"OKX API may be down or blocked in your network",
|
|
6578
|
+
`Try: curl ${config.baseUrl}/api/v5/public/time`
|
|
6579
|
+
]);
|
|
6580
|
+
passed = false;
|
|
6581
|
+
report.add("api", `FAIL /public/time ${msg} (${ms}ms)`);
|
|
6582
|
+
}
|
|
6583
|
+
return passed;
|
|
6584
|
+
}
|
|
6585
|
+
function getAuthHints(msg) {
|
|
6586
|
+
if (msg.includes("50111") || msg.includes("Invalid OK-ACCESS-KEY")) {
|
|
6587
|
+
return ["API key is invalid or expired", "Regenerate at https://www.okx.com/account/my-api"];
|
|
6588
|
+
}
|
|
6589
|
+
if (msg.includes("50112") || msg.includes("Invalid Sign")) {
|
|
6590
|
+
return ["Secret key or passphrase may be wrong", "Regenerate API key at https://www.okx.com/account/my-api"];
|
|
6591
|
+
}
|
|
6592
|
+
if (msg.includes("50113")) {
|
|
6593
|
+
return ["Passphrase is incorrect"];
|
|
6594
|
+
}
|
|
6595
|
+
if (msg.includes("50100")) {
|
|
6596
|
+
return ["API key lacks required permissions", "Update permissions at https://www.okx.com/account/my-api"];
|
|
6597
|
+
}
|
|
6598
|
+
return ["Check API credentials and permissions"];
|
|
6599
|
+
}
|
|
6600
|
+
async function checkAuth(client, config, report) {
|
|
6601
|
+
if (!config.hasAuth) {
|
|
6602
|
+
report.add("auth_api", "skipped (no credentials)");
|
|
6603
|
+
return true;
|
|
6604
|
+
}
|
|
6605
|
+
let passed = true;
|
|
6606
|
+
section("Authentication");
|
|
6607
|
+
const t1 = Date.now();
|
|
6608
|
+
try {
|
|
6609
|
+
await client.privateGet("/api/v5/account/balance");
|
|
6610
|
+
const ms = Date.now() - t1;
|
|
6611
|
+
ok("Account balance", `200 (${ms}ms)`);
|
|
6612
|
+
if (config.demo) {
|
|
6613
|
+
ok("Demo header", "x-simulated-trading: 1");
|
|
6614
|
+
}
|
|
6615
|
+
report.add("auth_api", `/account/balance 200 (${ms}ms)`);
|
|
6616
|
+
} catch (e) {
|
|
6617
|
+
const ms = Date.now() - t1;
|
|
6618
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
6619
|
+
const hints = getAuthHints(msg);
|
|
6620
|
+
fail("Account balance", msg, hints);
|
|
6621
|
+
passed = false;
|
|
6622
|
+
report.add("auth_api", `FAIL /account/balance ${msg} (${ms}ms)`);
|
|
6623
|
+
}
|
|
6624
|
+
return passed;
|
|
6625
|
+
}
|
|
6626
|
+
async function cmdDiagnose(config, profile) {
|
|
6627
|
+
process.stdout.write("\n OKX Trade CLI Diagnostics\n");
|
|
6628
|
+
process.stdout.write(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
6629
|
+
const report = new Report();
|
|
6630
|
+
report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
6631
|
+
const envPassed = checkEnvironment(report);
|
|
6632
|
+
const cfgPassed = checkConfig(config, profile, report);
|
|
6633
|
+
const client = new OkxRestClient(config);
|
|
6634
|
+
const netPassed = await checkNetwork(config, client, report);
|
|
6635
|
+
const authPassed = await checkAuth(client, config, report);
|
|
6636
|
+
const allPassed = envPassed && cfgPassed && netPassed && authPassed;
|
|
6637
|
+
process.stdout.write("\n");
|
|
6638
|
+
if (allPassed) {
|
|
6639
|
+
process.stdout.write(" Result: All checks passed \u2713\n");
|
|
6640
|
+
} else {
|
|
6641
|
+
process.stdout.write(" Result: Some checks failed \u2717\n");
|
|
6642
|
+
process.exitCode = 1;
|
|
6643
|
+
}
|
|
6644
|
+
report.add("result", allPassed ? "PASS" : "FAIL");
|
|
6645
|
+
report.print();
|
|
6646
|
+
}
|
|
6647
|
+
|
|
5729
6648
|
// src/config/loader.ts
|
|
5730
6649
|
function loadProfileConfig(opts) {
|
|
5731
6650
|
return loadConfig({
|
|
@@ -5735,32 +6654,11 @@ function loadProfileConfig(opts) {
|
|
|
5735
6654
|
demo: opts.demo ?? false,
|
|
5736
6655
|
site: opts.site,
|
|
5737
6656
|
userAgent: opts.userAgent,
|
|
5738
|
-
sourceTag: opts.sourceTag
|
|
6657
|
+
sourceTag: opts.sourceTag,
|
|
6658
|
+
verbose: opts.verbose
|
|
5739
6659
|
});
|
|
5740
6660
|
}
|
|
5741
6661
|
|
|
5742
|
-
// src/tips.ts
|
|
5743
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
5744
|
-
import { join as join4 } from "path";
|
|
5745
|
-
import { homedir as homedir4 } from "os";
|
|
5746
|
-
function showFirstRunTips(version) {
|
|
5747
|
-
const okxDir = join4(homedir4(), ".okx");
|
|
5748
|
-
const marker = join4(okxDir, ".tips-shown");
|
|
5749
|
-
if (existsSync4(marker)) return;
|
|
5750
|
-
const w = (s) => process.stderr.write(s);
|
|
5751
|
-
w("\n");
|
|
5752
|
-
w(` @okx_ai/okx-trade-cli v${version}
|
|
5753
|
-
`);
|
|
5754
|
-
w(" \u26A0\uFE0F Security Tips: NEVER send API keys in agent chat. Create a dedicated sub-account for your agent. Test on demo before going live.\n");
|
|
5755
|
-
w(" \u26A0\uFE0F \u5B89\u5168\u63D0\u793A\uFF1A\u5207\u52FF\u5728Agent\u5BF9\u8BDD\u4E2D\u53D1\u9001API Key\u3002\u8BF7\u521B\u5EFAAgent\u4E13\u7528\u5B50\u8D26\u6237\u63A5\u5165\u3002\u5148\u5728\u6A21\u62DF\u76D8\u5145\u5206\u6D4B\u8BD5\uFF0C\u518D\u63A5\u5165\u5B9E\u76D8\u3002\n");
|
|
5756
|
-
w("\n");
|
|
5757
|
-
try {
|
|
5758
|
-
mkdirSync4(okxDir, { recursive: true });
|
|
5759
|
-
writeFileSync4(marker, (/* @__PURE__ */ new Date()).toISOString() + "\n");
|
|
5760
|
-
} catch {
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5763
|
-
|
|
5764
6662
|
// src/commands/client-setup.ts
|
|
5765
6663
|
import * as fs2 from "fs";
|
|
5766
6664
|
var DETECTABLE_CLIENTS = ["claude-desktop", "cursor", "windsurf"];
|
|
@@ -5916,8 +6814,8 @@ var HELP_TREE = {
|
|
|
5916
6814
|
description: "Get trade fill history for spot orders"
|
|
5917
6815
|
},
|
|
5918
6816
|
place: {
|
|
5919
|
-
usage: "okx spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>] [--tdMode <cash|cross|isolated>]",
|
|
5920
|
-
description: "Place a new spot order"
|
|
6817
|
+
usage: "okx spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>] [--tdMode <cash|cross|isolated>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
|
|
6818
|
+
description: "Place a new spot order (supports attached TP/SL)"
|
|
5921
6819
|
},
|
|
5922
6820
|
amend: {
|
|
5923
6821
|
usage: "okx spot amend --instId <id> --ordId <id> [--newSz <n>] [--newPx <price>]",
|
|
@@ -5976,8 +6874,8 @@ var HELP_TREE = {
|
|
|
5976
6874
|
description: "Get trade fill history for swap orders"
|
|
5977
6875
|
},
|
|
5978
6876
|
place: {
|
|
5979
|
-
usage: "okx swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>] [--tdMode <cross|isolated>]",
|
|
5980
|
-
description: "Place a new perpetual swap order"
|
|
6877
|
+
usage: "okx swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>] [--tdMode <cross|isolated>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
|
|
6878
|
+
description: "Place a new perpetual swap order (supports attached TP/SL)"
|
|
5981
6879
|
},
|
|
5982
6880
|
cancel: {
|
|
5983
6881
|
usage: "okx swap cancel <instId> --ordId <id>",
|
|
@@ -6048,8 +6946,8 @@ var HELP_TREE = {
|
|
|
6048
6946
|
description: "Get trade fill history for futures orders"
|
|
6049
6947
|
},
|
|
6050
6948
|
place: {
|
|
6051
|
-
usage: "okx futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n>\n [--tdMode <cross|isolated>] [--posSide <net|long|short>] [--px <price>] [--reduceOnly]",
|
|
6052
|
-
description: "Place a new futures order"
|
|
6949
|
+
usage: "okx futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n>\n [--tdMode <cross|isolated>] [--posSide <net|long|short>] [--px <price>] [--reduceOnly]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
|
|
6950
|
+
description: "Place a new futures order (supports attached TP/SL)"
|
|
6053
6951
|
},
|
|
6054
6952
|
cancel: {
|
|
6055
6953
|
usage: "okx futures cancel <instId> --ordId <id>",
|
|
@@ -6106,6 +7004,73 @@ var HELP_TREE = {
|
|
|
6106
7004
|
}
|
|
6107
7005
|
}
|
|
6108
7006
|
},
|
|
7007
|
+
earn: {
|
|
7008
|
+
description: "Earn products \u2014 Simple Earn (savings/lending) and On-chain Earn (staking/DeFi)",
|
|
7009
|
+
subgroups: {
|
|
7010
|
+
savings: {
|
|
7011
|
+
description: "Simple Earn \u2014 flexible savings and lending",
|
|
7012
|
+
commands: {
|
|
7013
|
+
balance: {
|
|
7014
|
+
usage: "okx earn savings balance [<ccy>]",
|
|
7015
|
+
description: "Get savings balance (optionally filter by currency)"
|
|
7016
|
+
},
|
|
7017
|
+
purchase: {
|
|
7018
|
+
usage: "okx earn savings purchase --ccy <ccy> --amt <n> [--rate <rate>]",
|
|
7019
|
+
description: "Purchase Simple Earn (flexible savings). Rate defaults to 0.01 (1%)"
|
|
7020
|
+
},
|
|
7021
|
+
redeem: {
|
|
7022
|
+
usage: "okx earn savings redeem --ccy <ccy> --amt <n>",
|
|
7023
|
+
description: "Redeem Simple Earn (flexible savings)"
|
|
7024
|
+
},
|
|
7025
|
+
"set-rate": {
|
|
7026
|
+
usage: "okx earn savings set-rate --ccy <ccy> --rate <rate>",
|
|
7027
|
+
description: "Set lending rate for a currency"
|
|
7028
|
+
},
|
|
7029
|
+
"lending-history": {
|
|
7030
|
+
usage: "okx earn savings lending-history [--ccy <ccy>] [--limit <n>]",
|
|
7031
|
+
description: "Get lending history"
|
|
7032
|
+
},
|
|
7033
|
+
"rate-summary": {
|
|
7034
|
+
usage: "okx earn savings rate-summary [<ccy>]",
|
|
7035
|
+
description: "Get market lending rate summary (public, no auth needed)"
|
|
7036
|
+
},
|
|
7037
|
+
"rate-history": {
|
|
7038
|
+
usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
|
|
7039
|
+
description: "Get historical lending rates (public, no auth needed)"
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
7042
|
+
},
|
|
7043
|
+
onchain: {
|
|
7044
|
+
description: "On-chain Earn \u2014 staking and DeFi products",
|
|
7045
|
+
commands: {
|
|
7046
|
+
offers: {
|
|
7047
|
+
usage: "okx earn onchain offers [--productId <id>] [--protocolType <type>] [--ccy <ccy>]",
|
|
7048
|
+
description: "Browse available on-chain earn products (staking, DeFi)"
|
|
7049
|
+
},
|
|
7050
|
+
purchase: {
|
|
7051
|
+
usage: "okx earn onchain purchase --productId <id> --ccy <ccy> --amt <n> [--term <term>] [--tag <tag>]",
|
|
7052
|
+
description: "Purchase an on-chain earn product (stake/deposit)"
|
|
7053
|
+
},
|
|
7054
|
+
redeem: {
|
|
7055
|
+
usage: "okx earn onchain redeem --ordId <id> --protocolType <type> [--allowEarlyRedeem]",
|
|
7056
|
+
description: "Redeem an on-chain earn position"
|
|
7057
|
+
},
|
|
7058
|
+
cancel: {
|
|
7059
|
+
usage: "okx earn onchain cancel --ordId <id> --protocolType <type>",
|
|
7060
|
+
description: "Cancel a pending on-chain earn order"
|
|
7061
|
+
},
|
|
7062
|
+
orders: {
|
|
7063
|
+
usage: "okx earn onchain orders [--productId <id>] [--protocolType <type>] [--ccy <ccy>] [--state <state>]",
|
|
7064
|
+
description: "List active on-chain earn orders"
|
|
7065
|
+
},
|
|
7066
|
+
history: {
|
|
7067
|
+
usage: "okx earn onchain history [--productId <id>] [--protocolType <type>] [--ccy <ccy>]",
|
|
7068
|
+
description: "Get on-chain earn order history"
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
},
|
|
6109
7074
|
bot: {
|
|
6110
7075
|
description: "Trading bot strategies (grid, dca)",
|
|
6111
7076
|
subgroups: {
|
|
@@ -6185,6 +7150,10 @@ var HELP_TREE = {
|
|
|
6185
7150
|
setup: {
|
|
6186
7151
|
description: "Set up client integrations (Cursor, Windsurf, Claude, etc.)",
|
|
6187
7152
|
usage: `okx setup --client <${SUPPORTED_CLIENTS.join("|")}> [--profile <name>] [--modules <list>]`
|
|
7153
|
+
},
|
|
7154
|
+
diagnose: {
|
|
7155
|
+
description: "Run network diagnostics (DNS, TCP, TLS, API, auth)",
|
|
7156
|
+
usage: "okx diagnose [--profile <name>] [--demo]"
|
|
6188
7157
|
}
|
|
6189
7158
|
};
|
|
6190
7159
|
function printGlobalHelp() {
|
|
@@ -6196,6 +7165,7 @@ function printGlobalHelp() {
|
|
|
6196
7165
|
` --profile <name> Use a named profile from ${configFilePath()}`,
|
|
6197
7166
|
" --demo Use simulated trading (demo) mode",
|
|
6198
7167
|
" --json Output raw JSON",
|
|
7168
|
+
" --verbose Show detailed network request/response info (stderr)",
|
|
6199
7169
|
" --version, -v Show version",
|
|
6200
7170
|
" --help Show this help",
|
|
6201
7171
|
"",
|
|
@@ -6406,11 +7376,22 @@ var CLI_OPTIONS = {
|
|
|
6406
7376
|
// batch
|
|
6407
7377
|
action: { type: "string" },
|
|
6408
7378
|
orders: { type: "string" },
|
|
7379
|
+
// earn
|
|
7380
|
+
rate: { type: "string" },
|
|
6409
7381
|
// audit
|
|
6410
7382
|
since: { type: "string" },
|
|
6411
7383
|
tool: { type: "string" },
|
|
6412
7384
|
// config profile
|
|
6413
|
-
force: { type: "boolean", default: false }
|
|
7385
|
+
force: { type: "boolean", default: false },
|
|
7386
|
+
// onchain-earn
|
|
7387
|
+
productId: { type: "string" },
|
|
7388
|
+
protocolType: { type: "string" },
|
|
7389
|
+
term: { type: "string" },
|
|
7390
|
+
tag: { type: "string" },
|
|
7391
|
+
allowEarlyRedeem: { type: "boolean", default: false },
|
|
7392
|
+
state: { type: "string" },
|
|
7393
|
+
// diagnostics
|
|
7394
|
+
verbose: { type: "boolean", default: false }
|
|
6414
7395
|
};
|
|
6415
7396
|
function parseCli(argv) {
|
|
6416
7397
|
const negated = /* @__PURE__ */ new Set();
|
|
@@ -6672,7 +7653,7 @@ async function cmdMarketCandles(run, instId, opts) {
|
|
|
6672
7653
|
// src/commands/account.ts
|
|
6673
7654
|
import * as fs4 from "fs";
|
|
6674
7655
|
import * as path2 from "path";
|
|
6675
|
-
import * as
|
|
7656
|
+
import * as os4 from "os";
|
|
6676
7657
|
function getData2(result) {
|
|
6677
7658
|
return result.data;
|
|
6678
7659
|
}
|
|
@@ -6878,7 +7859,7 @@ function readAuditLogs(logDir, days = 7) {
|
|
|
6878
7859
|
return entries;
|
|
6879
7860
|
}
|
|
6880
7861
|
function cmdAccountAudit(opts) {
|
|
6881
|
-
const logDir = path2.join(
|
|
7862
|
+
const logDir = path2.join(os4.homedir(), ".okx", "logs");
|
|
6882
7863
|
const limit = Math.min(Number(opts.limit) || 20, 100);
|
|
6883
7864
|
let entries = readAuditLogs(logDir);
|
|
6884
7865
|
if (opts.tool) entries = entries.filter((e) => e.tool === opts.tool);
|
|
@@ -6932,7 +7913,11 @@ async function cmdSpotPlace(run, opts) {
|
|
|
6932
7913
|
side: opts.side,
|
|
6933
7914
|
ordType: opts.ordType,
|
|
6934
7915
|
sz: opts.sz,
|
|
6935
|
-
px: opts.px
|
|
7916
|
+
px: opts.px,
|
|
7917
|
+
tpTriggerPx: opts.tpTriggerPx,
|
|
7918
|
+
tpOrdPx: opts.tpOrdPx,
|
|
7919
|
+
slTriggerPx: opts.slTriggerPx,
|
|
7920
|
+
slOrdPx: opts.slOrdPx
|
|
6936
7921
|
});
|
|
6937
7922
|
const data = getData3(result);
|
|
6938
7923
|
if (opts.json) return printJson(data);
|
|
@@ -7157,7 +8142,11 @@ async function cmdSwapPlace(run, opts) {
|
|
|
7157
8142
|
ordType: opts.ordType,
|
|
7158
8143
|
sz: opts.sz,
|
|
7159
8144
|
posSide: opts.posSide,
|
|
7160
|
-
px: opts.px
|
|
8145
|
+
px: opts.px,
|
|
8146
|
+
tpTriggerPx: opts.tpTriggerPx,
|
|
8147
|
+
tpOrdPx: opts.tpOrdPx,
|
|
8148
|
+
slTriggerPx: opts.slTriggerPx,
|
|
8149
|
+
slOrdPx: opts.slOrdPx
|
|
7161
8150
|
});
|
|
7162
8151
|
const data = getData4(result);
|
|
7163
8152
|
if (opts.json) return printJson(data);
|
|
@@ -7459,7 +8448,11 @@ async function cmdFuturesPlace(run, opts) {
|
|
|
7459
8448
|
sz: opts.sz,
|
|
7460
8449
|
posSide: opts.posSide,
|
|
7461
8450
|
px: opts.px,
|
|
7462
|
-
reduceOnly: opts.reduceOnly
|
|
8451
|
+
reduceOnly: opts.reduceOnly,
|
|
8452
|
+
tpTriggerPx: opts.tpTriggerPx,
|
|
8453
|
+
tpOrdPx: opts.tpOrdPx,
|
|
8454
|
+
slTriggerPx: opts.slTriggerPx,
|
|
8455
|
+
slOrdPx: opts.slOrdPx
|
|
7463
8456
|
});
|
|
7464
8457
|
const data = getData5(result);
|
|
7465
8458
|
if (opts.json) return printJson(data);
|
|
@@ -8029,6 +9022,101 @@ function cmdConfigUse(profileName) {
|
|
|
8029
9022
|
`);
|
|
8030
9023
|
}
|
|
8031
9024
|
|
|
9025
|
+
// src/commands/earn.ts
|
|
9026
|
+
function extractData(result) {
|
|
9027
|
+
if (result && typeof result === "object") {
|
|
9028
|
+
const data = result["data"];
|
|
9029
|
+
if (Array.isArray(data)) return data;
|
|
9030
|
+
}
|
|
9031
|
+
return [];
|
|
9032
|
+
}
|
|
9033
|
+
function printDataList(data, json, emptyMsg, mapper) {
|
|
9034
|
+
if (json) {
|
|
9035
|
+
printJson(data);
|
|
9036
|
+
return;
|
|
9037
|
+
}
|
|
9038
|
+
if (!data.length) {
|
|
9039
|
+
process.stdout.write(emptyMsg + "\n");
|
|
9040
|
+
return;
|
|
9041
|
+
}
|
|
9042
|
+
printTable(data.map(mapper));
|
|
9043
|
+
}
|
|
9044
|
+
async function cmdEarnSavingsBalance(run, ccy, json) {
|
|
9045
|
+
const data = extractData(await run("earn_get_savings_balance", { ccy }));
|
|
9046
|
+
printDataList(data, json, "No savings balance", (r) => ({
|
|
9047
|
+
ccy: r["ccy"],
|
|
9048
|
+
amt: r["amt"],
|
|
9049
|
+
earnings: r["earnings"],
|
|
9050
|
+
rate: r["rate"],
|
|
9051
|
+
loanAmt: r["loanAmt"],
|
|
9052
|
+
pendingAmt: r["pendingAmt"]
|
|
9053
|
+
}));
|
|
9054
|
+
}
|
|
9055
|
+
async function cmdEarnSavingsPurchase(run, opts) {
|
|
9056
|
+
const data = extractData(await run("earn_savings_purchase", { ccy: opts.ccy, amt: opts.amt, rate: opts.rate }));
|
|
9057
|
+
if (opts.json) {
|
|
9058
|
+
printJson(data);
|
|
9059
|
+
return;
|
|
9060
|
+
}
|
|
9061
|
+
const r = data[0];
|
|
9062
|
+
if (!r) {
|
|
9063
|
+
process.stdout.write("No response data\n");
|
|
9064
|
+
return;
|
|
9065
|
+
}
|
|
9066
|
+
printKv({ ccy: r["ccy"], amt: r["amt"], side: r["side"], rate: r["rate"] });
|
|
9067
|
+
}
|
|
9068
|
+
async function cmdEarnSavingsRedeem(run, opts) {
|
|
9069
|
+
const data = extractData(await run("earn_savings_redeem", { ccy: opts.ccy, amt: opts.amt }));
|
|
9070
|
+
if (opts.json) {
|
|
9071
|
+
printJson(data);
|
|
9072
|
+
return;
|
|
9073
|
+
}
|
|
9074
|
+
const r = data[0];
|
|
9075
|
+
if (!r) {
|
|
9076
|
+
process.stdout.write("No response data\n");
|
|
9077
|
+
return;
|
|
9078
|
+
}
|
|
9079
|
+
printKv({ ccy: r["ccy"], amt: r["amt"], side: r["side"] });
|
|
9080
|
+
}
|
|
9081
|
+
async function cmdEarnSetLendingRate(run, opts) {
|
|
9082
|
+
const data = extractData(await run("earn_set_lending_rate", { ccy: opts.ccy, rate: opts.rate }));
|
|
9083
|
+
if (opts.json) {
|
|
9084
|
+
printJson(data);
|
|
9085
|
+
return;
|
|
9086
|
+
}
|
|
9087
|
+
const r = data[0];
|
|
9088
|
+
process.stdout.write(`Lending rate set: ${r?.["ccy"]} \u2192 ${r?.["rate"]}
|
|
9089
|
+
`);
|
|
9090
|
+
}
|
|
9091
|
+
async function cmdEarnLendingHistory(run, opts) {
|
|
9092
|
+
const data = extractData(await run("earn_get_lending_history", { ccy: opts.ccy, limit: opts.limit }));
|
|
9093
|
+
printDataList(data, opts.json, "No lending history", (r) => ({
|
|
9094
|
+
ccy: r["ccy"],
|
|
9095
|
+
amt: r["amt"],
|
|
9096
|
+
earnings: r["earnings"],
|
|
9097
|
+
rate: r["rate"],
|
|
9098
|
+
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
9099
|
+
}));
|
|
9100
|
+
}
|
|
9101
|
+
async function cmdEarnLendingRateSummary(run, ccy, json) {
|
|
9102
|
+
const data = extractData(await run("earn_get_lending_rate_summary", { ccy }));
|
|
9103
|
+
printDataList(data, json, "No rate summary data", (r) => ({
|
|
9104
|
+
ccy: r["ccy"],
|
|
9105
|
+
avgRate: r["avgRate"],
|
|
9106
|
+
estRate: r["estRate"],
|
|
9107
|
+
avgAmt: r["avgAmt"]
|
|
9108
|
+
}));
|
|
9109
|
+
}
|
|
9110
|
+
async function cmdEarnLendingRateHistory(run, opts) {
|
|
9111
|
+
const data = extractData(await run("earn_get_lending_rate_history", { ccy: opts.ccy, limit: opts.limit }));
|
|
9112
|
+
printDataList(data, opts.json, "No rate history data", (r) => ({
|
|
9113
|
+
ccy: r["ccy"],
|
|
9114
|
+
lendingRate: r["lendingRate"],
|
|
9115
|
+
rate: r["rate"],
|
|
9116
|
+
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
9117
|
+
}));
|
|
9118
|
+
}
|
|
9119
|
+
|
|
8032
9120
|
// src/commands/bot.ts
|
|
8033
9121
|
function getData7(result) {
|
|
8034
9122
|
return result.data;
|
|
@@ -8260,10 +9348,56 @@ async function cmdDcaSubOrders(run, opts) {
|
|
|
8260
9348
|
);
|
|
8261
9349
|
}
|
|
8262
9350
|
|
|
9351
|
+
// src/commands/onchain-earn.ts
|
|
9352
|
+
function cmdOnchainEarnOffers(run, v) {
|
|
9353
|
+
return run("onchain_earn_get_offers", {
|
|
9354
|
+
productId: v.productId,
|
|
9355
|
+
protocolType: v.protocolType,
|
|
9356
|
+
ccy: v.ccy
|
|
9357
|
+
});
|
|
9358
|
+
}
|
|
9359
|
+
function cmdOnchainEarnPurchase(run, v) {
|
|
9360
|
+
const investData = v.ccy && v.amt ? [{ ccy: v.ccy, amt: v.amt }] : void 0;
|
|
9361
|
+
return run("onchain_earn_purchase", {
|
|
9362
|
+
productId: v.productId,
|
|
9363
|
+
investData,
|
|
9364
|
+
term: v.term,
|
|
9365
|
+
tag: v.tag
|
|
9366
|
+
});
|
|
9367
|
+
}
|
|
9368
|
+
function cmdOnchainEarnRedeem(run, v) {
|
|
9369
|
+
return run("onchain_earn_redeem", {
|
|
9370
|
+
ordId: v.ordId,
|
|
9371
|
+
protocolType: v.protocolType,
|
|
9372
|
+
allowEarlyRedeem: v.allowEarlyRedeem
|
|
9373
|
+
});
|
|
9374
|
+
}
|
|
9375
|
+
function cmdOnchainEarnCancel(run, v) {
|
|
9376
|
+
return run("onchain_earn_cancel", {
|
|
9377
|
+
ordId: v.ordId,
|
|
9378
|
+
protocolType: v.protocolType
|
|
9379
|
+
});
|
|
9380
|
+
}
|
|
9381
|
+
function cmdOnchainEarnActiveOrders(run, v) {
|
|
9382
|
+
return run("onchain_earn_get_active_orders", {
|
|
9383
|
+
productId: v.productId,
|
|
9384
|
+
protocolType: v.protocolType,
|
|
9385
|
+
ccy: v.ccy,
|
|
9386
|
+
state: v.state
|
|
9387
|
+
});
|
|
9388
|
+
}
|
|
9389
|
+
function cmdOnchainEarnOrderHistory(run, v) {
|
|
9390
|
+
return run("onchain_earn_get_order_history", {
|
|
9391
|
+
productId: v.productId,
|
|
9392
|
+
protocolType: v.protocolType,
|
|
9393
|
+
ccy: v.ccy
|
|
9394
|
+
});
|
|
9395
|
+
}
|
|
9396
|
+
|
|
8263
9397
|
// src/index.ts
|
|
8264
|
-
var
|
|
8265
|
-
var
|
|
8266
|
-
var
|
|
9398
|
+
var _require2 = createRequire2(import.meta.url);
|
|
9399
|
+
var CLI_VERSION2 = _require2("../package.json").version;
|
|
9400
|
+
var GIT_HASH2 = true ? "4427916" : "dev";
|
|
8267
9401
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
8268
9402
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
8269
9403
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -8435,6 +9569,10 @@ function handleSpotCommand(run, action, rest, v, json) {
|
|
|
8435
9569
|
ordType: v.ordType,
|
|
8436
9570
|
sz: v.sz,
|
|
8437
9571
|
px: v.px,
|
|
9572
|
+
tpTriggerPx: v.tpTriggerPx,
|
|
9573
|
+
tpOrdPx: v.tpOrdPx,
|
|
9574
|
+
slTriggerPx: v.slTriggerPx,
|
|
9575
|
+
slOrdPx: v.slOrdPx,
|
|
8438
9576
|
json
|
|
8439
9577
|
});
|
|
8440
9578
|
if (action === "cancel")
|
|
@@ -8531,6 +9669,10 @@ function handleSwapCommand(run, action, rest, v, json) {
|
|
|
8531
9669
|
posSide: v.posSide,
|
|
8532
9670
|
px: v.px,
|
|
8533
9671
|
tdMode: v.tdMode ?? "cross",
|
|
9672
|
+
tpTriggerPx: v.tpTriggerPx,
|
|
9673
|
+
tpOrdPx: v.tpOrdPx,
|
|
9674
|
+
slTriggerPx: v.slTriggerPx,
|
|
9675
|
+
slOrdPx: v.slOrdPx,
|
|
8534
9676
|
json
|
|
8535
9677
|
});
|
|
8536
9678
|
if (action === "cancel")
|
|
@@ -8625,6 +9767,10 @@ function handleFuturesCommand(run, action, rest, v, json) {
|
|
|
8625
9767
|
posSide: v.posSide,
|
|
8626
9768
|
px: v.px,
|
|
8627
9769
|
reduceOnly: v.reduceOnly,
|
|
9770
|
+
tpTriggerPx: v.tpTriggerPx,
|
|
9771
|
+
tpOrdPx: v.tpOrdPx,
|
|
9772
|
+
slTriggerPx: v.slTriggerPx,
|
|
9773
|
+
slOrdPx: v.slOrdPx,
|
|
8628
9774
|
json
|
|
8629
9775
|
});
|
|
8630
9776
|
if (action === "cancel")
|
|
@@ -8713,24 +9859,75 @@ function handleBotCommand(run, action, rest, v, json) {
|
|
|
8713
9859
|
if (action === "grid") return handleBotGridCommand(run, v, rest, json);
|
|
8714
9860
|
if (action === "dca") return handleBotDcaCommand(run, rest[0], v, json);
|
|
8715
9861
|
}
|
|
9862
|
+
function handleEarnCommand(run, submodule, rest, v, json) {
|
|
9863
|
+
const action = rest[0];
|
|
9864
|
+
const innerRest = rest.slice(1);
|
|
9865
|
+
if (submodule === "savings") return handleEarnSavingsCommand(run, action, innerRest, v, json);
|
|
9866
|
+
if (submodule === "onchain") return handleEarnOnchainCommand(run, action, v, json);
|
|
9867
|
+
process.stderr.write(`Unknown earn sub-module: ${submodule}
|
|
9868
|
+
Valid: savings, onchain
|
|
9869
|
+
`);
|
|
9870
|
+
process.exitCode = 1;
|
|
9871
|
+
}
|
|
9872
|
+
function handleEarnSavingsCommand(run, action, rest, v, json) {
|
|
9873
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
9874
|
+
if (action === "balance") return cmdEarnSavingsBalance(run, rest[0] ?? v.ccy, json);
|
|
9875
|
+
if (action === "purchase") return cmdEarnSavingsPurchase(run, { ccy: v.ccy, amt: v.amt, rate: v.rate, json });
|
|
9876
|
+
if (action === "redeem") return cmdEarnSavingsRedeem(run, { ccy: v.ccy, amt: v.amt, json });
|
|
9877
|
+
if (action === "set-rate") return cmdEarnSetLendingRate(run, { ccy: v.ccy, rate: v.rate, json });
|
|
9878
|
+
if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
|
|
9879
|
+
if (action === "rate-summary") return cmdEarnLendingRateSummary(run, rest[0] ?? v.ccy, json);
|
|
9880
|
+
if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
|
|
9881
|
+
process.stderr.write(`Unknown earn savings command: ${action}
|
|
9882
|
+
`);
|
|
9883
|
+
process.exitCode = 1;
|
|
9884
|
+
}
|
|
9885
|
+
function handleEarnOnchainCommand(run, action, v, json) {
|
|
9886
|
+
if (action === "offers") return cmdOnchainEarnOffers(run, v).then((r) => outputResult(r, json));
|
|
9887
|
+
if (action === "purchase") return cmdOnchainEarnPurchase(run, v).then((r) => outputResult(r, json));
|
|
9888
|
+
if (action === "redeem") return cmdOnchainEarnRedeem(run, v).then((r) => outputResult(r, json));
|
|
9889
|
+
if (action === "cancel") return cmdOnchainEarnCancel(run, v).then((r) => outputResult(r, json));
|
|
9890
|
+
if (action === "orders") return cmdOnchainEarnActiveOrders(run, v).then((r) => outputResult(r, json));
|
|
9891
|
+
if (action === "history") return cmdOnchainEarnOrderHistory(run, v).then((r) => outputResult(r, json));
|
|
9892
|
+
process.stderr.write(`Unknown earn onchain command: ${action}
|
|
9893
|
+
`);
|
|
9894
|
+
process.exitCode = 1;
|
|
9895
|
+
}
|
|
9896
|
+
function outputResult(result, json) {
|
|
9897
|
+
if (json) {
|
|
9898
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
9899
|
+
} else {
|
|
9900
|
+
process.stdout.write(JSON.stringify(result.data, null, 2) + "\n");
|
|
9901
|
+
}
|
|
9902
|
+
}
|
|
9903
|
+
function printHelpForLevel(positionals) {
|
|
9904
|
+
const [module, subgroup] = positionals;
|
|
9905
|
+
if (!module) printHelp();
|
|
9906
|
+
else if (!subgroup) printHelp(module);
|
|
9907
|
+
else printHelp(module, subgroup);
|
|
9908
|
+
}
|
|
9909
|
+
function printVerboseConfigSummary(config, profile) {
|
|
9910
|
+
let authLabel = "\u2717";
|
|
9911
|
+
if (config.hasAuth && config.apiKey) {
|
|
9912
|
+
authLabel = `\u2713(${config.apiKey.slice(0, 3)}***${config.apiKey.slice(-3)})`;
|
|
9913
|
+
} else if (config.hasAuth) {
|
|
9914
|
+
authLabel = "\u2713";
|
|
9915
|
+
}
|
|
9916
|
+
process.stderr.write(
|
|
9917
|
+
`[verbose] config: profile=${profile ?? "default"} site=${config.site} base=${config.baseUrl} auth=${authLabel} demo=${config.demo ? "on" : "off"} modules=${config.modules.join(",")}
|
|
9918
|
+
`
|
|
9919
|
+
);
|
|
9920
|
+
}
|
|
8716
9921
|
async function main() {
|
|
8717
|
-
|
|
8718
|
-
checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION);
|
|
9922
|
+
checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION2);
|
|
8719
9923
|
const { values, positionals } = parseCli(process.argv.slice(2));
|
|
8720
9924
|
if (values.version) {
|
|
8721
|
-
process.stdout.write(`${
|
|
9925
|
+
process.stdout.write(`${CLI_VERSION2} (${GIT_HASH2})
|
|
8722
9926
|
`);
|
|
8723
9927
|
return;
|
|
8724
9928
|
}
|
|
8725
9929
|
if (values.help || positionals.length === 0) {
|
|
8726
|
-
|
|
8727
|
-
if (!module2) {
|
|
8728
|
-
printHelp();
|
|
8729
|
-
} else if (!subgroup) {
|
|
8730
|
-
printHelp(module2);
|
|
8731
|
-
} else {
|
|
8732
|
-
printHelp(module2, subgroup);
|
|
8733
|
-
}
|
|
9930
|
+
printHelpForLevel(positionals);
|
|
8734
9931
|
return;
|
|
8735
9932
|
}
|
|
8736
9933
|
const [module, action, ...rest] = positionals;
|
|
@@ -8738,16 +9935,23 @@ async function main() {
|
|
|
8738
9935
|
const json = v.json ?? false;
|
|
8739
9936
|
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
8740
9937
|
if (module === "setup") return handleSetupCommand(v);
|
|
8741
|
-
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, userAgent: `okx-trade-cli/${
|
|
9938
|
+
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
9939
|
+
if (config.verbose) printVerboseConfigSummary(config, v.profile);
|
|
9940
|
+
if (module === "diagnose") return cmdDiagnose(config, v.profile ?? "default");
|
|
8742
9941
|
const client = new OkxRestClient(config);
|
|
8743
9942
|
const run = createToolRunner(client, config);
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
9943
|
+
const moduleHandlers = {
|
|
9944
|
+
market: () => handleMarketCommand(run, action, rest, v, json),
|
|
9945
|
+
account: () => handleAccountCommand(run, action, rest, v, json),
|
|
9946
|
+
spot: () => handleSpotCommand(run, action, rest, v, json),
|
|
9947
|
+
swap: () => handleSwapCommand(run, action, rest, v, json),
|
|
9948
|
+
futures: () => handleFuturesCommand(run, action, rest, v, json),
|
|
9949
|
+
option: () => handleOptionCommand(run, action, rest, v, json),
|
|
9950
|
+
bot: () => handleBotCommand(run, action, rest, v, json),
|
|
9951
|
+
earn: () => handleEarnCommand(run, action, rest, v, json)
|
|
9952
|
+
};
|
|
9953
|
+
const handler = moduleHandlers[module];
|
|
9954
|
+
if (handler) return handler();
|
|
8751
9955
|
process.stderr.write(`Unknown command: ${module} ${action ?? ""}
|
|
8752
9956
|
`);
|
|
8753
9957
|
process.exitCode = 1;
|
|
@@ -8760,7 +9964,7 @@ main().catch((error) => {
|
|
|
8760
9964
|
`);
|
|
8761
9965
|
if (payload.suggestion) process.stderr.write(`Hint: ${payload.suggestion}
|
|
8762
9966
|
`);
|
|
8763
|
-
process.stderr.write(`Version: @okx_ai/okx-trade-cli@${
|
|
9967
|
+
process.stderr.write(`Version: @okx_ai/okx-trade-cli@${CLI_VERSION2}
|
|
8764
9968
|
`);
|
|
8765
9969
|
process.exitCode = 1;
|
|
8766
9970
|
});
|
|
@@ -8770,6 +9974,7 @@ export {
|
|
|
8770
9974
|
handleBotDcaCommand,
|
|
8771
9975
|
handleBotGridCommand,
|
|
8772
9976
|
handleConfigCommand,
|
|
9977
|
+
handleEarnCommand,
|
|
8773
9978
|
handleMarketCommand,
|
|
8774
9979
|
handleMarketDataCommand,
|
|
8775
9980
|
handleMarketPublicCommand,
|