@okx_ai/okx-trade-cli 1.2.7 → 1.2.8-beta.2
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 +297 -225
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1671,7 +1671,7 @@ function registerAccountTools() {
|
|
|
1671
1671
|
{
|
|
1672
1672
|
name: "account_get_asset_balance",
|
|
1673
1673
|
module: "account",
|
|
1674
|
-
description: "Get funding account balance (asset account). Different from account_get_balance which queries the trading account.",
|
|
1674
|
+
description: "Get funding account balance (asset account). Different from account_get_balance which queries the trading account. Optionally includes total asset valuation across all account types (trading, funding, earn, etc.).",
|
|
1675
1675
|
isWrite: false,
|
|
1676
1676
|
inputSchema: {
|
|
1677
1677
|
type: "object",
|
|
@@ -1679,17 +1679,41 @@ function registerAccountTools() {
|
|
|
1679
1679
|
ccy: {
|
|
1680
1680
|
type: "string",
|
|
1681
1681
|
description: "e.g. BTC or BTC,ETH. Omit for all."
|
|
1682
|
+
},
|
|
1683
|
+
showValuation: {
|
|
1684
|
+
type: "boolean",
|
|
1685
|
+
description: "Include total asset valuation breakdown by account type (trading/funding/earn). Default false."
|
|
1682
1686
|
}
|
|
1683
1687
|
}
|
|
1684
1688
|
},
|
|
1685
1689
|
handler: async (rawArgs, context) => {
|
|
1686
1690
|
const args = asRecord(rawArgs);
|
|
1687
|
-
const
|
|
1691
|
+
const ccy = readString(args, "ccy");
|
|
1692
|
+
const showValuation = readBoolean(args, "showValuation");
|
|
1693
|
+
if (showValuation) {
|
|
1694
|
+
const balanceResp2 = await context.client.privateGet(
|
|
1695
|
+
"/api/v5/asset/balances",
|
|
1696
|
+
compactObject({ ccy }),
|
|
1697
|
+
privateRateLimit("account_get_asset_balance", 6)
|
|
1698
|
+
);
|
|
1699
|
+
let valuationData = null;
|
|
1700
|
+
try {
|
|
1701
|
+
const valuationResp = await context.client.privateGet(
|
|
1702
|
+
"/api/v5/asset/asset-valuation",
|
|
1703
|
+
{},
|
|
1704
|
+
privateRateLimit("account_get_asset_valuation", 1)
|
|
1705
|
+
);
|
|
1706
|
+
valuationData = valuationResp.data;
|
|
1707
|
+
} catch {
|
|
1708
|
+
}
|
|
1709
|
+
return { ...normalizeResponse(balanceResp2), valuation: valuationData };
|
|
1710
|
+
}
|
|
1711
|
+
const balanceResp = await context.client.privateGet(
|
|
1688
1712
|
"/api/v5/asset/balances",
|
|
1689
|
-
compactObject({ ccy
|
|
1713
|
+
compactObject({ ccy }),
|
|
1690
1714
|
privateRateLimit("account_get_asset_balance", 6)
|
|
1691
1715
|
);
|
|
1692
|
-
return normalizeResponse(
|
|
1716
|
+
return normalizeResponse(balanceResp);
|
|
1693
1717
|
}
|
|
1694
1718
|
},
|
|
1695
1719
|
{
|
|
@@ -3104,6 +3128,39 @@ function registerGridTools() {
|
|
|
3104
3128
|
];
|
|
3105
3129
|
}
|
|
3106
3130
|
var BASE = "/api/v5/tradingBot/dca";
|
|
3131
|
+
function buildTriggerParam(args, algoOrdType) {
|
|
3132
|
+
const triggerStrategy = readString(args, "triggerStrategy") ?? "instant";
|
|
3133
|
+
if (triggerStrategy === "price" && algoOrdType === "spot_dca") {
|
|
3134
|
+
throw new OkxApiError(
|
|
3135
|
+
"triggerStrategy 'price' is only supported for contract_dca. spot_dca supports: instant, rsi",
|
|
3136
|
+
{ code: "VALIDATION", endpoint: `${BASE}/create` }
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
3139
|
+
const param = { triggerAction: "start", triggerStrategy };
|
|
3140
|
+
if (triggerStrategy === "price") {
|
|
3141
|
+
param["triggerPx"] = requireString(args, "triggerPx");
|
|
3142
|
+
const triggerCond = readString(args, "triggerCond");
|
|
3143
|
+
if (triggerCond) param["triggerCond"] = triggerCond;
|
|
3144
|
+
} else if (triggerStrategy === "rsi") {
|
|
3145
|
+
param["triggerCond"] = requireString(args, "triggerCond");
|
|
3146
|
+
param["thold"] = requireString(args, "thold");
|
|
3147
|
+
param["timeframe"] = requireString(args, "timeframe");
|
|
3148
|
+
param["timePeriod"] = readString(args, "timePeriod") ?? "14";
|
|
3149
|
+
}
|
|
3150
|
+
return param;
|
|
3151
|
+
}
|
|
3152
|
+
function validateSafetyOrderParams(args, maxSafetyOrds) {
|
|
3153
|
+
if (Number(maxSafetyOrds) <= 0) return;
|
|
3154
|
+
const required = ["safetyOrdAmt", "pxSteps", "pxStepsMult", "volMult"];
|
|
3155
|
+
for (const field of required) {
|
|
3156
|
+
if (!readString(args, field)) {
|
|
3157
|
+
throw new OkxApiError(`${field} is required when maxSafetyOrds > 0`, {
|
|
3158
|
+
code: "VALIDATION",
|
|
3159
|
+
endpoint: `${BASE}/create`
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3107
3164
|
function normalizeWrite2(response) {
|
|
3108
3165
|
const data = response.data;
|
|
3109
3166
|
if (Array.isArray(data) && data.length > 0) {
|
|
@@ -3173,57 +3230,11 @@ function registerDcaTools() {
|
|
|
3173
3230
|
endpoint: `${BASE}/create`
|
|
3174
3231
|
});
|
|
3175
3232
|
}
|
|
3176
|
-
const
|
|
3177
|
-
if (triggerStrategy === "price" && algoOrdType === "spot_dca") {
|
|
3178
|
-
throw new OkxApiError("triggerStrategy 'price' is only supported for contract_dca. spot_dca supports: instant, rsi", {
|
|
3179
|
-
code: "VALIDATION",
|
|
3180
|
-
endpoint: `${BASE}/create`
|
|
3181
|
-
});
|
|
3182
|
-
}
|
|
3183
|
-
const triggerParam = {
|
|
3184
|
-
triggerAction: "start",
|
|
3185
|
-
triggerStrategy
|
|
3186
|
-
};
|
|
3187
|
-
if (triggerStrategy === "price") {
|
|
3188
|
-
triggerParam["triggerPx"] = requireString(args, "triggerPx");
|
|
3189
|
-
const triggerCond = readString(args, "triggerCond");
|
|
3190
|
-
if (triggerCond) {
|
|
3191
|
-
triggerParam["triggerCond"] = triggerCond;
|
|
3192
|
-
}
|
|
3193
|
-
} else if (triggerStrategy === "rsi") {
|
|
3194
|
-
triggerParam["triggerCond"] = requireString(args, "triggerCond");
|
|
3195
|
-
triggerParam["thold"] = requireString(args, "thold");
|
|
3196
|
-
triggerParam["timeframe"] = requireString(args, "timeframe");
|
|
3197
|
-
const timePeriod = readString(args, "timePeriod");
|
|
3198
|
-
triggerParam["timePeriod"] = timePeriod ?? "14";
|
|
3199
|
-
}
|
|
3233
|
+
const triggerParam = buildTriggerParam(args, algoOrdType);
|
|
3200
3234
|
const maxSafetyOrds = requireString(args, "maxSafetyOrds");
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
code: "VALIDATION",
|
|
3205
|
-
endpoint: `${BASE}/create`
|
|
3206
|
-
});
|
|
3207
|
-
}
|
|
3208
|
-
if (!readString(args, "pxSteps")) {
|
|
3209
|
-
throw new OkxApiError("pxSteps is required when maxSafetyOrds > 0", {
|
|
3210
|
-
code: "VALIDATION",
|
|
3211
|
-
endpoint: `${BASE}/create`
|
|
3212
|
-
});
|
|
3213
|
-
}
|
|
3214
|
-
if (!readString(args, "pxStepsMult")) {
|
|
3215
|
-
throw new OkxApiError("pxStepsMult is required when maxSafetyOrds > 0", {
|
|
3216
|
-
code: "VALIDATION",
|
|
3217
|
-
endpoint: `${BASE}/create`
|
|
3218
|
-
});
|
|
3219
|
-
}
|
|
3220
|
-
if (!readString(args, "volMult")) {
|
|
3221
|
-
throw new OkxApiError("volMult is required when maxSafetyOrds > 0", {
|
|
3222
|
-
code: "VALIDATION",
|
|
3223
|
-
endpoint: `${BASE}/create`
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
|
-
}
|
|
3235
|
+
validateSafetyOrderParams(args, maxSafetyOrds);
|
|
3236
|
+
const allowReinvestRaw = args["allowReinvest"];
|
|
3237
|
+
const allowReinvest = allowReinvestRaw !== void 0 ? allowReinvestRaw === true || allowReinvestRaw === "true" : void 0;
|
|
3227
3238
|
const response = await context.client.privatePost(
|
|
3228
3239
|
`${BASE}/create`,
|
|
3229
3240
|
compactObject({
|
|
@@ -3240,7 +3251,7 @@ function registerDcaTools() {
|
|
|
3240
3251
|
tpPct: requireString(args, "tpPct"),
|
|
3241
3252
|
slPct: readString(args, "slPct"),
|
|
3242
3253
|
slMode: readString(args, "slMode"),
|
|
3243
|
-
allowReinvest
|
|
3254
|
+
allowReinvest,
|
|
3244
3255
|
triggerParams: [triggerParam],
|
|
3245
3256
|
tag: context.config.sourceTag,
|
|
3246
3257
|
algoClOrdId: readString(args, "algoClOrdId"),
|
|
@@ -4800,6 +4811,7 @@ function registerFuturesTools() {
|
|
|
4800
4811
|
}
|
|
4801
4812
|
];
|
|
4802
4813
|
}
|
|
4814
|
+
var TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
4803
4815
|
function registerMarketTools() {
|
|
4804
4816
|
return [
|
|
4805
4817
|
{
|
|
@@ -4899,7 +4911,7 @@ function registerMarketTools() {
|
|
|
4899
4911
|
{
|
|
4900
4912
|
name: "market_get_candles",
|
|
4901
4913
|
module: "market",
|
|
4902
|
-
description: "Get candlestick (OHLCV) data for an instrument.
|
|
4914
|
+
description: "Get candlestick (OHLCV) data for an instrument. Automatically retrieves historical data (back to 2021) when requesting older time ranges. Use the `after` parameter to paginate back in time (the old `history` parameter has been removed). IMPORTANT: Before fetching with `after`/`before`, estimate the number of candles: time_range_ms / bar_interval_ms. If the estimate exceeds ~500 candles, inform the user of the estimated count and ask for confirmation before proceeding.",
|
|
4903
4915
|
isWrite: false,
|
|
4904
4916
|
inputSchema: {
|
|
4905
4917
|
type: "object",
|
|
@@ -4924,29 +4936,29 @@ function registerMarketTools() {
|
|
|
4924
4936
|
limit: {
|
|
4925
4937
|
type: "number",
|
|
4926
4938
|
description: "Max results (default 100)"
|
|
4927
|
-
},
|
|
4928
|
-
history: {
|
|
4929
|
-
type: "boolean",
|
|
4930
|
-
description: "true=older historical data beyond recent window"
|
|
4931
4939
|
}
|
|
4932
4940
|
},
|
|
4933
4941
|
required: ["instId"]
|
|
4934
4942
|
},
|
|
4935
4943
|
handler: async (rawArgs, context) => {
|
|
4936
4944
|
const args = asRecord(rawArgs);
|
|
4937
|
-
const
|
|
4938
|
-
const
|
|
4939
|
-
const
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4945
|
+
const afterTs = readString(args, "after");
|
|
4946
|
+
const beforeTs = readString(args, "before");
|
|
4947
|
+
const query = compactObject({
|
|
4948
|
+
instId: requireString(args, "instId"),
|
|
4949
|
+
bar: readString(args, "bar"),
|
|
4950
|
+
after: afterTs,
|
|
4951
|
+
before: beforeTs,
|
|
4952
|
+
limit: readNumber(args, "limit")
|
|
4953
|
+
});
|
|
4954
|
+
const rateLimit = publicRateLimit("market_get_candles", 40);
|
|
4955
|
+
const hasTimestamp = afterTs !== void 0 || beforeTs !== void 0;
|
|
4956
|
+
const useHistory = afterTs !== void 0 && Number(afterTs) < Date.now() - TWO_DAYS_MS;
|
|
4957
|
+
const path42 = useHistory ? "/api/v5/market/history-candles" : "/api/v5/market/candles";
|
|
4958
|
+
const response = await context.client.publicGet(path42, query, rateLimit);
|
|
4959
|
+
if (!useHistory && hasTimestamp && Array.isArray(response.data) && response.data.length === 0) {
|
|
4960
|
+
return normalizeResponse(await context.client.publicGet("/api/v5/market/history-candles", query, rateLimit));
|
|
4961
|
+
}
|
|
4950
4962
|
return normalizeResponse(response);
|
|
4951
4963
|
}
|
|
4952
4964
|
},
|
|
@@ -5799,7 +5811,14 @@ function registerOptionTools() {
|
|
|
5799
5811
|
handler: async (rawArgs, context) => {
|
|
5800
5812
|
const args = asRecord(rawArgs);
|
|
5801
5813
|
const status = readString(args, "status") ?? "live";
|
|
5802
|
-
|
|
5814
|
+
let path42;
|
|
5815
|
+
if (status === "archive") {
|
|
5816
|
+
path42 = "/api/v5/trade/orders-history-archive";
|
|
5817
|
+
} else if (status === "history") {
|
|
5818
|
+
path42 = "/api/v5/trade/orders-history";
|
|
5819
|
+
} else {
|
|
5820
|
+
path42 = "/api/v5/trade/orders-pending";
|
|
5821
|
+
}
|
|
5803
5822
|
const response = await context.client.privateGet(
|
|
5804
5823
|
path42,
|
|
5805
5824
|
compactObject({
|
|
@@ -7430,58 +7449,60 @@ function checkClaudeCodeConfig() {
|
|
|
7430
7449
|
if (anyParseError) return "parse-error";
|
|
7431
7450
|
return "not-configured";
|
|
7432
7451
|
}
|
|
7452
|
+
function handleJsonClient(clientId, report, configuredClients) {
|
|
7453
|
+
const configPath = getConfigPath(clientId);
|
|
7454
|
+
if (!configPath) return false;
|
|
7455
|
+
const name = CLIENT_NAMES[clientId];
|
|
7456
|
+
const status = checkJsonMcpConfig(configPath);
|
|
7457
|
+
if (status === "missing") return false;
|
|
7458
|
+
if (status === "found") {
|
|
7459
|
+
ok(name, `configured (${sanitize(configPath)})`);
|
|
7460
|
+
report.add(`client_${clientId}`, `OK ${sanitize(configPath)}`);
|
|
7461
|
+
configuredClients.push(clientId);
|
|
7462
|
+
return false;
|
|
7463
|
+
}
|
|
7464
|
+
if (status === "not-configured") {
|
|
7465
|
+
fail(name, "okx-trade-mcp not found in mcpServers", [`Run: okx setup --client ${clientId}`]);
|
|
7466
|
+
report.add(`client_${clientId}`, "NOT_CONFIGURED");
|
|
7467
|
+
} else {
|
|
7468
|
+
fail(name, `JSON parse error in ${sanitize(configPath)}`, [
|
|
7469
|
+
`Check ${sanitize(configPath)} for JSON syntax errors`,
|
|
7470
|
+
`Then run: okx setup --client ${clientId}`
|
|
7471
|
+
]);
|
|
7472
|
+
report.add(`client_${clientId}`, "PARSE_ERROR");
|
|
7473
|
+
}
|
|
7474
|
+
return true;
|
|
7475
|
+
}
|
|
7476
|
+
function handleClaudeCodeClient(report, configuredClients) {
|
|
7477
|
+
const status = checkClaudeCodeConfig();
|
|
7478
|
+
if (status === "missing") return false;
|
|
7479
|
+
const name = CLIENT_NAMES["claude-code"];
|
|
7480
|
+
if (status === "found") {
|
|
7481
|
+
ok(name, "configured");
|
|
7482
|
+
report.add("client_claude-code", "OK");
|
|
7483
|
+
configuredClients.push("claude-code");
|
|
7484
|
+
return false;
|
|
7485
|
+
}
|
|
7486
|
+
if (status === "not-configured") {
|
|
7487
|
+
warn(name, "installed but okx-trade-mcp not configured", [
|
|
7488
|
+
"Run: okx setup --client claude-code"
|
|
7489
|
+
]);
|
|
7490
|
+
report.add("client_claude-code", "NOT_CONFIGURED");
|
|
7491
|
+
return false;
|
|
7492
|
+
}
|
|
7493
|
+
fail(name, "settings file has JSON parse error", ["Run: okx setup --client claude-code"]);
|
|
7494
|
+
report.add("client_claude-code", "PARSE_ERROR");
|
|
7495
|
+
return true;
|
|
7496
|
+
}
|
|
7433
7497
|
function checkMcpClients(report) {
|
|
7434
7498
|
section("MCP Client Config");
|
|
7435
7499
|
const jsonClients = ["claude-desktop", "cursor", "windsurf"];
|
|
7436
7500
|
const configuredClients = [];
|
|
7437
7501
|
let anyFailed = false;
|
|
7438
7502
|
for (const clientId of jsonClients) {
|
|
7439
|
-
|
|
7440
|
-
if (!configPath) continue;
|
|
7441
|
-
const name = CLIENT_NAMES[clientId];
|
|
7442
|
-
const status = checkJsonMcpConfig(configPath);
|
|
7443
|
-
if (status === "missing") {
|
|
7444
|
-
continue;
|
|
7445
|
-
}
|
|
7446
|
-
if (status === "found") {
|
|
7447
|
-
ok(name, `configured (${sanitize(configPath)})`);
|
|
7448
|
-
report.add(`client_${clientId}`, `OK ${sanitize(configPath)}`);
|
|
7449
|
-
configuredClients.push(clientId);
|
|
7450
|
-
} else if (status === "not-configured") {
|
|
7451
|
-
fail(name, "okx-trade-mcp not found in mcpServers", [
|
|
7452
|
-
`Run: okx setup --client ${clientId}`
|
|
7453
|
-
]);
|
|
7454
|
-
report.add(`client_${clientId}`, "NOT_CONFIGURED");
|
|
7455
|
-
anyFailed = true;
|
|
7456
|
-
} else {
|
|
7457
|
-
fail(name, `JSON parse error in ${sanitize(configPath)}`, [
|
|
7458
|
-
`Check ${sanitize(configPath)} for JSON syntax errors`,
|
|
7459
|
-
`Then run: okx setup --client ${clientId}`
|
|
7460
|
-
]);
|
|
7461
|
-
report.add(`client_${clientId}`, "PARSE_ERROR");
|
|
7462
|
-
anyFailed = true;
|
|
7463
|
-
}
|
|
7464
|
-
}
|
|
7465
|
-
const claudeCodeStatus = checkClaudeCodeConfig();
|
|
7466
|
-
if (claudeCodeStatus !== "missing") {
|
|
7467
|
-
const name = CLIENT_NAMES["claude-code"];
|
|
7468
|
-
if (claudeCodeStatus === "found") {
|
|
7469
|
-
ok(name, "configured");
|
|
7470
|
-
report.add("client_claude-code", "OK");
|
|
7471
|
-
configuredClients.push("claude-code");
|
|
7472
|
-
} else if (claudeCodeStatus === "not-configured") {
|
|
7473
|
-
warn(name, "installed but okx-trade-mcp not configured", [
|
|
7474
|
-
"Run: okx setup --client claude-code"
|
|
7475
|
-
]);
|
|
7476
|
-
report.add("client_claude-code", "NOT_CONFIGURED");
|
|
7477
|
-
} else {
|
|
7478
|
-
fail(name, "settings file has JSON parse error", [
|
|
7479
|
-
"Run: okx setup --client claude-code"
|
|
7480
|
-
]);
|
|
7481
|
-
report.add("client_claude-code", "PARSE_ERROR");
|
|
7482
|
-
anyFailed = true;
|
|
7483
|
-
}
|
|
7503
|
+
if (handleJsonClient(clientId, report, configuredClients)) anyFailed = true;
|
|
7484
7504
|
}
|
|
7505
|
+
if (handleClaudeCodeClient(report, configuredClients)) anyFailed = true;
|
|
7485
7506
|
if (configuredClients.length === 0 && !anyFailed) {
|
|
7486
7507
|
fail("no client", "no MCP client configuration found", [
|
|
7487
7508
|
"Run: okx setup --client <client>",
|
|
@@ -7755,7 +7776,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
7755
7776
|
|
|
7756
7777
|
// src/commands/diagnose.ts
|
|
7757
7778
|
var CLI_VERSION = readCliVersion();
|
|
7758
|
-
var GIT_HASH = true ? "
|
|
7779
|
+
var GIT_HASH = true ? "f9ea608" : "dev";
|
|
7759
7780
|
function maskKey2(key) {
|
|
7760
7781
|
if (!key) return "(not set)";
|
|
7761
7782
|
if (key.length <= 8) return "****";
|
|
@@ -8676,6 +8697,45 @@ function printGlobalHelp() {
|
|
|
8676
8697
|
lines.push("", 'Run "okx <module> --help" for module details.', "");
|
|
8677
8698
|
output(lines.join(EOL2));
|
|
8678
8699
|
}
|
|
8700
|
+
function printSubgroupOnlyModule(lines, moduleName, group) {
|
|
8701
|
+
const subgroupNames = Object.keys(group.subgroups);
|
|
8702
|
+
const colWidth = Math.max(...subgroupNames.map((n) => n.length)) + 4;
|
|
8703
|
+
lines.push(`Usage: okx ${moduleName} <strategy> <action> [args...]`);
|
|
8704
|
+
lines.push("", `${group.description}.`, "");
|
|
8705
|
+
lines.push("Strategies:");
|
|
8706
|
+
for (const [sgName, sg] of Object.entries(group.subgroups)) {
|
|
8707
|
+
lines.push(` ${sgName.padEnd(colWidth)}${sg.description}`);
|
|
8708
|
+
}
|
|
8709
|
+
lines.push("", `Run "okx ${moduleName} <strategy> --help" for details.`);
|
|
8710
|
+
}
|
|
8711
|
+
function printMixedModule(lines, moduleName, group) {
|
|
8712
|
+
lines.push(`Usage: okx ${moduleName} <action> [args...]`);
|
|
8713
|
+
lines.push("", `${group.description}.`, "", "Commands:");
|
|
8714
|
+
printCommandList(lines, group.commands);
|
|
8715
|
+
lines.push("", "Subgroups:");
|
|
8716
|
+
const subgroupEntries = Object.entries(group.subgroups);
|
|
8717
|
+
const colWidth = Math.max(...subgroupEntries.map(([n]) => n.length)) + 4;
|
|
8718
|
+
for (const [sgName, sg] of subgroupEntries) {
|
|
8719
|
+
lines.push(` ${sgName.padEnd(colWidth)}${sg.description}`);
|
|
8720
|
+
}
|
|
8721
|
+
lines.push("", `Run "okx ${moduleName} <subgroup> --help" for subgroup details.`);
|
|
8722
|
+
}
|
|
8723
|
+
function printCommandsOnlyModule(lines, moduleName, group) {
|
|
8724
|
+
lines.push(`Usage: okx ${moduleName} <action> [args...]`);
|
|
8725
|
+
lines.push("", `${group.description}.`, "", "Commands:");
|
|
8726
|
+
printCommandList(lines, group.commands);
|
|
8727
|
+
}
|
|
8728
|
+
function printUsageModule(lines, group) {
|
|
8729
|
+
lines.push(`Usage: ${group.usage}`);
|
|
8730
|
+
lines.push("", `${group.description}.`);
|
|
8731
|
+
if (group.commands) {
|
|
8732
|
+
lines.push("");
|
|
8733
|
+
for (const cmd of Object.values(group.commands)) {
|
|
8734
|
+
lines.push(` ${cmd.description}`);
|
|
8735
|
+
lines.push(` Usage: ${cmd.usage}`);
|
|
8736
|
+
}
|
|
8737
|
+
}
|
|
8738
|
+
}
|
|
8679
8739
|
function printModuleHelp(moduleName) {
|
|
8680
8740
|
const group = HELP_TREE[moduleName];
|
|
8681
8741
|
if (!group) {
|
|
@@ -8687,40 +8747,13 @@ function printModuleHelp(moduleName) {
|
|
|
8687
8747
|
const hasCommands = group.commands && Object.keys(group.commands).length > 0;
|
|
8688
8748
|
const lines = [""];
|
|
8689
8749
|
if (hasSubgroups && !hasCommands) {
|
|
8690
|
-
|
|
8691
|
-
lines.push(`Usage: okx ${moduleName} <strategy> <action> [args...]`);
|
|
8692
|
-
lines.push("", `${group.description}.`, "");
|
|
8693
|
-
lines.push("Strategies:");
|
|
8694
|
-
const colWidth = Math.max(...subgroupNames.map((n) => n.length)) + 4;
|
|
8695
|
-
for (const [sgName, sg] of Object.entries(group.subgroups)) {
|
|
8696
|
-
lines.push(` ${sgName.padEnd(colWidth)}${sg.description}`);
|
|
8697
|
-
}
|
|
8698
|
-
lines.push("", `Run "okx ${moduleName} <strategy> --help" for details.`);
|
|
8750
|
+
printSubgroupOnlyModule(lines, moduleName, group);
|
|
8699
8751
|
} else if (hasSubgroups && hasCommands) {
|
|
8700
|
-
lines
|
|
8701
|
-
lines.push("", `${group.description}.`, "", "Commands:");
|
|
8702
|
-
printCommandList(lines, group.commands);
|
|
8703
|
-
lines.push("", "Subgroups:");
|
|
8704
|
-
const subgroupEntries = Object.entries(group.subgroups);
|
|
8705
|
-
const colWidth = Math.max(...subgroupEntries.map(([n]) => n.length)) + 4;
|
|
8706
|
-
for (const [sgName, sg] of subgroupEntries) {
|
|
8707
|
-
lines.push(` ${sgName.padEnd(colWidth)}${sg.description}`);
|
|
8708
|
-
}
|
|
8709
|
-
lines.push("", `Run "okx ${moduleName} <subgroup> --help" for subgroup details.`);
|
|
8752
|
+
printMixedModule(lines, moduleName, group);
|
|
8710
8753
|
} else if (hasCommands) {
|
|
8711
|
-
lines
|
|
8712
|
-
lines.push("", `${group.description}.`, "", "Commands:");
|
|
8713
|
-
printCommandList(lines, group.commands);
|
|
8754
|
+
printCommandsOnlyModule(lines, moduleName, group);
|
|
8714
8755
|
} else if (group.usage) {
|
|
8715
|
-
lines
|
|
8716
|
-
lines.push("", `${group.description}.`);
|
|
8717
|
-
if (group.commands) {
|
|
8718
|
-
lines.push("");
|
|
8719
|
-
for (const cmd of Object.values(group.commands)) {
|
|
8720
|
-
lines.push(` ${cmd.description}`);
|
|
8721
|
-
lines.push(` Usage: ${cmd.usage}`);
|
|
8722
|
-
}
|
|
8723
|
-
}
|
|
8756
|
+
printUsageModule(lines, group);
|
|
8724
8757
|
}
|
|
8725
8758
|
lines.push("");
|
|
8726
8759
|
output(lines.join(EOL2));
|
|
@@ -8791,6 +8824,8 @@ var CLI_OPTIONS = {
|
|
|
8791
8824
|
bar: { type: "string" },
|
|
8792
8825
|
limit: { type: "string" },
|
|
8793
8826
|
sz: { type: "string" },
|
|
8827
|
+
after: { type: "string" },
|
|
8828
|
+
before: { type: "string" },
|
|
8794
8829
|
// orders
|
|
8795
8830
|
instId: { type: "string" },
|
|
8796
8831
|
history: { type: "boolean", default: false },
|
|
@@ -8842,6 +8877,7 @@ var CLI_OPTIONS = {
|
|
|
8842
8877
|
quoteCcy: { type: "string" },
|
|
8843
8878
|
// account extras
|
|
8844
8879
|
archive: { type: "boolean", default: false },
|
|
8880
|
+
valuation: { type: "boolean", default: false },
|
|
8845
8881
|
posMode: { type: "string" },
|
|
8846
8882
|
ccy: { type: "string" },
|
|
8847
8883
|
from: { type: "string" },
|
|
@@ -9136,7 +9172,7 @@ async function cmdMarketOrderbook(run, instId, sz, json) {
|
|
|
9136
9172
|
for (const [p, s] of bids) outputLine(` ${p.padStart(16)} ${s}`);
|
|
9137
9173
|
}
|
|
9138
9174
|
async function cmdMarketCandles(run, instId, opts) {
|
|
9139
|
-
const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit });
|
|
9175
|
+
const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit, after: opts.after, before: opts.before });
|
|
9140
9176
|
const candles = getData(result);
|
|
9141
9177
|
if (opts.json) return printJson(candles);
|
|
9142
9178
|
printTable(
|
|
@@ -9227,27 +9263,53 @@ async function cmdAccountBalance(run, ccy, json) {
|
|
|
9227
9263
|
const data = getData2(result);
|
|
9228
9264
|
if (json) return printJson(data);
|
|
9229
9265
|
const details = data?.[0]?.["details"] ?? [];
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9266
|
+
const rows = details.filter((d) => Number(d["eq"]) > 0).map((d) => ({
|
|
9267
|
+
currency: d["ccy"],
|
|
9268
|
+
equity: d["eq"],
|
|
9269
|
+
available: d["availEq"],
|
|
9270
|
+
frozen: d["frozenBal"]
|
|
9271
|
+
}));
|
|
9272
|
+
if (rows.length === 0 && data?.[0]) {
|
|
9273
|
+
printTable([{ currency: "Total", equity: data[0]["totalEq"] ?? "0", available: data[0]["adjEq"] ?? "0", frozen: "-" }]);
|
|
9274
|
+
return;
|
|
9275
|
+
}
|
|
9276
|
+
printTable(rows);
|
|
9238
9277
|
}
|
|
9239
|
-
async function cmdAccountAssetBalance(run, ccy, json) {
|
|
9240
|
-
const result = await run("account_get_asset_balance", {
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9278
|
+
async function cmdAccountAssetBalance(run, ccy, json, showValuation) {
|
|
9279
|
+
const result = await run("account_get_asset_balance", {
|
|
9280
|
+
ccy,
|
|
9281
|
+
...showValuation ? { showValuation: true } : {}
|
|
9282
|
+
});
|
|
9283
|
+
const data = result.data ?? [];
|
|
9284
|
+
if (json) return printJson(showValuation ? { data, valuation: result.valuation } : data);
|
|
9285
|
+
const assetRows = data.filter((r) => Number(r["bal"]) > 0).map((r) => ({
|
|
9286
|
+
ccy: r["ccy"],
|
|
9287
|
+
bal: r["bal"],
|
|
9288
|
+
availBal: r["availBal"],
|
|
9289
|
+
frozenBal: r["frozenBal"]
|
|
9290
|
+
}));
|
|
9291
|
+
if (assetRows.length === 0 && data.length > 0) {
|
|
9292
|
+
outputLine("Total balance: 0");
|
|
9293
|
+
} else {
|
|
9294
|
+
printTable(assetRows);
|
|
9295
|
+
}
|
|
9296
|
+
if (showValuation && result.valuation) {
|
|
9297
|
+
const valuationData = result.valuation ?? [];
|
|
9298
|
+
outputLine("");
|
|
9299
|
+
outputLine("Asset Valuation by Account Type:");
|
|
9300
|
+
printTable(
|
|
9301
|
+
valuationData.map((v) => {
|
|
9302
|
+
const details = v["details"] ?? {};
|
|
9303
|
+
return {
|
|
9304
|
+
totalBal: v["totalBal"],
|
|
9305
|
+
classic: details["classic"],
|
|
9306
|
+
earn: details["earn"],
|
|
9307
|
+
funding: details["funding"],
|
|
9308
|
+
trading: details["trading"]
|
|
9309
|
+
};
|
|
9310
|
+
})
|
|
9311
|
+
);
|
|
9312
|
+
}
|
|
9251
9313
|
}
|
|
9252
9314
|
async function cmdAccountPositions(run, opts) {
|
|
9253
9315
|
const result = await run("account_get_positions", { instType: opts.instType, instId: opts.instId });
|
|
@@ -10163,7 +10225,7 @@ async function cmdFuturesBatch(run, opts) {
|
|
|
10163
10225
|
process.exitCode = 1;
|
|
10164
10226
|
return;
|
|
10165
10227
|
}
|
|
10166
|
-
const result = await run(tool,
|
|
10228
|
+
const result = await run(tool, { orders: parsed });
|
|
10167
10229
|
const data = getData5(result);
|
|
10168
10230
|
if (opts.json) return printJson(data);
|
|
10169
10231
|
emitBatchResults3(data ?? []);
|
|
@@ -10667,6 +10729,58 @@ function buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo) {
|
|
|
10667
10729
|
}
|
|
10668
10730
|
return entry;
|
|
10669
10731
|
}
|
|
10732
|
+
function tryOpenUrl(url) {
|
|
10733
|
+
try {
|
|
10734
|
+
let opener;
|
|
10735
|
+
if (process.platform === "darwin") {
|
|
10736
|
+
opener = "open";
|
|
10737
|
+
} else if (process.platform === "win32") {
|
|
10738
|
+
opener = "start";
|
|
10739
|
+
} else {
|
|
10740
|
+
opener = "xdg-open";
|
|
10741
|
+
}
|
|
10742
|
+
spawnSync2(opener, [url], { stdio: "ignore", shell: process.platform === "win32" });
|
|
10743
|
+
} catch {
|
|
10744
|
+
}
|
|
10745
|
+
}
|
|
10746
|
+
async function promptCredentials(rl, t) {
|
|
10747
|
+
const apiKey = (await prompt(rl, "API Key: ")).trim();
|
|
10748
|
+
if (!apiKey) {
|
|
10749
|
+
errorLine(t.emptyApiKey);
|
|
10750
|
+
process.exitCode = 1;
|
|
10751
|
+
return null;
|
|
10752
|
+
}
|
|
10753
|
+
const secretKey = (await prompt(rl, "Secret Key: ")).trim();
|
|
10754
|
+
if (!secretKey) {
|
|
10755
|
+
errorLine(t.emptySecretKey);
|
|
10756
|
+
process.exitCode = 1;
|
|
10757
|
+
return null;
|
|
10758
|
+
}
|
|
10759
|
+
const passphrase = (await prompt(rl, "Passphrase: ")).trim();
|
|
10760
|
+
if (!passphrase) {
|
|
10761
|
+
errorLine(t.emptyPassphrase);
|
|
10762
|
+
process.exitCode = 1;
|
|
10763
|
+
return null;
|
|
10764
|
+
}
|
|
10765
|
+
return { apiKey, secretKey, passphrase };
|
|
10766
|
+
}
|
|
10767
|
+
function saveConfig(config, profileName, t) {
|
|
10768
|
+
const configPath = configFilePath();
|
|
10769
|
+
try {
|
|
10770
|
+
writeCliConfig(config);
|
|
10771
|
+
output(t.saved(configPath));
|
|
10772
|
+
output(t.defaultProfile(profileName));
|
|
10773
|
+
output(t.usage);
|
|
10774
|
+
} catch (err) {
|
|
10775
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10776
|
+
const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
|
|
10777
|
+
errorOutput(t.writeFailed(message));
|
|
10778
|
+
if (isPermission) errorOutput(t.permissionDenied(configPath));
|
|
10779
|
+
errorOutput(t.manualWrite(configPath));
|
|
10780
|
+
outputLine(stringify(config));
|
|
10781
|
+
process.exitCode = 1;
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10670
10784
|
async function cmdConfigInit(lang = "en") {
|
|
10671
10785
|
const t = messages[lang];
|
|
10672
10786
|
outputLine(t.title);
|
|
@@ -10682,14 +10796,9 @@ async function cmdConfigInit(lang = "en") {
|
|
|
10682
10796
|
const demoRaw = (await prompt(rl, t.demoPrompt)).trim().toLowerCase();
|
|
10683
10797
|
const demo = demoRaw !== "n";
|
|
10684
10798
|
const apiUrl = buildApiUrl(siteKey, demo);
|
|
10685
|
-
const hintText = demo ? t.hintDemo : t.hintLive;
|
|
10686
10799
|
output(t.createApiKey(apiUrl));
|
|
10687
|
-
output(t.hint(
|
|
10688
|
-
|
|
10689
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
10690
|
-
spawnSync2(opener, [apiUrl], { stdio: "ignore", shell: process.platform === "win32" });
|
|
10691
|
-
} catch {
|
|
10692
|
-
}
|
|
10800
|
+
output(t.hint(demo ? t.hintDemo : t.hintLive));
|
|
10801
|
+
tryOpenUrl(apiUrl);
|
|
10693
10802
|
const defaultProfileName = demo ? "okx-demo" : "okx-prod";
|
|
10694
10803
|
const profileNameRaw = await prompt(rl, t.profilePrompt(defaultProfileName));
|
|
10695
10804
|
const profileName = profileNameRaw.trim() || defaultProfileName;
|
|
@@ -10701,49 +10810,14 @@ async function cmdConfigInit(lang = "en") {
|
|
|
10701
10810
|
return;
|
|
10702
10811
|
}
|
|
10703
10812
|
}
|
|
10704
|
-
const
|
|
10705
|
-
if (!
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
return;
|
|
10709
|
-
}
|
|
10710
|
-
const secretKey = (await prompt(rl, "Secret Key: ")).trim();
|
|
10711
|
-
if (!secretKey) {
|
|
10712
|
-
errorLine(t.emptySecretKey);
|
|
10713
|
-
process.exitCode = 1;
|
|
10714
|
-
return;
|
|
10715
|
-
}
|
|
10716
|
-
const passphrase = (await prompt(rl, "Passphrase: ")).trim();
|
|
10717
|
-
if (!passphrase) {
|
|
10718
|
-
errorLine(t.emptyPassphrase);
|
|
10719
|
-
process.exitCode = 1;
|
|
10720
|
-
return;
|
|
10721
|
-
}
|
|
10722
|
-
if (demo) {
|
|
10723
|
-
outputLine(t.demoSelected);
|
|
10724
|
-
}
|
|
10725
|
-
const profileEntry = buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo);
|
|
10726
|
-
config.profiles[profileName] = profileEntry;
|
|
10813
|
+
const credentials = await promptCredentials(rl, t);
|
|
10814
|
+
if (!credentials) return;
|
|
10815
|
+
if (demo) outputLine(t.demoSelected);
|
|
10816
|
+
config.profiles[profileName] = buildProfileEntry(siteKey, credentials.apiKey, credentials.secretKey, credentials.passphrase, demo);
|
|
10727
10817
|
if (!config.default_profile || config.default_profile !== profileName) {
|
|
10728
10818
|
config.default_profile = profileName;
|
|
10729
10819
|
}
|
|
10730
|
-
|
|
10731
|
-
try {
|
|
10732
|
-
writeCliConfig(config);
|
|
10733
|
-
output(t.saved(configPath));
|
|
10734
|
-
output(t.defaultProfile(profileName));
|
|
10735
|
-
output(t.usage);
|
|
10736
|
-
} catch (err) {
|
|
10737
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
10738
|
-
const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
|
|
10739
|
-
errorOutput(t.writeFailed(message));
|
|
10740
|
-
if (isPermission) {
|
|
10741
|
-
errorOutput(t.permissionDenied(configPath));
|
|
10742
|
-
}
|
|
10743
|
-
errorOutput(t.manualWrite(configPath));
|
|
10744
|
-
outputLine(stringify(config));
|
|
10745
|
-
process.exitCode = 1;
|
|
10746
|
-
}
|
|
10820
|
+
saveConfig(config, profileName, t);
|
|
10747
10821
|
} finally {
|
|
10748
10822
|
rl.close();
|
|
10749
10823
|
}
|
|
@@ -11119,7 +11193,6 @@ async function cmdGridCreate(run, opts) {
|
|
|
11119
11193
|
});
|
|
11120
11194
|
const data = getData7(result);
|
|
11121
11195
|
if (opts.json) return printJson(data);
|
|
11122
|
-
const r = data?.[0];
|
|
11123
11196
|
emitWriteResult5(data?.[0], "Grid bot created", "algoId");
|
|
11124
11197
|
}
|
|
11125
11198
|
async function cmdGridStop(run, opts) {
|
|
@@ -11131,7 +11204,6 @@ async function cmdGridStop(run, opts) {
|
|
|
11131
11204
|
});
|
|
11132
11205
|
const data = getData7(result);
|
|
11133
11206
|
if (opts.json) return printJson(data);
|
|
11134
|
-
const r = data?.[0];
|
|
11135
11207
|
emitWriteResult5(data?.[0], "Grid bot stopped", "algoId");
|
|
11136
11208
|
}
|
|
11137
11209
|
async function cmdDcaCreate(run, opts) {
|
|
@@ -11565,7 +11637,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
11565
11637
|
// src/index.ts
|
|
11566
11638
|
var _require3 = createRequire3(import.meta.url);
|
|
11567
11639
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
11568
|
-
var GIT_HASH2 = true ? "
|
|
11640
|
+
var GIT_HASH2 = true ? "f9ea608" : "dev";
|
|
11569
11641
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
11570
11642
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
11571
11643
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -11626,7 +11698,7 @@ function handleMarketDataCommand(run, action, rest, v, json) {
|
|
|
11626
11698
|
if (action === "orderbook")
|
|
11627
11699
|
return cmdMarketOrderbook(run, rest[0], v.sz !== void 0 ? Number(v.sz) : void 0, json);
|
|
11628
11700
|
if (action === "candles")
|
|
11629
|
-
return cmdMarketCandles(run, rest[0], { bar: v.bar, limit, json });
|
|
11701
|
+
return cmdMarketCandles(run, rest[0], { bar: v.bar, limit, after: v.after, before: v.before, json });
|
|
11630
11702
|
if (action === "funding-rate")
|
|
11631
11703
|
return cmdMarketFundingRate(run, rest[0], { history: v.history ?? false, limit, json });
|
|
11632
11704
|
if (action === "trades")
|
|
@@ -11661,7 +11733,7 @@ function handleAccountCommand(run, action, rest, v, json) {
|
|
|
11661
11733
|
return cmdAccountAudit({ limit: v.limit, tool: v.tool, since: v.since, json });
|
|
11662
11734
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
11663
11735
|
if (action === "balance") return cmdAccountBalance(run, rest[0], json);
|
|
11664
|
-
if (action === "asset-balance") return cmdAccountAssetBalance(run, v.ccy, json);
|
|
11736
|
+
if (action === "asset-balance") return cmdAccountAssetBalance(run, v.ccy, json, v.valuation);
|
|
11665
11737
|
if (action === "positions")
|
|
11666
11738
|
return cmdAccountPositions(run, { instType: v.instType, instId: v.instId, json });
|
|
11667
11739
|
if (action === "positions-history")
|