@okx_ai/okx-trade-cli 1.2.5-beta.5 → 1.2.6-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1019,9 +1019,9 @@ var OKX_CODE_BEHAVIORS = {
1019
1019
  "50100": { retry: false, suggestion: "API key lacks required permissions. Update API key permissions." },
1020
1020
  "50110": { retry: false, suggestion: "API key expired. Generate a new API key." },
1021
1021
  // Insufficient funds / margin → do not retry
1022
- "51008": { retry: false, suggestion: "Insufficient balance. Top up account before retrying." },
1023
- "51119": { retry: false, suggestion: "Insufficient margin. Add margin before retrying." },
1024
- "51127": { retry: false, suggestion: "Insufficient available margin. Reduce position or add margin." },
1022
+ "51008": { retry: false, suggestion: "Insufficient balance in trading account. Check funding account via account_get_asset_balance \u2014 funds may be there. Use account_transfer (from=18, to=6) to move funds to trading account, then retry." },
1023
+ "51119": { retry: false, suggestion: "Insufficient margin. Add margin or check funding account (account_get_asset_balance). Transfer via account_transfer (from=18, to=6) if needed." },
1024
+ "51127": { retry: false, suggestion: "Insufficient available margin. Reduce position, add margin, or transfer from funding account (account_transfer from=18 to=6)." },
1025
1025
  // Instrument unavailable → do not retry
1026
1026
  "51021": { retry: false, suggestion: "Instrument does not exist. Check instId." },
1027
1027
  "51022": { retry: false, suggestion: "Instrument not available for trading." },
@@ -1104,6 +1104,15 @@ var OkxRestClient = class {
1104
1104
  rateLimit
1105
1105
  });
1106
1106
  }
1107
+ async publicPost(path42, body, rateLimit) {
1108
+ return this.request({
1109
+ method: "POST",
1110
+ path: path42,
1111
+ auth: "public",
1112
+ body,
1113
+ rateLimit
1114
+ });
1115
+ }
1107
1116
  async privatePost(path42, body, rateLimit) {
1108
1117
  return this.request({
1109
1118
  method: "POST",
@@ -1258,46 +1267,6 @@ var OkxRestClient = class {
1258
1267
  return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
1259
1268
  }
1260
1269
  };
1261
- var DEFAULT_SOURCE_TAG = "MCP";
1262
- var OKX_SITES = {
1263
- global: {
1264
- label: "Global",
1265
- apiBaseUrl: "https://www.okx.com",
1266
- webUrl: "https://www.okx.com"
1267
- },
1268
- eea: {
1269
- label: "EEA",
1270
- apiBaseUrl: "https://eea.okx.com",
1271
- webUrl: "https://my.okx.com"
1272
- },
1273
- us: {
1274
- label: "US",
1275
- apiBaseUrl: "https://app.okx.com",
1276
- webUrl: "https://app.okx.com"
1277
- }
1278
- };
1279
- var SITE_IDS = Object.keys(OKX_SITES);
1280
- var BOT_SUB_MODULE_IDS = [
1281
- "bot.grid",
1282
- "bot.dca"
1283
- ];
1284
- var BOT_DEFAULT_SUB_MODULES = ["bot.grid"];
1285
- var EARN_SUB_MODULE_IDS = [
1286
- "earn.savings",
1287
- "earn.onchain",
1288
- "earn.dcd"
1289
- ];
1290
- var MODULES = [
1291
- "market",
1292
- "spot",
1293
- "swap",
1294
- "futures",
1295
- "option",
1296
- "account",
1297
- ...EARN_SUB_MODULE_IDS,
1298
- ...BOT_SUB_MODULE_IDS
1299
- ];
1300
- var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES];
1301
1270
  function asRecord(value) {
1302
1271
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1303
1272
  return {};
@@ -1422,6 +1391,152 @@ function assertNotDemo(config, endpoint) {
1422
1391
  );
1423
1392
  }
1424
1393
  }
1394
+ var INDICATOR_BARS = [
1395
+ "3m",
1396
+ "5m",
1397
+ "15m",
1398
+ "1H",
1399
+ "4H",
1400
+ "12Hutc",
1401
+ "1Dutc",
1402
+ "3Dutc",
1403
+ "1Wutc"
1404
+ ];
1405
+ var INDICATOR_CODE_OVERRIDES = {
1406
+ "rainbow": "BTCRAINBOW",
1407
+ "range-filter": "RANGEFILTER",
1408
+ "stoch-rsi": "STOCHRSI",
1409
+ "pi-cycle-top": "PI_CYCLE_TOP",
1410
+ "pi-cycle-bottom": "PI_CYCLE_BOTTOM",
1411
+ // boll is an alias for bb; server supports BB not BOLL
1412
+ "boll": "BB"
1413
+ };
1414
+ function resolveIndicatorCode(name) {
1415
+ const lower = name.toLowerCase();
1416
+ return INDICATOR_CODE_OVERRIDES[lower] ?? name.toUpperCase().replace(/-/g, "_");
1417
+ }
1418
+ function readNumberArray(args, key) {
1419
+ const value = args[key];
1420
+ if (value === void 0 || value === null) return void 0;
1421
+ if (!Array.isArray(value)) return void 0;
1422
+ return value.map((item) => Number(item));
1423
+ }
1424
+ function registerIndicatorTools() {
1425
+ return [
1426
+ {
1427
+ name: "market_get_indicator",
1428
+ module: "market",
1429
+ description: "Get technical indicator values for an instrument (MA, EMA, RSI, MACD, BB, KDJ, SUPERTREND, AHR999, BTCRAINBOW, and more). No credentials required.",
1430
+ isWrite: false,
1431
+ inputSchema: {
1432
+ type: "object",
1433
+ properties: {
1434
+ instId: {
1435
+ type: "string",
1436
+ description: "Instrument ID, e.g. BTC-USDT"
1437
+ },
1438
+ indicator: {
1439
+ type: "string",
1440
+ description: "Indicator name (case-insensitive). Examples: ma, ema, rsi, macd, bb, kdj, supertrend, ahr999, rainbow, pi-cycle-top, pi-cycle-bottom, mayer, envelope, halftrend, alphatrend, pmax, waddah, tdi, qqe, range-filter"
1441
+ },
1442
+ bar: {
1443
+ type: "string",
1444
+ enum: [...INDICATOR_BARS],
1445
+ description: "Timeframe. Default: 1H"
1446
+ },
1447
+ params: {
1448
+ type: "array",
1449
+ items: { type: "number" },
1450
+ description: "Indicator parameters, e.g. [5, 20] for MA with periods 5 and 20"
1451
+ },
1452
+ returnList: {
1453
+ type: "boolean",
1454
+ description: "Return a historical list instead of the latest value only. Default: false"
1455
+ },
1456
+ limit: {
1457
+ type: "integer",
1458
+ minimum: 1,
1459
+ maximum: 100,
1460
+ description: "Number of historical records to return (only used when returnList=true). Default: 10"
1461
+ },
1462
+ backtestTime: {
1463
+ type: "integer",
1464
+ description: "Backtest timestamp in milliseconds. Omit for live (real-time) mode"
1465
+ }
1466
+ },
1467
+ required: ["instId", "indicator"]
1468
+ },
1469
+ handler: async (rawArgs, context) => {
1470
+ const args = asRecord(rawArgs);
1471
+ const instId = requireString(args, "instId");
1472
+ const indicator = requireString(args, "indicator");
1473
+ const bar = readString(args, "bar") ?? "1H";
1474
+ const params = readNumberArray(args, "params");
1475
+ const returnList = readBoolean(args, "returnList") ?? false;
1476
+ const limit = readNumber(args, "limit") ?? 10;
1477
+ const backtestTime = readNumber(args, "backtestTime");
1478
+ const apiCode = resolveIndicatorCode(indicator);
1479
+ const indicatorConfig = compactObject({
1480
+ paramList: params && params.length > 0 ? params : void 0,
1481
+ returnList,
1482
+ limit: returnList ? limit : void 0
1483
+ });
1484
+ const body = compactObject({
1485
+ instId,
1486
+ timeframes: [bar],
1487
+ indicators: { [apiCode]: indicatorConfig },
1488
+ backtestTime
1489
+ });
1490
+ const response = await context.client.publicPost(
1491
+ "/api/v5/aigc/mcp/indicators",
1492
+ body,
1493
+ publicRateLimit("market_get_indicator", 5)
1494
+ );
1495
+ return normalizeResponse(response);
1496
+ }
1497
+ }
1498
+ ];
1499
+ }
1500
+ var DEFAULT_SOURCE_TAG = "MCP";
1501
+ var OKX_SITES = {
1502
+ global: {
1503
+ label: "Global",
1504
+ apiBaseUrl: "https://www.okx.com",
1505
+ webUrl: "https://www.okx.com"
1506
+ },
1507
+ eea: {
1508
+ label: "EEA",
1509
+ apiBaseUrl: "https://eea.okx.com",
1510
+ webUrl: "https://my.okx.com"
1511
+ },
1512
+ us: {
1513
+ label: "US",
1514
+ apiBaseUrl: "https://app.okx.com",
1515
+ webUrl: "https://app.okx.com"
1516
+ }
1517
+ };
1518
+ var SITE_IDS = Object.keys(OKX_SITES);
1519
+ var BOT_SUB_MODULE_IDS = [
1520
+ "bot.grid",
1521
+ "bot.dca"
1522
+ ];
1523
+ var BOT_DEFAULT_SUB_MODULES = ["bot.grid"];
1524
+ var EARN_SUB_MODULE_IDS = [
1525
+ "earn.savings",
1526
+ "earn.onchain",
1527
+ "earn.dcd"
1528
+ ];
1529
+ var MODULES = [
1530
+ "market",
1531
+ "spot",
1532
+ "swap",
1533
+ "futures",
1534
+ "option",
1535
+ "account",
1536
+ ...EARN_SUB_MODULE_IDS,
1537
+ ...BOT_SUB_MODULE_IDS
1538
+ ];
1539
+ var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES];
1425
1540
  function registerAccountTools() {
1426
1541
  return [
1427
1542
  {
@@ -3196,7 +3311,7 @@ function registerEarnTools() {
3196
3311
  {
3197
3312
  name: "earn_get_savings_balance",
3198
3313
  module: "earn.savings",
3199
- description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history \u2014 do NOT use earn_get_lending_rate_summary for this purpose. Private endpoint. Rate limit: 6 req/s.",
3314
+ description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history \u2014 do NOT use earn_get_lending_rate_summary for this purpose.",
3200
3315
  isWrite: false,
3201
3316
  inputSchema: {
3202
3317
  type: "object",
@@ -3220,7 +3335,7 @@ function registerEarnTools() {
3220
3335
  {
3221
3336
  name: "earn_savings_purchase",
3222
3337
  module: "earn.savings",
3223
- 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.",
3338
+ description: "Purchase Simple Earn (savings/flexible earn). [CAUTION] Moves real funds into earn product.",
3224
3339
  isWrite: true,
3225
3340
  inputSchema: {
3226
3341
  type: "object",
@@ -3259,7 +3374,7 @@ function registerEarnTools() {
3259
3374
  {
3260
3375
  name: "earn_savings_redeem",
3261
3376
  module: "earn.savings",
3262
- 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.",
3377
+ description: "Redeem Simple Earn (savings/flexible earn). [CAUTION] Withdraws funds from earn product.",
3263
3378
  isWrite: true,
3264
3379
  inputSchema: {
3265
3380
  type: "object",
@@ -3293,7 +3408,7 @@ function registerEarnTools() {
3293
3408
  {
3294
3409
  name: "earn_set_lending_rate",
3295
3410
  module: "earn.savings",
3296
- 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.",
3411
+ description: "Set lending rate for Simple Earn. [CAUTION] Changes your lending rate preference.",
3297
3412
  isWrite: true,
3298
3413
  inputSchema: {
3299
3414
  type: "object",
@@ -3326,7 +3441,7 @@ function registerEarnTools() {
3326
3441
  {
3327
3442
  name: "earn_get_lending_history",
3328
3443
  module: "earn.savings",
3329
- description: "Get market lending rate history for Simple Earn. Use this tool to query market lending rates. Returns market lending records with amount, rate, and earnings data. Private endpoint. Rate limit: 6 req/s.",
3444
+ description: "Get market lending rate history for Simple Earn. Use this tool to query market lending rates. Returns market lending records with amount, rate, and earnings data.",
3330
3445
  isWrite: false,
3331
3446
  inputSchema: {
3332
3447
  type: "object",
@@ -3367,7 +3482,7 @@ function registerEarnTools() {
3367
3482
  {
3368
3483
  name: "earn_get_lending_rate_summary",
3369
3484
  module: "earn.savings",
3370
- description: "Get coin lending market rate summary. NOT related to Simple Earn. Public endpoint (no API key required). Use this to query the lending/borrowing market rates (\u501F\u5E01\u5E02\u573A\u5229\u7387). Returns aggregate overview: average rate (avgRate), estimated next-cycle rate (estRate), previous-cycle rate (preRate), and available lending amounts. Rate limit: 6 req/s.",
3485
+ description: "Get coin lending market rate summary. NOT related to Simple Earn. Use this to query the lending/borrowing market rates (\u501F\u5E01\u5E02\u573A\u5229\u7387). Returns aggregate overview: average rate (avgRate), estimated next-cycle rate (estRate), previous-cycle rate (preRate), and available lending amounts.",
3371
3486
  isWrite: false,
3372
3487
  inputSchema: {
3373
3488
  type: "object",
@@ -3391,7 +3506,7 @@ function registerEarnTools() {
3391
3506
  {
3392
3507
  name: "earn_get_lending_rate_history",
3393
3508
  module: "earn.savings",
3394
- description: "Query Simple Earn lending rates. Public endpoint (no API key required). Use this tool when the user asks about current or historical lending rates for Simple Earn, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns actual settled lending rate records (lendingRate field) with timestamps, ordered newest-first. Rate limit: 6 req/s.",
3509
+ description: "Query Simple Earn lending rates. Use this tool when the user asks about current or historical lending rates for Simple Earn, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns actual settled lending rate records (lendingRate field) with timestamps, ordered newest-first.",
3395
3510
  isWrite: false,
3396
3511
  inputSchema: {
3397
3512
  type: "object",
@@ -3439,22 +3554,22 @@ function registerOnchainEarnTools() {
3439
3554
  {
3440
3555
  name: "onchain_earn_get_offers",
3441
3556
  module: "earn.onchain",
3442
- description: "Get available on-chain earn (staking/DeFi) offers. Returns investment products with protocol name, APY, terms, and limits. Always display the protocol name (protocol field) and earnings currency (earningData[].ccy field) when showing offers to the user. Private endpoint. Rate limit: 3 req/s.",
3557
+ description: "List staking/DeFi products with APY, terms, and limits. Always show protocol name (protocol field) and earnings currency (earningData[].ccy) when presenting results.",
3443
3558
  isWrite: false,
3444
3559
  inputSchema: {
3445
3560
  type: "object",
3446
3561
  properties: {
3447
3562
  productId: {
3448
3563
  type: "string",
3449
- description: "Specific product ID to query. Omit for all offers."
3564
+ description: "Product ID filter."
3450
3565
  },
3451
3566
  protocolType: {
3452
3567
  type: "string",
3453
- description: "Protocol type filter: staking, defi. Omit for all types."
3568
+ description: "staking|defi"
3454
3569
  },
3455
3570
  ccy: {
3456
3571
  type: "string",
3457
- description: "Currency filter, e.g. ETH. Omit for all currencies."
3572
+ description: "e.g. ETH"
3458
3573
  }
3459
3574
  }
3460
3575
  },
@@ -3478,34 +3593,34 @@ function registerOnchainEarnTools() {
3478
3593
  {
3479
3594
  name: "onchain_earn_purchase",
3480
3595
  module: "earn.onchain",
3481
- 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.",
3596
+ description: "Invest in a staking/DeFi product. [CAUTION] Moves real funds. Not available in demo mode.",
3482
3597
  isWrite: true,
3483
3598
  inputSchema: {
3484
3599
  type: "object",
3485
3600
  properties: {
3486
3601
  productId: {
3487
3602
  type: "string",
3488
- description: "Product ID to purchase"
3603
+ description: "Product ID"
3489
3604
  },
3490
3605
  investData: {
3491
3606
  type: "array",
3492
- description: "Investment data array: [{ccy, amt}]. Each item specifies currency and amount.",
3607
+ description: "Array of {ccy, amt} to invest.",
3493
3608
  items: {
3494
3609
  type: "object",
3495
3610
  properties: {
3496
- ccy: { type: "string", description: "Currency, e.g. ETH" },
3497
- amt: { type: "string", description: "Amount to invest" }
3611
+ ccy: { type: "string", description: "e.g. ETH" },
3612
+ amt: { type: "string" }
3498
3613
  },
3499
3614
  required: ["ccy", "amt"]
3500
3615
  }
3501
3616
  },
3502
3617
  term: {
3503
3618
  type: "string",
3504
- description: "Investment term in days. Required for fixed-term products."
3619
+ description: "Investment term in days, required for fixed-term products."
3505
3620
  },
3506
3621
  tag: {
3507
3622
  type: "string",
3508
- description: "Order tag for tracking (optional)."
3623
+ description: "Order tag."
3509
3624
  }
3510
3625
  },
3511
3626
  required: ["productId", "investData"]
@@ -3532,22 +3647,22 @@ function registerOnchainEarnTools() {
3532
3647
  {
3533
3648
  name: "onchain_earn_redeem",
3534
3649
  module: "earn.onchain",
3535
- 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.",
3650
+ description: "Redeem a staking/DeFi investment. [CAUTION] Some products have lock periods, early redemption may incur penalties. Not available in demo mode.",
3536
3651
  isWrite: true,
3537
3652
  inputSchema: {
3538
3653
  type: "object",
3539
3654
  properties: {
3540
3655
  ordId: {
3541
3656
  type: "string",
3542
- description: "Order ID to redeem"
3657
+ description: "Order ID"
3543
3658
  },
3544
3659
  protocolType: {
3545
3660
  type: "string",
3546
- description: "Protocol type: staking, defi"
3661
+ description: "staking|defi"
3547
3662
  },
3548
3663
  allowEarlyRedeem: {
3549
3664
  type: "boolean",
3550
- description: "Allow early redemption for fixed-term products (may incur penalties). Default false."
3665
+ description: "Allow early exit for fixed-term products, may incur penalties. Default: false."
3551
3666
  }
3552
3667
  },
3553
3668
  required: ["ordId", "protocolType"]
@@ -3573,18 +3688,18 @@ function registerOnchainEarnTools() {
3573
3688
  {
3574
3689
  name: "onchain_earn_cancel",
3575
3690
  module: "earn.onchain",
3576
- 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.",
3691
+ description: "Cancel a pending staking/DeFi purchase order. [CAUTION] Not available in demo mode.",
3577
3692
  isWrite: true,
3578
3693
  inputSchema: {
3579
3694
  type: "object",
3580
3695
  properties: {
3581
3696
  ordId: {
3582
3697
  type: "string",
3583
- description: "Order ID to cancel"
3698
+ description: "Order ID"
3584
3699
  },
3585
3700
  protocolType: {
3586
3701
  type: "string",
3587
- description: "Protocol type: staking, defi"
3702
+ description: "staking|defi"
3588
3703
  }
3589
3704
  },
3590
3705
  required: ["ordId", "protocolType"]
@@ -3609,26 +3724,26 @@ function registerOnchainEarnTools() {
3609
3724
  {
3610
3725
  name: "onchain_earn_get_active_orders",
3611
3726
  module: "earn.onchain",
3612
- description: "Get active on-chain earn orders. Returns current staking/DeFi investments. Private endpoint. Rate limit: 3 req/s.",
3727
+ description: "List current active staking/DeFi investments.",
3613
3728
  isWrite: false,
3614
3729
  inputSchema: {
3615
3730
  type: "object",
3616
3731
  properties: {
3617
3732
  productId: {
3618
3733
  type: "string",
3619
- description: "Filter by product ID. Omit for all."
3734
+ description: "Product ID filter."
3620
3735
  },
3621
3736
  protocolType: {
3622
3737
  type: "string",
3623
- description: "Filter by protocol type: staking, defi. Omit for all."
3738
+ description: "staking|defi"
3624
3739
  },
3625
3740
  ccy: {
3626
3741
  type: "string",
3627
- description: "Filter by currency, e.g. ETH. Omit for all."
3742
+ description: "e.g. ETH"
3628
3743
  },
3629
3744
  state: {
3630
3745
  type: "string",
3631
- description: "Filter by state: 8 (pending), 13 (cancelling), 9 (onchain), 1 (earning), 2 (redeeming). Omit for all."
3746
+ description: "8=pending, 13=cancelling, 9=onchain, 1=earning, 2=redeeming"
3632
3747
  }
3633
3748
  }
3634
3749
  },
@@ -3653,34 +3768,34 @@ function registerOnchainEarnTools() {
3653
3768
  {
3654
3769
  name: "onchain_earn_get_order_history",
3655
3770
  module: "earn.onchain",
3656
- description: "Get on-chain earn order history. Returns past staking/DeFi investments including redeemed orders. Private endpoint. Rate limit: 3 req/s.",
3771
+ description: "List past staking/DeFi orders including redeemed ones.",
3657
3772
  isWrite: false,
3658
3773
  inputSchema: {
3659
3774
  type: "object",
3660
3775
  properties: {
3661
3776
  productId: {
3662
3777
  type: "string",
3663
- description: "Filter by product ID. Omit for all."
3778
+ description: "Product ID filter."
3664
3779
  },
3665
3780
  protocolType: {
3666
3781
  type: "string",
3667
- description: "Filter by protocol type: staking, defi. Omit for all."
3782
+ description: "staking|defi"
3668
3783
  },
3669
3784
  ccy: {
3670
3785
  type: "string",
3671
- description: "Filter by currency, e.g. ETH. Omit for all."
3786
+ description: "e.g. ETH"
3672
3787
  },
3673
3788
  after: {
3674
3789
  type: "string",
3675
- description: "Pagination: return results before this order ID"
3790
+ description: "Cursor: results older than this order ID."
3676
3791
  },
3677
3792
  before: {
3678
3793
  type: "string",
3679
- description: "Pagination: return results after this order ID"
3794
+ description: "Cursor: results newer than this order ID."
3680
3795
  },
3681
3796
  limit: {
3682
3797
  type: "string",
3683
- description: "Max results to return (default 100, max 100)"
3798
+ description: "Max results (default 100, max 100)."
3684
3799
  }
3685
3800
  }
3686
3801
  },
@@ -4059,7 +4174,12 @@ function buildContractTradeTools(cfg) {
4059
4174
  },
4060
4175
  sz: {
4061
4176
  type: "string",
4062
- description: "Contracts count (NOT USDT). Use market_get_instruments for ctVal."
4177
+ description: "Contracts count by default. Set tgtCcy=quote_ccy to use USDT amount instead."
4178
+ },
4179
+ tgtCcy: {
4180
+ type: "string",
4181
+ enum: ["base_ccy", "quote_ccy"],
4182
+ description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT"
4063
4183
  },
4064
4184
  px: { type: "string", description: "Required for limit/post_only/fok/ioc" },
4065
4185
  reduceOnly: {
@@ -4087,6 +4207,7 @@ function buildContractTradeTools(cfg) {
4087
4207
  posSide: readString(args, "posSide"),
4088
4208
  ordType: requireString(args, "ordType"),
4089
4209
  sz: requireString(args, "sz"),
4210
+ tgtCcy: readString(args, "tgtCcy"),
4090
4211
  px: readString(args, "px"),
4091
4212
  reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
4092
4213
  clOrdId: readString(args, "clOrdId"),
@@ -5715,7 +5836,7 @@ function registerSpotTradeTools() {
5715
5836
  {
5716
5837
  name: "spot_place_order",
5717
5838
  module: "spot",
5718
- description: "Place a spot order. Optionally attach TP/SL via tpTriggerPx/slTriggerPx (assembled into attachAlgoOrds automatically). [CAUTION] Executes real trades.",
5839
+ description: "Place a spot order. Attach TP/SL via tpTriggerPx/slTriggerPx. [CAUTION] Executes real trades.",
5719
5840
  isWrite: true,
5720
5841
  inputSchema: {
5721
5842
  type: "object",
@@ -5727,7 +5848,7 @@ function registerSpotTradeTools() {
5727
5848
  tdMode: {
5728
5849
  type: "string",
5729
5850
  enum: ["cash", "cross", "isolated"],
5730
- description: "cash=regular spot; cross/isolated=margin"
5851
+ description: "cash=regular spot, cross/isolated=margin"
5731
5852
  },
5732
5853
  side: {
5733
5854
  type: "string",
@@ -5740,7 +5861,12 @@ function registerSpotTradeTools() {
5740
5861
  },
5741
5862
  sz: {
5742
5863
  type: "string",
5743
- description: "Buy market: quote amount; all others: base amount"
5864
+ description: "Buy market: quote amount, all others: base amount"
5865
+ },
5866
+ tgtCcy: {
5867
+ type: "string",
5868
+ enum: ["base_ccy", "quote_ccy"],
5869
+ description: "Size unit. base_ccy(default): sz in base (e.g. BTC), quote_ccy: sz in quote (e.g. USDT)"
5744
5870
  },
5745
5871
  px: {
5746
5872
  type: "string",
@@ -5752,19 +5878,19 @@ function registerSpotTradeTools() {
5752
5878
  },
5753
5879
  tpTriggerPx: {
5754
5880
  type: "string",
5755
- description: "TP trigger price; places TP at tpOrdPx"
5881
+ description: "TP trigger price"
5756
5882
  },
5757
5883
  tpOrdPx: {
5758
5884
  type: "string",
5759
- description: "TP order price; -1=market"
5885
+ description: "TP order price, -1=market"
5760
5886
  },
5761
5887
  slTriggerPx: {
5762
5888
  type: "string",
5763
- description: "SL trigger price; places SL at slOrdPx"
5889
+ description: "SL trigger price"
5764
5890
  },
5765
5891
  slOrdPx: {
5766
5892
  type: "string",
5767
- description: "SL order price; -1=market"
5893
+ description: "SL order price, -1=market"
5768
5894
  }
5769
5895
  },
5770
5896
  required: ["instId", "tdMode", "side", "ordType", "sz"]
@@ -5780,6 +5906,7 @@ function registerSpotTradeTools() {
5780
5906
  side: requireString(args, "side"),
5781
5907
  ordType: requireString(args, "ordType"),
5782
5908
  sz: requireString(args, "sz"),
5909
+ tgtCcy: readString(args, "tgtCcy"),
5783
5910
  px: readString(args, "px"),
5784
5911
  clOrdId: readString(args, "clOrdId"),
5785
5912
  tag: context.config.sourceTag,
@@ -5793,7 +5920,7 @@ function registerSpotTradeTools() {
5793
5920
  {
5794
5921
  name: "spot_cancel_order",
5795
5922
  module: "spot",
5796
- description: "Cancel an unfilled spot order by order ID or client order ID.",
5923
+ description: "Cancel an unfilled spot order.",
5797
5924
  isWrite: true,
5798
5925
  inputSchema: {
5799
5926
  type: "object",
@@ -5806,8 +5933,7 @@ function registerSpotTradeTools() {
5806
5933
  type: "string"
5807
5934
  },
5808
5935
  clOrdId: {
5809
- type: "string",
5810
- description: "Client order ID"
5936
+ type: "string"
5811
5937
  }
5812
5938
  },
5813
5939
  required: ["instId"]
@@ -5839,24 +5965,22 @@ function registerSpotTradeTools() {
5839
5965
  description: "e.g. BTC-USDT"
5840
5966
  },
5841
5967
  ordId: {
5842
- type: "string",
5843
- description: "Order ID"
5968
+ type: "string"
5844
5969
  },
5845
5970
  clOrdId: {
5846
- type: "string",
5847
- description: "Client order ID"
5971
+ type: "string"
5848
5972
  },
5849
5973
  newSz: {
5850
5974
  type: "string",
5851
- description: "New order size in base currency (e.g. BTC amount)"
5975
+ description: "New size in base currency"
5852
5976
  },
5853
5977
  newPx: {
5854
5978
  type: "string",
5855
- description: "New order price"
5979
+ description: "New price"
5856
5980
  },
5857
5981
  newClOrdId: {
5858
5982
  type: "string",
5859
- description: "New client order ID after amendment"
5983
+ description: "Replacement client order ID"
5860
5984
  }
5861
5985
  },
5862
5986
  required: ["instId"]
@@ -5881,15 +6005,14 @@ function registerSpotTradeTools() {
5881
6005
  {
5882
6006
  name: "spot_get_orders",
5883
6007
  module: "spot",
5884
- description: "Query spot open orders, order history (last 7 days), or order archive (up to 3 months).",
6008
+ description: "Query spot orders. status: open(active)|history(7d)|archive(3mo).",
5885
6009
  isWrite: false,
5886
6010
  inputSchema: {
5887
6011
  type: "object",
5888
6012
  properties: {
5889
6013
  status: {
5890
6014
  type: "string",
5891
- enum: ["open", "history", "archive"],
5892
- description: "open=active, history=7d, archive=3mo"
6015
+ enum: ["open", "history", "archive"]
5893
6016
  },
5894
6017
  instId: {
5895
6018
  type: "string",
@@ -5905,11 +6028,11 @@ function registerSpotTradeTools() {
5905
6028
  },
5906
6029
  after: {
5907
6030
  type: "string",
5908
- description: "Pagination: before this order ID"
6031
+ description: "Cursor: older than this order ID"
5909
6032
  },
5910
6033
  before: {
5911
6034
  type: "string",
5912
- description: "Pagination: after this order ID"
6035
+ description: "Cursor: newer than this order ID"
5913
6036
  },
5914
6037
  begin: {
5915
6038
  type: "string",
@@ -5950,7 +6073,7 @@ function registerSpotTradeTools() {
5950
6073
  {
5951
6074
  name: "spot_place_algo_order",
5952
6075
  module: "spot",
5953
- description: "Place a spot algo order: TP/SL (conditional/oco) or trailing stop (move_order_stop). For conditional/oco: use tpTriggerPx, tpOrdPx, slTriggerPx, slOrdPx. For move_order_stop: use callbackRatio (e.g. '0.01'=1%) OR callbackSpread, and optionally activePx. [CAUTION] Executes real trades.",
6076
+ description: "Place a spot algo order: TP/SL (conditional/oco) or trailing stop (move_order_stop). [CAUTION] Executes real trades.",
5954
6077
  isWrite: true,
5955
6078
  inputSchema: {
5956
6079
  type: "object",
@@ -5962,7 +6085,7 @@ function registerSpotTradeTools() {
5962
6085
  tdMode: {
5963
6086
  type: "string",
5964
6087
  enum: ["cash", "cross", "isolated"],
5965
- description: "cash=non-margin spot (default); cross/isolated=margin mode"
6088
+ description: "cash(default)=spot, cross/isolated=margin"
5966
6089
  },
5967
6090
  side: {
5968
6091
  type: "string",
@@ -5971,7 +6094,7 @@ function registerSpotTradeTools() {
5971
6094
  ordType: {
5972
6095
  type: "string",
5973
6096
  enum: ["conditional", "oco", "move_order_stop"],
5974
- description: "conditional=single TP/SL; oco=TP+SL pair (one-cancels-other); move_order_stop=trailing stop"
6097
+ description: "conditional=single TP/SL, oco=TP+SL pair, move_order_stop=trailing stop"
5975
6098
  },
5976
6099
  sz: {
5977
6100
  type: "string",
@@ -5983,7 +6106,7 @@ function registerSpotTradeTools() {
5983
6106
  },
5984
6107
  tpOrdPx: {
5985
6108
  type: "string",
5986
- description: "TP order price; -1=market (conditional/oco only)"
6109
+ description: "TP order price, -1=market (conditional/oco only)"
5987
6110
  },
5988
6111
  slTriggerPx: {
5989
6112
  type: "string",
@@ -5991,19 +6114,19 @@ function registerSpotTradeTools() {
5991
6114
  },
5992
6115
  slOrdPx: {
5993
6116
  type: "string",
5994
- description: "SL order price; -1=market (conditional/oco only)"
6117
+ description: "SL order price, -1=market (conditional/oco only)"
5995
6118
  },
5996
6119
  callbackRatio: {
5997
6120
  type: "string",
5998
- description: "Callback ratio (e.g. '0.01'=1%); provide either ratio or spread (move_order_stop only)"
6121
+ description: "Callback ratio e.g. 0.01=1%, use ratio or spread (move_order_stop only)"
5999
6122
  },
6000
6123
  callbackSpread: {
6001
6124
  type: "string",
6002
- description: "Callback spread in price units; provide either ratio or spread (move_order_stop only)"
6125
+ description: "Callback spread in price units, use ratio or spread (move_order_stop only)"
6003
6126
  },
6004
6127
  activePx: {
6005
6128
  type: "string",
6006
- description: "Activation price; tracking starts after market reaches this level (move_order_stop only)"
6129
+ description: "Activation price, trailing starts when market hits this (move_order_stop only)"
6007
6130
  }
6008
6131
  },
6009
6132
  required: ["instId", "side", "ordType", "sz"]
@@ -6041,12 +6164,12 @@ function registerSpotTradeTools() {
6041
6164
  type: "object",
6042
6165
  properties: {
6043
6166
  instId: { type: "string", description: "e.g. BTC-USDT" },
6044
- algoId: { type: "string", description: "Algo order ID" },
6045
- newSz: { type: "string", description: "New order size in base currency (e.g. BTC amount)" },
6167
+ algoId: { type: "string" },
6168
+ newSz: { type: "string", description: "New size in base currency" },
6046
6169
  newTpTriggerPx: { type: "string", description: "New TP trigger price" },
6047
- newTpOrdPx: { type: "string", description: "New TP order price; -1=market" },
6170
+ newTpOrdPx: { type: "string", description: "New TP order price, -1=market" },
6048
6171
  newSlTriggerPx: { type: "string", description: "New SL trigger price" },
6049
- newSlOrdPx: { type: "string", description: "New SL order price; -1=market" }
6172
+ newSlOrdPx: { type: "string", description: "New SL order price, -1=market" }
6050
6173
  },
6051
6174
  required: ["instId", "algoId"]
6052
6175
  },
@@ -6081,8 +6204,7 @@ function registerSpotTradeTools() {
6081
6204
  description: "e.g. BTC-USDT"
6082
6205
  },
6083
6206
  algoId: {
6084
- type: "string",
6085
- description: "Algo order ID"
6207
+ type: "string"
6086
6208
  }
6087
6209
  },
6088
6210
  required: ["instId", "algoId"]
@@ -6113,7 +6235,7 @@ function registerSpotTradeTools() {
6113
6235
  status: {
6114
6236
  type: "string",
6115
6237
  enum: ["pending", "history"],
6116
- description: "pending=active (default); history=completed"
6238
+ description: "pending=active (default), history=completed"
6117
6239
  },
6118
6240
  instId: {
6119
6241
  type: "string",
@@ -6122,15 +6244,15 @@ function registerSpotTradeTools() {
6122
6244
  ordType: {
6123
6245
  type: "string",
6124
6246
  enum: ["conditional", "oco", "move_order_stop"],
6125
- description: "Filter by type; omit for all"
6247
+ description: "Filter by type"
6126
6248
  },
6127
6249
  after: {
6128
6250
  type: "string",
6129
- description: "Pagination: before this algo ID"
6251
+ description: "Cursor: older than this algo ID"
6130
6252
  },
6131
6253
  before: {
6132
6254
  type: "string",
6133
- description: "Pagination: after this algo ID"
6255
+ description: "Cursor: newer than this algo ID"
6134
6256
  },
6135
6257
  limit: {
6136
6258
  type: "number",
@@ -6139,7 +6261,7 @@ function registerSpotTradeTools() {
6139
6261
  state: {
6140
6262
  type: "string",
6141
6263
  enum: ["effective", "canceled", "order_failed"],
6142
- description: "Required when status=history. effective=triggered, canceled, order_failed. Defaults to effective."
6264
+ description: "Required for history. effective=triggered, canceled, order_failed. Default: effective."
6143
6265
  }
6144
6266
  }
6145
6267
  },
@@ -6182,14 +6304,14 @@ function registerSpotTradeTools() {
6182
6304
  {
6183
6305
  name: "spot_get_fills",
6184
6306
  module: "spot",
6185
- description: "Get spot transaction fill details. archive=false (default): last 3 days; archive=true: up to 3 months.",
6307
+ description: "Get spot transaction fills. archive=false(3d, default)|true(up to 3mo).",
6186
6308
  isWrite: false,
6187
6309
  inputSchema: {
6188
6310
  type: "object",
6189
6311
  properties: {
6190
6312
  archive: {
6191
6313
  type: "boolean",
6192
- description: "true=up to 3 months; false=last 3 days (default)"
6314
+ description: "true=up to 3mo, false=3d (default)"
6193
6315
  },
6194
6316
  instId: {
6195
6317
  type: "string",
@@ -6201,11 +6323,11 @@ function registerSpotTradeTools() {
6201
6323
  },
6202
6324
  after: {
6203
6325
  type: "string",
6204
- description: "Pagination: before this bill ID"
6326
+ description: "Cursor: older than this bill ID"
6205
6327
  },
6206
6328
  before: {
6207
6329
  type: "string",
6208
- description: "Pagination: after this bill ID"
6330
+ description: "Cursor: newer than this bill ID"
6209
6331
  },
6210
6332
  begin: {
6211
6333
  type: "string",
@@ -6245,19 +6367,18 @@ function registerSpotTradeTools() {
6245
6367
  {
6246
6368
  name: "spot_batch_orders",
6247
6369
  module: "spot",
6248
- description: "[CAUTION] Batch place/cancel/amend up to 20 spot orders in one request. Use action='place'/'cancel'/'amend'. Private. Rate limit: 60 req/s.",
6370
+ description: "[CAUTION] Batch place/cancel/amend up to 20 spot orders. action: place|cancel|amend.",
6249
6371
  isWrite: true,
6250
6372
  inputSchema: {
6251
6373
  type: "object",
6252
6374
  properties: {
6253
6375
  action: {
6254
6376
  type: "string",
6255
- enum: ["place", "cancel", "amend"],
6256
- description: "place|cancel|amend"
6377
+ enum: ["place", "cancel", "amend"]
6257
6378
  },
6258
6379
  orders: {
6259
6380
  type: "array",
6260
- description: "Array (max 20). place: {instId,side,ordType,sz,tdMode?,px?,clOrdId?,tpTriggerPx?,tpOrdPx?,slTriggerPx?,slOrdPx?} (tdMode defaults to cash for non-margin accounts; use cross for unified/margin accounts). cancel: {instId,ordId|clOrdId}. amend: {instId,ordId|clOrdId,newSz?,newPx?}.",
6381
+ description: "Array (max 20). place: {instId,side,ordType,sz,tdMode(default cash; use cross for unified/margin accounts),px?,clOrdId?,tpTriggerPx?,tpOrdPx?,slTriggerPx?,slOrdPx?}. cancel: {instId,ordId|clOrdId}. amend: {instId,ordId|clOrdId,newSz?,newPx?}.",
6261
6382
  items: {
6262
6383
  type: "object"
6263
6384
  }
@@ -6304,7 +6425,7 @@ function registerSpotTradeTools() {
6304
6425
  {
6305
6426
  name: "spot_get_order",
6306
6427
  module: "spot",
6307
- description: "Get details of a single spot order by order ID or client order ID.",
6428
+ description: "Get details of a single spot order.",
6308
6429
  isWrite: false,
6309
6430
  inputSchema: {
6310
6431
  type: "object",
@@ -6341,7 +6462,7 @@ function registerSpotTradeTools() {
6341
6462
  {
6342
6463
  name: "spot_batch_amend",
6343
6464
  module: "spot",
6344
- description: "[CAUTION] Batch amend up to 20 unfilled spot orders in one request. Modify price and/or size per order.",
6465
+ description: "[CAUTION] Batch amend up to 20 unfilled spot orders.",
6345
6466
  isWrite: true,
6346
6467
  inputSchema: {
6347
6468
  type: "object",
@@ -6371,7 +6492,7 @@ function registerSpotTradeTools() {
6371
6492
  {
6372
6493
  name: "spot_batch_cancel",
6373
6494
  module: "spot",
6374
- description: "[CAUTION] Batch cancel up to 20 spot orders in one request. Provide instId plus ordId or clOrdId for each order.",
6495
+ description: "[CAUTION] Batch cancel up to 20 spot orders.",
6375
6496
  isWrite: true,
6376
6497
  inputSchema: {
6377
6498
  type: "object",
@@ -6514,6 +6635,7 @@ function registerSwapTradeTools() {
6514
6635
  function allToolSpecs() {
6515
6636
  return [
6516
6637
  ...registerMarketTools(),
6638
+ ...registerIndicatorTools(),
6517
6639
  ...registerSpotTradeTools(),
6518
6640
  ...registerSwapTradeTools(),
6519
6641
  ...registerFuturesTools(),
@@ -6545,20 +6667,38 @@ function readFullConfig() {
6545
6667
  const path42 = configFilePath();
6546
6668
  if (!existsSync(path42)) return { profiles: {} };
6547
6669
  const raw = readFileSync(path42, "utf-8");
6548
- return parse(raw);
6670
+ try {
6671
+ return parse(raw);
6672
+ } catch (err) {
6673
+ throw new ConfigError(
6674
+ `Failed to parse ${path42}: ${err instanceof Error ? err.message : String(err)}`,
6675
+ `If your passphrase or keys contain special characters:
6676
+ - Contains # \\ " \u2192 use single quotes: passphrase = 'your#pass'
6677
+ - Contains ' \u2192 use double quotes: passphrase = "your'pass"
6678
+ - Contains both \u2192 use triple quotes: passphrase = '''your'#pass'''
6679
+ Or re-run: okx config init`
6680
+ );
6681
+ }
6549
6682
  }
6550
6683
  function readTomlProfile(profileName) {
6551
6684
  const config = readFullConfig();
6552
6685
  const name = profileName ?? config.default_profile ?? "default";
6553
6686
  return config.profiles?.[name] ?? {};
6554
6687
  }
6688
+ var CONFIG_HEADER = `# OKX Trade Kit Configuration
6689
+ # If editing manually, wrap values containing special chars in quotes:
6690
+ # passphrase = 'value' (if value contains # \\ ")
6691
+ # passphrase = "value" (if value contains ')
6692
+ # passphrase = '''value''' (if value contains both)
6693
+
6694
+ `;
6555
6695
  function writeFullConfig(config) {
6556
6696
  const path42 = configFilePath();
6557
6697
  const dir = dirname(path42);
6558
6698
  if (!existsSync(dir)) {
6559
6699
  mkdirSync(dir, { recursive: true });
6560
6700
  }
6561
- writeFileSync(path42, stringify(config), "utf-8");
6701
+ writeFileSync(path42, CONFIG_HEADER + stringify(config), "utf-8");
6562
6702
  }
6563
6703
  function expandShorthand(moduleId) {
6564
6704
  if (moduleId === "all") return [...MODULES];
@@ -6883,6 +7023,73 @@ import tls from "tls";
6883
7023
  // src/commands/diagnose-utils.ts
6884
7024
  import fs2 from "fs";
6885
7025
  import { createRequire } from "module";
7026
+
7027
+ // src/formatter.ts
7028
+ import { EOL } from "os";
7029
+ var stdioOutput = {
7030
+ out: (message) => process.stdout.write(message),
7031
+ err: (message) => process.stderr.write(message)
7032
+ };
7033
+ var activeOutput = stdioOutput;
7034
+ function setOutput(impl) {
7035
+ activeOutput = impl;
7036
+ }
7037
+ function output(message) {
7038
+ activeOutput.out(message);
7039
+ }
7040
+ function errorOutput(message) {
7041
+ activeOutput.err(message);
7042
+ }
7043
+ function outputLine(message) {
7044
+ activeOutput.out(message + EOL);
7045
+ }
7046
+ function errorLine(message) {
7047
+ activeOutput.err(message + EOL);
7048
+ }
7049
+ function printJson(data) {
7050
+ activeOutput.out(JSON.stringify(data, null, 2) + EOL);
7051
+ }
7052
+ function printTable(rows) {
7053
+ if (rows.length === 0) {
7054
+ activeOutput.out("(no data)" + EOL);
7055
+ return;
7056
+ }
7057
+ const keys = Object.keys(rows[0]);
7058
+ const widths = keys.map(
7059
+ (k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
7060
+ );
7061
+ const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
7062
+ const divider = widths.map((w) => "-".repeat(w)).join(" ");
7063
+ activeOutput.out(header + EOL + divider + EOL);
7064
+ for (const row of rows) {
7065
+ activeOutput.out(keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" ") + EOL);
7066
+ }
7067
+ }
7068
+ function printKv(obj, indent = 0) {
7069
+ const pad = " ".repeat(indent);
7070
+ for (const [k, v] of Object.entries(obj)) {
7071
+ if (v !== null && typeof v === "object" && !Array.isArray(v)) {
7072
+ activeOutput.out(`${pad}${k}:${EOL}`);
7073
+ printKv(v, indent + 2);
7074
+ } else {
7075
+ activeOutput.out(`${pad}${k.padEnd(20 - indent)} ${v}${EOL}`);
7076
+ }
7077
+ }
7078
+ }
7079
+ function markFailedIfSCodeError(data) {
7080
+ if (!Array.isArray(data)) return;
7081
+ for (const item of data) {
7082
+ if (item !== null && typeof item === "object") {
7083
+ const sCode = item["sCode"];
7084
+ if (sCode !== void 0 && sCode !== "0" && sCode !== 0) {
7085
+ process.exitCode = 1;
7086
+ return;
7087
+ }
7088
+ }
7089
+ }
7090
+ }
7091
+
7092
+ // src/commands/diagnose-utils.ts
6886
7093
  var _require = createRequire(import.meta.url);
6887
7094
  function readCliVersion() {
6888
7095
  for (const rel of ["../package.json", "../../package.json"]) {
@@ -6899,18 +7106,14 @@ var Report = class {
6899
7106
  this.lines.push({ key, value });
6900
7107
  }
6901
7108
  print() {
6902
- const w = process.stdout.write.bind(process.stdout);
6903
7109
  const sep = "\u2500".repeat(52);
6904
- w(`
6905
- \u2500\u2500 Diagnostic Report (copy & share) ${sep.slice(35)}
6906
- `);
7110
+ outputLine("");
7111
+ outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep.slice(35)}`);
6907
7112
  for (const { key, value } of this.lines) {
6908
- w(` ${key.padEnd(14)} ${value}
6909
- `);
7113
+ outputLine(` ${key.padEnd(14)} ${value}`);
6910
7114
  }
6911
- w(` ${sep}
6912
-
6913
- `);
7115
+ outputLine(` ${sep}`);
7116
+ outputLine("");
6914
7117
  }
6915
7118
  /** Write report to a file path, returns true on success. */
6916
7119
  writeToFile(filePath) {
@@ -6931,31 +7134,25 @@ var Report = class {
6931
7134
  }
6932
7135
  };
6933
7136
  function ok(label, detail) {
6934
- process.stdout.write(` \u2713 ${label.padEnd(14)} ${detail}
6935
- `);
7137
+ outputLine(` \u2713 ${label.padEnd(14)} ${detail}`);
6936
7138
  }
6937
7139
  function fail(label, detail, hints) {
6938
- process.stdout.write(` \u2717 ${label.padEnd(14)} ${detail}
6939
- `);
7140
+ outputLine(` \u2717 ${label.padEnd(14)} ${detail}`);
6940
7141
  for (const hint of hints) {
6941
- process.stdout.write(` \u2192 ${hint}
6942
- `);
7142
+ outputLine(` \u2192 ${hint}`);
6943
7143
  }
6944
7144
  }
6945
7145
  function section(title) {
6946
- process.stdout.write(`
6947
- ${title}
6948
- `);
7146
+ outputLine("");
7147
+ outputLine(` ${title}`);
6949
7148
  }
6950
7149
  function writeReportIfRequested(report, outputPath) {
6951
7150
  if (!outputPath) return;
6952
7151
  const written = report.writeToFile(outputPath);
6953
7152
  if (written) {
6954
- process.stdout.write(` Report saved to: ${outputPath}
6955
- `);
7153
+ outputLine(` Report saved to: ${outputPath}`);
6956
7154
  } else {
6957
- process.stderr.write(` Warning: failed to write report to: ${outputPath}
6958
- `);
7155
+ errorLine(` Warning: failed to write report to: ${outputPath}`);
6959
7156
  }
6960
7157
  }
6961
7158
  function sanitize(value) {
@@ -7155,8 +7352,7 @@ function checkMcpLogs(report) {
7155
7352
  report.add("mcp_log", logPath);
7156
7353
  if (lines.length > 0) {
7157
7354
  ok("last lines", `(${lines.length} shown)`);
7158
- for (const line of lines) process.stdout.write(` ${sanitize(line)}
7159
- `);
7355
+ for (const line of lines) outputLine(` ${sanitize(line)}`);
7160
7356
  } else {
7161
7357
  ok("last lines", "(empty log)");
7162
7358
  }
@@ -7291,8 +7487,9 @@ function checkModuleLoading(entryPath, report) {
7291
7487
  }
7292
7488
  }
7293
7489
  async function cmdDiagnoseMcp(options = {}) {
7294
- process.stdout.write("\n OKX MCP Server Diagnostics\n");
7295
- 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");
7490
+ outputLine("");
7491
+ outputLine(" OKX MCP Server Diagnostics");
7492
+ outputLine(" \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");
7296
7493
  const report = new Report();
7297
7494
  report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
7298
7495
  report.add("mode", "mcp");
@@ -7313,11 +7510,11 @@ async function cmdDiagnoseMcp(options = {}) {
7313
7510
  handshakePassed = true;
7314
7511
  }
7315
7512
  const allPassed = nodePassed && entryPassed && cfgPassed && moduleLoadPassed && handshakePassed;
7316
- process.stdout.write("\n");
7513
+ outputLine("");
7317
7514
  if (allPassed) {
7318
- process.stdout.write(" Result: All checks passed \u2713\n");
7515
+ outputLine(" Result: All checks passed \u2713");
7319
7516
  } else {
7320
- process.stdout.write(" Result: Some checks failed \u2717\n");
7517
+ outputLine(" Result: Some checks failed \u2717");
7321
7518
  process.exitCode = 1;
7322
7519
  }
7323
7520
  report.add("result", allPassed ? "PASS" : "FAIL");
@@ -7327,7 +7524,7 @@ async function cmdDiagnoseMcp(options = {}) {
7327
7524
 
7328
7525
  // src/commands/diagnose.ts
7329
7526
  var CLI_VERSION = readCliVersion();
7330
- var GIT_HASH = true ? "3157372" : "dev";
7527
+ var GIT_HASH = true ? "c27439d" : "dev";
7331
7528
  function maskKey2(key) {
7332
7529
  if (!key) return "(not set)";
7333
7530
  if (key.length <= 8) return "****";
@@ -7569,22 +7766,52 @@ async function cmdDiagnose(config, profile, options = {}) {
7569
7766
  }
7570
7767
  return runCliChecks(config, profile, options.output);
7571
7768
  }
7769
+ function checkConfigFile(report) {
7770
+ section("Config File");
7771
+ const path5 = configFilePath();
7772
+ try {
7773
+ readFullConfig();
7774
+ ok("Config parse", `${path5} OK`);
7775
+ report.add("config_parse", "OK");
7776
+ return true;
7777
+ } catch (e) {
7778
+ const msg = e instanceof Error ? e.message : String(e);
7779
+ fail("Config parse", msg, [
7780
+ `If passphrase contains special characters (# \\ " '), wrap in quotes:`,
7781
+ ` Contains # \\ " \u2192 passphrase = 'value'`,
7782
+ ` Contains ' \u2192 passphrase = "value"`,
7783
+ " Contains both \u2192 passphrase = '''value'''",
7784
+ "Or re-run: okx config init"
7785
+ ]);
7786
+ report.add("config_parse", `FAIL ${msg}`);
7787
+ return false;
7788
+ }
7789
+ }
7572
7790
  async function runCliChecks(config, profile, outputPath) {
7573
7791
  process.stdout.write("\n OKX Trade CLI Diagnostics\n");
7574
7792
  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");
7575
7793
  const report = new Report();
7576
7794
  report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
7795
+ const configFilePassed = checkConfigFile(report);
7577
7796
  const envPassed = checkEnvironment(report);
7797
+ if (!config) {
7798
+ fail("Config", "Could not load config (see Config File check above)", []);
7799
+ report.add("result", "FAIL");
7800
+ report.print();
7801
+ writeReportIfRequested(report, outputPath);
7802
+ process.exitCode = 1;
7803
+ return;
7804
+ }
7578
7805
  const cfgPassed = checkConfig(config, profile, report);
7579
7806
  const client = new OkxRestClient(config);
7580
7807
  const netPassed = await checkNetwork(config, client, report);
7581
7808
  const authPassed = await checkAuth(client, config, report);
7582
- const allPassed = envPassed && cfgPassed && netPassed && authPassed;
7583
- process.stdout.write("\n");
7809
+ const allPassed = configFilePassed && envPassed && cfgPassed && netPassed && authPassed;
7810
+ outputLine("");
7584
7811
  if (allPassed) {
7585
- process.stdout.write(" Result: All checks passed \u2713\n");
7812
+ outputLine(" Result: All checks passed \u2713");
7586
7813
  } else {
7587
- process.stdout.write(" Result: Some checks failed \u2717\n");
7814
+ outputLine(" Result: Some checks failed \u2717");
7588
7815
  process.exitCode = 1;
7589
7816
  }
7590
7817
  report.add("result", allPassed ? "PASS" : "FAIL");
@@ -7606,6 +7833,9 @@ function loadProfileConfig(opts) {
7606
7833
  });
7607
7834
  }
7608
7835
 
7836
+ // src/help.ts
7837
+ import { EOL as EOL2 } from "os";
7838
+
7609
7839
  // src/commands/client-setup.ts
7610
7840
  import * as fs5 from "fs";
7611
7841
  var DETECTABLE_CLIENTS = ["claude-desktop", "cursor", "windsurf"];
@@ -7621,14 +7851,11 @@ function cmdSetupClients() {
7621
7851
  }
7622
7852
  }
7623
7853
  if (detected.length > 0) {
7624
- process.stdout.write(`Detected clients:
7625
- `);
7854
+ outputLine("Detected clients:");
7626
7855
  for (const { id, path: path5 } of detected) {
7627
- process.stdout.write(` ${id.padEnd(16)} ${path5}
7628
- `);
7856
+ outputLine(` ${id.padEnd(16)} ${path5}`);
7629
7857
  }
7630
- process.stdout.write(`
7631
- `);
7858
+ outputLine("");
7632
7859
  }
7633
7860
  printSetupUsage();
7634
7861
  }
@@ -8203,13 +8430,12 @@ function printGlobalHelp() {
8203
8430
  lines.push(` ${name.padEnd(colWidth)}${group.description}`);
8204
8431
  }
8205
8432
  lines.push("", 'Run "okx <module> --help" for module details.', "");
8206
- process.stdout.write(lines.join("\n"));
8433
+ output(lines.join(EOL2));
8207
8434
  }
8208
8435
  function printModuleHelp(moduleName) {
8209
8436
  const group = HELP_TREE[moduleName];
8210
8437
  if (!group) {
8211
- process.stderr.write(`Unknown module: ${moduleName}
8212
- `);
8438
+ errorLine(`Unknown module: ${moduleName}`);
8213
8439
  process.exitCode = 1;
8214
8440
  return;
8215
8441
  }
@@ -8253,20 +8479,18 @@ function printModuleHelp(moduleName) {
8253
8479
  }
8254
8480
  }
8255
8481
  lines.push("");
8256
- process.stdout.write(lines.join("\n"));
8482
+ output(lines.join(EOL2));
8257
8483
  }
8258
8484
  function printSubgroupHelp(moduleName, subgroupName) {
8259
8485
  const group = HELP_TREE[moduleName];
8260
8486
  if (!group) {
8261
- process.stderr.write(`Unknown module: ${moduleName}
8262
- `);
8487
+ errorLine(`Unknown module: ${moduleName}`);
8263
8488
  process.exitCode = 1;
8264
8489
  return;
8265
8490
  }
8266
8491
  const subgroup = group.subgroups?.[subgroupName];
8267
8492
  if (!subgroup) {
8268
- process.stderr.write(`Unknown subgroup: ${moduleName} ${subgroupName}
8269
- `);
8493
+ errorLine(`Unknown subgroup: ${moduleName} ${subgroupName}`);
8270
8494
  process.exitCode = 1;
8271
8495
  return;
8272
8496
  }
@@ -8282,7 +8506,7 @@ function printSubgroupHelp(moduleName, subgroupName) {
8282
8506
  printCommandList(lines, subgroup.commands);
8283
8507
  }
8284
8508
  lines.push("");
8285
- process.stdout.write(lines.join("\n"));
8509
+ output(lines.join(EOL2));
8286
8510
  }
8287
8511
  function printCommandList(lines, commands) {
8288
8512
  const names = Object.keys(commands);
@@ -8333,6 +8557,7 @@ var CLI_OPTIONS = {
8333
8557
  px: { type: "string" },
8334
8558
  posSide: { type: "string" },
8335
8559
  tdMode: { type: "string" },
8560
+ tgtCcy: { type: "string" },
8336
8561
  // leverage
8337
8562
  lever: { type: "string" },
8338
8563
  mgnMode: { type: "string" },
@@ -8433,6 +8658,10 @@ var CLI_OPTIONS = {
8433
8658
  maxTermDays: { type: "string" },
8434
8659
  expDate: { type: "string" },
8435
8660
  minAnnualizedYield: { type: "string" },
8661
+ // indicator
8662
+ params: { type: "string" },
8663
+ list: { type: "boolean", default: false },
8664
+ "backtest-time": { type: "string" },
8436
8665
  // diagnostics — cli/mcp/all/output are diagnose-specific; verbose is shared
8437
8666
  verbose: { type: "boolean", default: false },
8438
8667
  mcp: { type: "boolean", default: false },
@@ -8467,42 +8696,6 @@ function parseCli(argv) {
8467
8696
  return { values, positionals };
8468
8697
  }
8469
8698
 
8470
- // src/formatter.ts
8471
- function printJson(data) {
8472
- process.stdout.write(JSON.stringify(data, null, 2) + "\n");
8473
- }
8474
- function printTable(rows) {
8475
- if (rows.length === 0) {
8476
- process.stdout.write("(no data)\n");
8477
- return;
8478
- }
8479
- const keys = Object.keys(rows[0]);
8480
- const widths = keys.map(
8481
- (k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
8482
- );
8483
- const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
8484
- const divider = widths.map((w) => "-".repeat(w)).join(" ");
8485
- process.stdout.write(header + "\n" + divider + "\n");
8486
- for (const row of rows) {
8487
- process.stdout.write(
8488
- keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" ") + "\n"
8489
- );
8490
- }
8491
- }
8492
- function printKv(obj, indent = 0) {
8493
- const pad = " ".repeat(indent);
8494
- for (const [k, v] of Object.entries(obj)) {
8495
- if (v !== null && typeof v === "object" && !Array.isArray(v)) {
8496
- process.stdout.write(`${pad}${k}:
8497
- `);
8498
- printKv(v, indent + 2);
8499
- } else {
8500
- process.stdout.write(`${pad}${k.padEnd(20 - indent)} ${v}
8501
- `);
8502
- }
8503
- }
8504
- }
8505
-
8506
8699
  // src/commands/market.ts
8507
8700
  function getData(result) {
8508
8701
  return result.data;
@@ -8538,7 +8731,7 @@ async function cmdMarketFundingRate(run, instId, opts) {
8538
8731
  } else {
8539
8732
  const r = items?.[0];
8540
8733
  if (!r) {
8541
- process.stdout.write("No data\n");
8734
+ outputLine("No data");
8542
8735
  return;
8543
8736
  }
8544
8737
  printKv({
@@ -8611,7 +8804,7 @@ async function cmdMarketPriceLimit(run, instId, json) {
8611
8804
  if (json) return printJson(items);
8612
8805
  const r = items?.[0];
8613
8806
  if (!r) {
8614
- process.stdout.write("No data\n");
8807
+ outputLine("No data");
8615
8808
  return;
8616
8809
  }
8617
8810
  printKv({
@@ -8639,7 +8832,7 @@ async function cmdMarketTicker(run, instId, json) {
8639
8832
  const items = getData(result);
8640
8833
  if (json) return printJson(items);
8641
8834
  if (!items?.length) {
8642
- process.stdout.write("No data\n");
8835
+ outputLine("No data");
8643
8836
  return;
8644
8837
  }
8645
8838
  const t = items[0];
@@ -8673,17 +8866,16 @@ async function cmdMarketOrderbook(run, instId, sz, json) {
8673
8866
  if (json) return printJson(data);
8674
8867
  const book = data[0];
8675
8868
  if (!book) {
8676
- process.stdout.write("No data\n");
8869
+ outputLine("No data");
8677
8870
  return;
8678
8871
  }
8679
8872
  const asks = book["asks"].slice(0, 5);
8680
8873
  const bids = book["bids"].slice(0, 5);
8681
- process.stdout.write("Asks (price / size):\n");
8682
- for (const [p, s] of asks.reverse()) process.stdout.write(` ${p.padStart(16)} ${s}
8683
- `);
8684
- process.stdout.write("Bids (price / size):\n");
8685
- for (const [p, s] of bids) process.stdout.write(` ${p.padStart(16)} ${s}
8686
- `);
8874
+ outputLine("Asks (price / size):");
8875
+ asks.reverse();
8876
+ for (const [p, s] of asks) outputLine(` ${p.padStart(16)} ${s}`);
8877
+ outputLine("Bids (price / size):");
8878
+ for (const [p, s] of bids) outputLine(` ${p.padStart(16)} ${s}`);
8687
8879
  }
8688
8880
  async function cmdMarketCandles(run, instId, opts) {
8689
8881
  const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit });
@@ -8700,6 +8892,54 @@ async function cmdMarketCandles(run, instId, opts) {
8700
8892
  }))
8701
8893
  );
8702
8894
  }
8895
+ async function cmdMarketIndicator(run, indicator, instId, opts) {
8896
+ const params = opts.params ? opts.params.split(",").map((p) => Number(p.trim())).filter((n) => !Number.isNaN(n)) : void 0;
8897
+ const result = await run("market_get_indicator", {
8898
+ instId,
8899
+ indicator,
8900
+ bar: opts.bar,
8901
+ params: params && params.length > 0 ? params : void 0,
8902
+ returnList: opts.list ?? false,
8903
+ limit: opts.limit,
8904
+ backtestTime: opts.backtestTime
8905
+ });
8906
+ const outerArray = getData(result);
8907
+ if (opts.json) return printJson(outerArray);
8908
+ if (!outerArray?.length) {
8909
+ process.stdout.write("No data\n");
8910
+ return;
8911
+ }
8912
+ const apiCode = resolveIndicatorCode(indicator);
8913
+ const response = outerArray[0];
8914
+ const innerArray = response["data"];
8915
+ const instData = innerArray?.[0];
8916
+ const timeframes = instData?.["timeframes"];
8917
+ if (!timeframes) {
8918
+ process.stdout.write(JSON.stringify(outerArray, null, 2) + "\n");
8919
+ return;
8920
+ }
8921
+ for (const [tf, tfData] of Object.entries(timeframes)) {
8922
+ const indicators = tfData?.["indicators"];
8923
+ const values = indicators?.[apiCode];
8924
+ if (!values?.length) continue;
8925
+ process.stdout.write(`${instId} \xB7 ${apiCode} \xB7 ${tf}
8926
+ `);
8927
+ process.stdout.write("\u2500".repeat(40) + "\n");
8928
+ if (opts.list) {
8929
+ const tableRows = values.map((entry) => ({
8930
+ ts: new Date(Number(entry["ts"])).toLocaleString(),
8931
+ ...entry["values"]
8932
+ }));
8933
+ printTable(tableRows);
8934
+ } else {
8935
+ const latest = values[0];
8936
+ printKv({
8937
+ ts: new Date(Number(latest["ts"])).toLocaleString(),
8938
+ ...latest["values"]
8939
+ });
8940
+ }
8941
+ }
8942
+ }
8703
8943
  async function cmdMarketStockTokens(run, opts) {
8704
8944
  const result = await run("market_get_stock_tokens", { instType: opts.instType, instId: opts.instId });
8705
8945
  const items = getData(result);
@@ -8757,7 +8997,7 @@ async function cmdAccountPositions(run, opts) {
8757
8997
  if (opts.json) return printJson(positions);
8758
8998
  const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
8759
8999
  if (!open.length) {
8760
- process.stdout.write("No open positions\n");
9000
+ outputLine("No open positions");
8761
9001
  return;
8762
9002
  }
8763
9003
  printTable(
@@ -8795,7 +9035,7 @@ async function cmdAccountFees(run, opts) {
8795
9035
  if (opts.json) return printJson(data);
8796
9036
  const fee = data?.[0];
8797
9037
  if (!fee) {
8798
- process.stdout.write("No data\n");
9038
+ outputLine("No data");
8799
9039
  return;
8800
9040
  }
8801
9041
  printKv({
@@ -8813,7 +9053,7 @@ async function cmdAccountConfig(run, json) {
8813
9053
  if (json) return printJson(data);
8814
9054
  const cfg = data?.[0];
8815
9055
  if (!cfg) {
8816
- process.stdout.write("No data\n");
9056
+ outputLine("No data");
8817
9057
  return;
8818
9058
  }
8819
9059
  printKv({
@@ -8831,8 +9071,7 @@ async function cmdAccountSetPositionMode(run, posMode, json) {
8831
9071
  const data = getData2(result);
8832
9072
  if (json) return printJson(data);
8833
9073
  const r = data?.[0];
8834
- process.stdout.write(`Position mode set: ${r?.["posMode"]}
8835
- `);
9074
+ outputLine(`Position mode set: ${r?.["posMode"]}`);
8836
9075
  }
8837
9076
  async function cmdAccountMaxSize(run, opts) {
8838
9077
  const result = await run("account_get_max_size", { instId: opts.instId, tdMode: opts.tdMode, px: opts.px });
@@ -8840,7 +9079,7 @@ async function cmdAccountMaxSize(run, opts) {
8840
9079
  if (opts.json) return printJson(data);
8841
9080
  const r = data?.[0];
8842
9081
  if (!r) {
8843
- process.stdout.write("No data\n");
9082
+ outputLine("No data");
8844
9083
  return;
8845
9084
  }
8846
9085
  printKv({ instId: r["instId"], maxBuy: r["maxBuy"], maxSell: r["maxSell"] });
@@ -8851,7 +9090,7 @@ async function cmdAccountMaxAvailSize(run, opts) {
8851
9090
  if (opts.json) return printJson(data);
8852
9091
  const r = data?.[0];
8853
9092
  if (!r) {
8854
- process.stdout.write("No data\n");
9093
+ outputLine("No data");
8855
9094
  return;
8856
9095
  }
8857
9096
  printKv({ instId: r["instId"], availBuy: r["availBuy"], availSell: r["availSell"] });
@@ -8895,8 +9134,7 @@ async function cmdAccountTransfer(run, opts) {
8895
9134
  const data = getData2(result);
8896
9135
  if (opts.json) return printJson(data);
8897
9136
  const r = data?.[0];
8898
- process.stdout.write(`Transfer: ${r?.["transId"]} (${r?.["ccy"]} ${r?.["amt"]})
8899
- `);
9137
+ outputLine(`Transfer: ${r?.["transId"]} (${r?.["ccy"]} ${r?.["amt"]})`);
8900
9138
  }
8901
9139
  function readAuditLogs(logDir, days = 7) {
8902
9140
  const entries = [];
@@ -8938,7 +9176,7 @@ function cmdAccountAudit(opts) {
8938
9176
  entries = entries.slice(0, limit);
8939
9177
  if (opts.json) return printJson(entries);
8940
9178
  if (!entries.length) {
8941
- process.stdout.write("No audit log entries\n");
9179
+ outputLine("No audit log entries");
8942
9180
  return;
8943
9181
  }
8944
9182
  printTable(
@@ -8956,6 +9194,25 @@ function cmdAccountAudit(opts) {
8956
9194
  function getData3(result) {
8957
9195
  return result.data;
8958
9196
  }
9197
+ function emitWriteResult(item, label, idKey) {
9198
+ const isError = item?.["sCode"] !== "0" && item?.["sCode"] !== 0;
9199
+ if (isError) {
9200
+ errorLine(`Error: ${item?.["sMsg"]} (sCode ${item?.["sCode"]})`);
9201
+ } else {
9202
+ outputLine(`${label}: ${item?.[idKey]} (OK)`);
9203
+ }
9204
+ }
9205
+ function emitBatchResults(items) {
9206
+ for (const r of items) {
9207
+ const isError = r["sCode"] !== "0" && r["sCode"] !== 0;
9208
+ const id = r["ordId"] ?? r["clOrdId"] ?? "?";
9209
+ if (isError) {
9210
+ errorLine(`${id}: ${r["sMsg"]} (sCode ${r["sCode"]})`);
9211
+ } else {
9212
+ outputLine(`${id}: OK`);
9213
+ }
9214
+ }
9215
+ }
8959
9216
  async function cmdSpotOrders(run, opts) {
8960
9217
  const result = await run("spot_get_orders", { instId: opts.instId, status: opts.status });
8961
9218
  const orders = getData3(result);
@@ -8980,6 +9237,7 @@ async function cmdSpotPlace(run, opts) {
8980
9237
  side: opts.side,
8981
9238
  ordType: opts.ordType,
8982
9239
  sz: opts.sz,
9240
+ tgtCcy: opts.tgtCcy,
8983
9241
  px: opts.px,
8984
9242
  tpTriggerPx: opts.tpTriggerPx,
8985
9243
  tpOrdPx: opts.tpOrdPx,
@@ -8988,9 +9246,7 @@ async function cmdSpotPlace(run, opts) {
8988
9246
  });
8989
9247
  const data = getData3(result);
8990
9248
  if (opts.json) return printJson(data);
8991
- const order = data?.[0];
8992
- process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
8993
- `);
9249
+ emitWriteResult(data?.[0], "Order placed", "ordId");
8994
9250
  }
8995
9251
  async function cmdSpotCancel(run, opts) {
8996
9252
  const { instId, ordId, clOrdId, json } = opts;
@@ -8998,9 +9254,7 @@ async function cmdSpotCancel(run, opts) {
8998
9254
  const result = await run("spot_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
8999
9255
  const data = getData3(result);
9000
9256
  if (json) return printJson(data);
9001
- const r = data?.[0];
9002
- process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9003
- `);
9257
+ emitWriteResult(data?.[0], "Cancelled", "ordId");
9004
9258
  }
9005
9259
  async function cmdSpotAlgoPlace(run, opts) {
9006
9260
  const result = await run("spot_place_algo_order", {
@@ -9019,11 +9273,7 @@ async function cmdSpotAlgoPlace(run, opts) {
9019
9273
  });
9020
9274
  const data = getData3(result);
9021
9275
  if (opts.json) return printJson(data);
9022
- const order = data?.[0];
9023
- process.stdout.write(
9024
- `Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9025
- `
9026
- );
9276
+ emitWriteResult(data?.[0], "Algo order placed", "algoId");
9027
9277
  }
9028
9278
  async function cmdSpotAlgoAmend(run, opts) {
9029
9279
  const result = await run("spot_amend_algo_order", {
@@ -9037,21 +9287,13 @@ async function cmdSpotAlgoAmend(run, opts) {
9037
9287
  });
9038
9288
  const data = getData3(result);
9039
9289
  if (opts.json) return printJson(data);
9040
- const r = data?.[0];
9041
- process.stdout.write(
9042
- `Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9043
- `
9044
- );
9290
+ emitWriteResult(data?.[0], "Algo order amended", "algoId");
9045
9291
  }
9046
9292
  async function cmdSpotAlgoCancel(run, instId, algoId, json) {
9047
9293
  const result = await run("spot_cancel_algo_order", { instId, algoId });
9048
9294
  const data = getData3(result);
9049
9295
  if (json) return printJson(data);
9050
- const r = data?.[0];
9051
- process.stdout.write(
9052
- `Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9053
- `
9054
- );
9296
+ emitWriteResult(data?.[0], "Algo order cancelled", "algoId");
9055
9297
  }
9056
9298
  async function cmdSpotGet(run, opts) {
9057
9299
  const result = await run("spot_get_order", { instId: opts.instId, ordId: opts.ordId, clOrdId: opts.clOrdId });
@@ -9059,7 +9301,7 @@ async function cmdSpotGet(run, opts) {
9059
9301
  if (opts.json) return printJson(data);
9060
9302
  const o = data?.[0];
9061
9303
  if (!o) {
9062
- process.stdout.write("No data\n");
9304
+ outputLine("No data");
9063
9305
  return;
9064
9306
  }
9065
9307
  printKv({
@@ -9085,9 +9327,7 @@ async function cmdSpotAmend(run, opts) {
9085
9327
  });
9086
9328
  const data = getData3(result);
9087
9329
  if (opts.json) return printJson(data);
9088
- const r = data?.[0];
9089
- process.stdout.write(`Order amended: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9090
- `);
9330
+ emitWriteResult(data?.[0], "Order amended", "ordId");
9091
9331
  }
9092
9332
  async function cmdSpotAlgoOrders(run, opts) {
9093
9333
  const result = await run("spot_get_algo_orders", {
@@ -9098,7 +9338,7 @@ async function cmdSpotAlgoOrders(run, opts) {
9098
9338
  const orders = getData3(result);
9099
9339
  if (opts.json) return printJson(orders);
9100
9340
  if (!(orders ?? []).length) {
9101
- process.stdout.write("No algo orders\n");
9341
+ outputLine("No algo orders");
9102
9342
  return;
9103
9343
  }
9104
9344
  printTable(
@@ -9142,23 +9382,19 @@ async function cmdSpotAlgoTrailPlace(run, opts) {
9142
9382
  });
9143
9383
  const data = getData3(result);
9144
9384
  if (opts.json) return printJson(data);
9145
- const order = data?.[0];
9146
- process.stdout.write(
9147
- `Trailing stop placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9148
- `
9149
- );
9385
+ emitWriteResult(data?.[0], "Trailing stop placed", "algoId");
9150
9386
  }
9151
9387
  async function cmdSpotBatch(run, opts) {
9152
9388
  let parsed;
9153
9389
  try {
9154
9390
  parsed = JSON.parse(opts.orders);
9155
9391
  } catch {
9156
- process.stderr.write("Error: --orders must be a valid JSON array\n");
9392
+ errorLine("Error: --orders must be a valid JSON array");
9157
9393
  process.exitCode = 1;
9158
9394
  return;
9159
9395
  }
9160
9396
  if (!Array.isArray(parsed) || parsed.length === 0) {
9161
- process.stderr.write("Error: --orders must be a non-empty JSON array\n");
9397
+ errorLine("Error: --orders must be a non-empty JSON array");
9162
9398
  process.exitCode = 1;
9163
9399
  return;
9164
9400
  }
@@ -9169,31 +9405,46 @@ async function cmdSpotBatch(run, opts) {
9169
9405
  };
9170
9406
  const tool = toolMap[opts.action];
9171
9407
  if (!tool) {
9172
- process.stderr.write(`Error: --action must be one of: place, amend, cancel
9173
- `);
9408
+ errorLine("Error: --action must be one of: place, amend, cancel");
9174
9409
  process.exitCode = 1;
9175
9410
  return;
9176
9411
  }
9177
9412
  const result = await run(tool, tool === "spot_batch_orders" ? { action: opts.action, orders: parsed } : { orders: parsed });
9178
9413
  const data = getData3(result);
9179
9414
  if (opts.json) return printJson(data);
9180
- for (const r of data ?? []) {
9181
- process.stdout.write(`${r["ordId"] ?? r["clOrdId"] ?? "?"}: ${r["sCode"] === "0" ? "OK" : r["sMsg"]}
9182
- `);
9183
- }
9415
+ emitBatchResults(data ?? []);
9184
9416
  }
9185
9417
 
9186
9418
  // src/commands/swap.ts
9187
9419
  function getData4(result) {
9188
9420
  return result.data;
9189
9421
  }
9422
+ function emitWriteResult2(item, label, idKey) {
9423
+ const isError = item?.["sCode"] !== "0" && item?.["sCode"] !== 0;
9424
+ if (isError) {
9425
+ errorLine(`Error: ${item?.["sMsg"]} (sCode ${item?.["sCode"]})`);
9426
+ } else {
9427
+ outputLine(`${label}: ${item?.[idKey]} (OK)`);
9428
+ }
9429
+ }
9430
+ function emitBatchResults2(items) {
9431
+ for (const r of items) {
9432
+ const isError = r["sCode"] !== "0" && r["sCode"] !== 0;
9433
+ const id = r["ordId"] ?? r["clOrdId"] ?? "?";
9434
+ if (isError) {
9435
+ errorLine(`${id}: ${r["sMsg"]} (sCode ${r["sCode"]})`);
9436
+ } else {
9437
+ outputLine(`${id}: OK`);
9438
+ }
9439
+ }
9440
+ }
9190
9441
  async function cmdSwapPositions(run, instId, json) {
9191
9442
  const result = await run("swap_get_positions", { instId });
9192
9443
  const positions = getData4(result);
9193
9444
  if (json) return printJson(positions);
9194
9445
  const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
9195
9446
  if (!open.length) {
9196
- process.stdout.write("No open positions\n");
9447
+ outputLine("No open positions");
9197
9448
  return;
9198
9449
  }
9199
9450
  printTable(
@@ -9232,6 +9483,7 @@ async function cmdSwapPlace(run, opts) {
9232
9483
  side: opts.side,
9233
9484
  ordType: opts.ordType,
9234
9485
  sz: opts.sz,
9486
+ tgtCcy: opts.tgtCcy,
9235
9487
  posSide: opts.posSide,
9236
9488
  px: opts.px,
9237
9489
  tpTriggerPx: opts.tpTriggerPx,
@@ -9241,9 +9493,7 @@ async function cmdSwapPlace(run, opts) {
9241
9493
  });
9242
9494
  const data = getData4(result);
9243
9495
  if (opts.json) return printJson(data);
9244
- const order = data?.[0];
9245
- process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9246
- `);
9496
+ emitWriteResult2(data?.[0], "Order placed", "ordId");
9247
9497
  }
9248
9498
  async function cmdSwapCancel(run, opts) {
9249
9499
  const { instId, ordId, clOrdId, json } = opts;
@@ -9251,9 +9501,7 @@ async function cmdSwapCancel(run, opts) {
9251
9501
  const result = await run("swap_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
9252
9502
  const data = getData4(result);
9253
9503
  if (json) return printJson(data);
9254
- const r = data?.[0];
9255
- process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9256
- `);
9504
+ emitWriteResult2(data?.[0], "Cancelled", "ordId");
9257
9505
  }
9258
9506
  async function cmdSwapAlgoPlace(run, opts) {
9259
9507
  const result = await run("swap_place_algo_order", {
@@ -9274,11 +9522,7 @@ async function cmdSwapAlgoPlace(run, opts) {
9274
9522
  });
9275
9523
  const data = getData4(result);
9276
9524
  if (opts.json) return printJson(data);
9277
- const order = data?.[0];
9278
- process.stdout.write(
9279
- `Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9280
- `
9281
- );
9525
+ emitWriteResult2(data?.[0], "Algo order placed", "algoId");
9282
9526
  }
9283
9527
  async function cmdSwapAlgoAmend(run, opts) {
9284
9528
  const result = await run("swap_amend_algo_order", {
@@ -9292,11 +9536,7 @@ async function cmdSwapAlgoAmend(run, opts) {
9292
9536
  });
9293
9537
  const data = getData4(result);
9294
9538
  if (opts.json) return printJson(data);
9295
- const r = data?.[0];
9296
- process.stdout.write(
9297
- `Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9298
- `
9299
- );
9539
+ emitWriteResult2(data?.[0], "Algo order amended", "algoId");
9300
9540
  }
9301
9541
  async function cmdSwapAlgoTrailPlace(run, opts) {
9302
9542
  const result = await run("swap_place_move_stop_order", {
@@ -9312,21 +9552,13 @@ async function cmdSwapAlgoTrailPlace(run, opts) {
9312
9552
  });
9313
9553
  const data = getData4(result);
9314
9554
  if (opts.json) return printJson(data);
9315
- const order = data?.[0];
9316
- process.stdout.write(
9317
- `Trailing stop placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9318
- `
9319
- );
9555
+ emitWriteResult2(data?.[0], "Trailing stop placed", "algoId");
9320
9556
  }
9321
9557
  async function cmdSwapAlgoCancel(run, instId, algoId, json) {
9322
9558
  const result = await run("swap_cancel_algo_orders", { orders: [{ instId, algoId }] });
9323
9559
  const data = getData4(result);
9324
9560
  if (json) return printJson(data);
9325
- const r = data?.[0];
9326
- process.stdout.write(
9327
- `Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9328
- `
9329
- );
9561
+ emitWriteResult2(data?.[0], "Algo order cancelled", "algoId");
9330
9562
  }
9331
9563
  async function cmdSwapAlgoOrders(run, opts) {
9332
9564
  const result = await run("swap_get_algo_orders", {
@@ -9337,7 +9569,7 @@ async function cmdSwapAlgoOrders(run, opts) {
9337
9569
  const orders = getData4(result);
9338
9570
  if (opts.json) return printJson(orders);
9339
9571
  if (!(orders ?? []).length) {
9340
- process.stdout.write("No algo orders\n");
9572
+ outputLine("No algo orders");
9341
9573
  return;
9342
9574
  }
9343
9575
  printTable(
@@ -9374,7 +9606,7 @@ async function cmdSwapGet(run, opts) {
9374
9606
  if (opts.json) return printJson(data);
9375
9607
  const o = data?.[0];
9376
9608
  if (!o) {
9377
- process.stdout.write("No data\n");
9609
+ outputLine("No data");
9378
9610
  return;
9379
9611
  }
9380
9612
  printKv({
@@ -9401,8 +9633,7 @@ async function cmdSwapClose(run, opts) {
9401
9633
  const data = getData4(result);
9402
9634
  if (opts.json) return printJson(data);
9403
9635
  const r = data?.[0];
9404
- process.stdout.write(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}
9405
- `);
9636
+ outputLine(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}`);
9406
9637
  }
9407
9638
  async function cmdSwapGetLeverage(run, opts) {
9408
9639
  const result = await run("swap_get_leverage", { instId: opts.instId, mgnMode: opts.mgnMode });
@@ -9427,9 +9658,7 @@ async function cmdSwapAmend(run, opts) {
9427
9658
  });
9428
9659
  const data = getData4(result);
9429
9660
  if (opts.json) return printJson(data);
9430
- const r = data?.[0];
9431
- process.stdout.write(`Order amended: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9432
- `);
9661
+ emitWriteResult2(data?.[0], "Order amended", "ordId");
9433
9662
  }
9434
9663
  async function cmdSwapSetLeverage(run, opts) {
9435
9664
  const result = await run("swap_set_leverage", {
@@ -9441,20 +9670,19 @@ async function cmdSwapSetLeverage(run, opts) {
9441
9670
  const data = getData4(result);
9442
9671
  if (opts.json) return printJson(data);
9443
9672
  const r = data?.[0];
9444
- process.stdout.write(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}
9445
- `);
9673
+ outputLine(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}`);
9446
9674
  }
9447
9675
  async function cmdSwapBatch(run, opts) {
9448
9676
  let parsed;
9449
9677
  try {
9450
9678
  parsed = JSON.parse(opts.orders);
9451
9679
  } catch {
9452
- process.stderr.write("Error: --orders must be a valid JSON array\n");
9680
+ errorLine("Error: --orders must be a valid JSON array");
9453
9681
  process.exitCode = 1;
9454
9682
  return;
9455
9683
  }
9456
9684
  if (!Array.isArray(parsed) || parsed.length === 0) {
9457
- process.stderr.write("Error: --orders must be a non-empty JSON array\n");
9685
+ errorLine("Error: --orders must be a non-empty JSON array");
9458
9686
  process.exitCode = 1;
9459
9687
  return;
9460
9688
  }
@@ -9465,24 +9693,39 @@ async function cmdSwapBatch(run, opts) {
9465
9693
  };
9466
9694
  const tool = toolMap[opts.action];
9467
9695
  if (!tool) {
9468
- process.stderr.write(`Error: --action must be one of: place, amend, cancel
9469
- `);
9696
+ errorLine("Error: --action must be one of: place, amend, cancel");
9470
9697
  process.exitCode = 1;
9471
9698
  return;
9472
9699
  }
9473
9700
  const result = await run(tool, tool === "swap_batch_orders" ? { action: opts.action, orders: parsed } : { orders: parsed });
9474
9701
  const data = getData4(result);
9475
9702
  if (opts.json) return printJson(data);
9476
- for (const r of data ?? []) {
9477
- process.stdout.write(`${r["ordId"] ?? r["clOrdId"] ?? "?"}: ${r["sCode"] === "0" ? "OK" : r["sMsg"]}
9478
- `);
9479
- }
9703
+ emitBatchResults2(data ?? []);
9480
9704
  }
9481
9705
 
9482
9706
  // src/commands/futures.ts
9483
9707
  function getData5(result) {
9484
9708
  return result.data;
9485
9709
  }
9710
+ function emitWriteResult3(item, label, idKey) {
9711
+ const isError = item?.["sCode"] !== "0" && item?.["sCode"] !== 0;
9712
+ if (isError) {
9713
+ errorLine(`Error: ${item?.["sMsg"]} (sCode ${item?.["sCode"]})`);
9714
+ } else {
9715
+ outputLine(`${label}: ${item?.[idKey]} (OK)`);
9716
+ }
9717
+ }
9718
+ function emitBatchResults3(items) {
9719
+ for (const r of items) {
9720
+ const isError = r["sCode"] !== "0" && r["sCode"] !== 0;
9721
+ const id = r["ordId"] ?? r["clOrdId"] ?? "?";
9722
+ if (isError) {
9723
+ errorLine(`${id}: ${r["sMsg"]} (sCode ${r["sCode"]})`);
9724
+ } else {
9725
+ outputLine(`${id}: OK`);
9726
+ }
9727
+ }
9728
+ }
9486
9729
  async function cmdFuturesOrders(run, opts) {
9487
9730
  const result = await run("futures_get_orders", { instId: opts.instId, status: opts.status });
9488
9731
  const orders = getData5(result);
@@ -9506,7 +9749,7 @@ async function cmdFuturesPositions(run, instId, json) {
9506
9749
  if (json) return printJson(positions);
9507
9750
  const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
9508
9751
  if (!open.length) {
9509
- process.stdout.write("No open positions\n");
9752
+ outputLine("No open positions");
9510
9753
  return;
9511
9754
  }
9512
9755
  printTable(
@@ -9542,6 +9785,7 @@ async function cmdFuturesPlace(run, opts) {
9542
9785
  side: opts.side,
9543
9786
  ordType: opts.ordType,
9544
9787
  sz: opts.sz,
9788
+ tgtCcy: opts.tgtCcy,
9545
9789
  posSide: opts.posSide,
9546
9790
  px: opts.px,
9547
9791
  reduceOnly: opts.reduceOnly,
@@ -9552,9 +9796,7 @@ async function cmdFuturesPlace(run, opts) {
9552
9796
  });
9553
9797
  const data = getData5(result);
9554
9798
  if (opts.json) return printJson(data);
9555
- const order = data?.[0];
9556
- process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9557
- `);
9799
+ emitWriteResult3(data?.[0], "Order placed", "ordId");
9558
9800
  }
9559
9801
  async function cmdFuturesCancel(run, opts) {
9560
9802
  const { instId, ordId, clOrdId, json } = opts;
@@ -9562,9 +9804,7 @@ async function cmdFuturesCancel(run, opts) {
9562
9804
  const result = await run("futures_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
9563
9805
  const data = getData5(result);
9564
9806
  if (json) return printJson(data);
9565
- const r = data?.[0];
9566
- process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9567
- `);
9807
+ emitWriteResult3(data?.[0], "Cancelled", "ordId");
9568
9808
  }
9569
9809
  async function cmdFuturesGet(run, opts) {
9570
9810
  const result = await run("futures_get_order", { instId: opts.instId, ordId: opts.ordId });
@@ -9572,7 +9812,7 @@ async function cmdFuturesGet(run, opts) {
9572
9812
  if (opts.json) return printJson(data);
9573
9813
  const o = data?.[0];
9574
9814
  if (!o) {
9575
- process.stdout.write("No data\n");
9815
+ outputLine("No data");
9576
9816
  return;
9577
9817
  }
9578
9818
  printKv({
@@ -9599,9 +9839,7 @@ async function cmdFuturesAmend(run, opts) {
9599
9839
  });
9600
9840
  const data = getData5(result);
9601
9841
  if (opts.json) return printJson(data);
9602
- const r = data?.[0];
9603
- process.stdout.write(`Order amended: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9604
- `);
9842
+ emitWriteResult3(data?.[0], "Order amended", "ordId");
9605
9843
  }
9606
9844
  async function cmdFuturesClose(run, opts) {
9607
9845
  const result = await run("futures_close_position", {
@@ -9613,8 +9851,7 @@ async function cmdFuturesClose(run, opts) {
9613
9851
  const data = getData5(result);
9614
9852
  if (opts.json) return printJson(data);
9615
9853
  const r = data?.[0];
9616
- process.stdout.write(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}
9617
- `);
9854
+ outputLine(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}`);
9618
9855
  }
9619
9856
  async function cmdFuturesSetLeverage(run, opts) {
9620
9857
  const result = await run("futures_set_leverage", {
@@ -9626,8 +9863,7 @@ async function cmdFuturesSetLeverage(run, opts) {
9626
9863
  const data = getData5(result);
9627
9864
  if (opts.json) return printJson(data);
9628
9865
  const r = data?.[0];
9629
- process.stdout.write(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}
9630
- `);
9866
+ outputLine(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}`);
9631
9867
  }
9632
9868
  async function cmdFuturesGetLeverage(run, opts) {
9633
9869
  const result = await run("futures_get_leverage", { instId: opts.instId, mgnMode: opts.mgnMode });
@@ -9647,12 +9883,12 @@ async function cmdFuturesBatch(run, opts) {
9647
9883
  try {
9648
9884
  parsed = JSON.parse(opts.orders);
9649
9885
  } catch {
9650
- process.stderr.write("Error: --orders must be a valid JSON array\n");
9886
+ errorLine("Error: --orders must be a valid JSON array");
9651
9887
  process.exitCode = 1;
9652
9888
  return;
9653
9889
  }
9654
9890
  if (!Array.isArray(parsed) || parsed.length === 0) {
9655
- process.stderr.write("Error: --orders must be a non-empty JSON array\n");
9891
+ errorLine("Error: --orders must be a non-empty JSON array");
9656
9892
  process.exitCode = 1;
9657
9893
  return;
9658
9894
  }
@@ -9663,18 +9899,14 @@ async function cmdFuturesBatch(run, opts) {
9663
9899
  };
9664
9900
  const tool = toolMap[opts.action];
9665
9901
  if (!tool) {
9666
- process.stderr.write(`Error: --action must be one of: place, amend, cancel
9667
- `);
9902
+ errorLine("Error: --action must be one of: place, amend, cancel");
9668
9903
  process.exitCode = 1;
9669
9904
  return;
9670
9905
  }
9671
9906
  const result = await run(tool, tool === "futures_batch_orders" ? { orders: parsed } : { orders: parsed });
9672
9907
  const data = getData5(result);
9673
9908
  if (opts.json) return printJson(data);
9674
- for (const r of data ?? []) {
9675
- process.stdout.write(`${r["ordId"] ?? r["clOrdId"] ?? "?"}: ${r["sCode"] === "0" ? "OK" : r["sMsg"]}
9676
- `);
9677
- }
9909
+ emitBatchResults3(data ?? []);
9678
9910
  }
9679
9911
  async function cmdFuturesAlgoPlace(run, opts) {
9680
9912
  const result = await run("futures_place_algo_order", {
@@ -9695,11 +9927,7 @@ async function cmdFuturesAlgoPlace(run, opts) {
9695
9927
  });
9696
9928
  const data = getData5(result);
9697
9929
  if (opts.json) return printJson(data);
9698
- const order = data?.[0];
9699
- process.stdout.write(
9700
- `Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9701
- `
9702
- );
9930
+ emitWriteResult3(data?.[0], "Algo order placed", "algoId");
9703
9931
  }
9704
9932
  async function cmdFuturesAlgoTrailPlace(run, opts) {
9705
9933
  const result = await run("futures_place_move_stop_order", {
@@ -9715,11 +9943,7 @@ async function cmdFuturesAlgoTrailPlace(run, opts) {
9715
9943
  });
9716
9944
  const data = getData5(result);
9717
9945
  if (opts.json) return printJson(data);
9718
- const order = data?.[0];
9719
- process.stdout.write(
9720
- `Trailing stop placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9721
- `
9722
- );
9946
+ emitWriteResult3(data?.[0], "Trailing stop placed", "algoId");
9723
9947
  }
9724
9948
  async function cmdFuturesAlgoAmend(run, opts) {
9725
9949
  const result = await run("futures_amend_algo_order", {
@@ -9733,21 +9957,13 @@ async function cmdFuturesAlgoAmend(run, opts) {
9733
9957
  });
9734
9958
  const data = getData5(result);
9735
9959
  if (opts.json) return printJson(data);
9736
- const r = data?.[0];
9737
- process.stdout.write(
9738
- `Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9739
- `
9740
- );
9960
+ emitWriteResult3(data?.[0], "Algo order amended", "algoId");
9741
9961
  }
9742
9962
  async function cmdFuturesAlgoCancel(run, instId, algoId, json) {
9743
9963
  const result = await run("futures_cancel_algo_orders", { orders: [{ instId, algoId }] });
9744
9964
  const data = getData5(result);
9745
9965
  if (json) return printJson(data);
9746
- const r = data?.[0];
9747
- process.stdout.write(
9748
- `Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9749
- `
9750
- );
9966
+ emitWriteResult3(data?.[0], "Algo order cancelled", "algoId");
9751
9967
  }
9752
9968
  async function cmdFuturesAlgoOrders(run, opts) {
9753
9969
  const result = await run("futures_get_algo_orders", {
@@ -9758,7 +9974,7 @@ async function cmdFuturesAlgoOrders(run, opts) {
9758
9974
  const orders = getData5(result);
9759
9975
  if (opts.json) return printJson(orders);
9760
9976
  if (!(orders ?? []).length) {
9761
- process.stdout.write("No algo orders\n");
9977
+ outputLine("No algo orders");
9762
9978
  return;
9763
9979
  }
9764
9980
  printTable(
@@ -9779,6 +9995,25 @@ async function cmdFuturesAlgoOrders(run, opts) {
9779
9995
  function getData6(result) {
9780
9996
  return result.data;
9781
9997
  }
9998
+ function emitWriteResult4(item, label, idKey) {
9999
+ const isError = item?.["sCode"] !== "0" && item?.["sCode"] !== 0;
10000
+ if (isError) {
10001
+ errorLine(`Error: ${item?.["sMsg"]} (sCode ${item?.["sCode"]})`);
10002
+ } else {
10003
+ outputLine(`${label}: ${item?.[idKey]} (OK)`);
10004
+ }
10005
+ }
10006
+ function emitBatchResults4(items) {
10007
+ for (const r of items) {
10008
+ const isError = r["sCode"] !== "0" && r["sCode"] !== 0;
10009
+ const id = r["ordId"] ?? r["clOrdId"] ?? "?";
10010
+ if (isError) {
10011
+ errorLine(`${id}: ${r["sMsg"]} (sCode ${r["sCode"]})`);
10012
+ } else {
10013
+ outputLine(`${id}: OK`);
10014
+ }
10015
+ }
10016
+ }
9782
10017
  async function cmdOptionOrders(run, opts) {
9783
10018
  const result = await run("option_get_orders", {
9784
10019
  instId: opts.instId,
@@ -9809,7 +10044,7 @@ async function cmdOptionGet(run, opts) {
9809
10044
  if (opts.json) return printJson(data);
9810
10045
  const o = data?.[0];
9811
10046
  if (!o) {
9812
- process.stdout.write("No data\n");
10047
+ outputLine("No data");
9813
10048
  return;
9814
10049
  }
9815
10050
  printKv({
@@ -9834,7 +10069,7 @@ async function cmdOptionPositions(run, opts) {
9834
10069
  if (opts.json) return printJson(positions);
9835
10070
  const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
9836
10071
  if (!open.length) {
9837
- process.stdout.write("No open positions\n");
10072
+ outputLine("No open positions");
9838
10073
  return;
9839
10074
  }
9840
10075
  printTable(
@@ -9924,9 +10159,7 @@ async function cmdOptionPlace(run, opts) {
9924
10159
  });
9925
10160
  const data = getData6(result);
9926
10161
  if (opts.json) return printJson(data);
9927
- const order = data?.[0];
9928
- process.stdout.write(`Order placed: ${order?.["ordId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9929
- `);
10162
+ emitWriteResult4(data?.[0], "Order placed", "ordId");
9930
10163
  }
9931
10164
  async function cmdOptionCancel(run, opts) {
9932
10165
  const result = await run("option_cancel_order", {
@@ -9936,9 +10169,7 @@ async function cmdOptionCancel(run, opts) {
9936
10169
  });
9937
10170
  const data = getData6(result);
9938
10171
  if (opts.json) return printJson(data);
9939
- const r = data?.[0];
9940
- process.stdout.write(`Cancelled: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9941
- `);
10172
+ emitWriteResult4(data?.[0], "Cancelled", "ordId");
9942
10173
  }
9943
10174
  async function cmdOptionAmend(run, opts) {
9944
10175
  const result = await run("option_amend_order", {
@@ -9950,31 +10181,26 @@ async function cmdOptionAmend(run, opts) {
9950
10181
  });
9951
10182
  const data = getData6(result);
9952
10183
  if (opts.json) return printJson(data);
9953
- const r = data?.[0];
9954
- process.stdout.write(`Amended: ${r?.["ordId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
9955
- `);
10184
+ emitWriteResult4(data?.[0], "Amended", "ordId");
9956
10185
  }
9957
10186
  async function cmdOptionBatchCancel(run, opts) {
9958
10187
  let parsed;
9959
10188
  try {
9960
10189
  parsed = JSON.parse(opts.orders);
9961
10190
  } catch {
9962
- process.stderr.write("Error: --orders must be a valid JSON array\n");
10191
+ errorLine("Error: --orders must be a valid JSON array");
9963
10192
  process.exitCode = 1;
9964
10193
  return;
9965
10194
  }
9966
10195
  if (!Array.isArray(parsed) || parsed.length === 0) {
9967
- process.stderr.write("Error: --orders must be a non-empty JSON array\n");
10196
+ errorLine("Error: --orders must be a non-empty JSON array");
9968
10197
  process.exitCode = 1;
9969
10198
  return;
9970
10199
  }
9971
10200
  const result = await run("option_batch_cancel", { orders: parsed });
9972
10201
  const data = getData6(result);
9973
10202
  if (opts.json) return printJson(data);
9974
- for (const r of data ?? []) {
9975
- process.stdout.write(`${r["ordId"]}: ${r["sCode"] === "0" ? "OK" : r["sMsg"]}
9976
- `);
9977
- }
10203
+ emitBatchResults4(data ?? []);
9978
10204
  }
9979
10205
  async function cmdOptionAlgoPlace(run, opts) {
9980
10206
  const result = await run("option_place_algo_order", {
@@ -9992,11 +10218,7 @@ async function cmdOptionAlgoPlace(run, opts) {
9992
10218
  });
9993
10219
  const data = getData6(result);
9994
10220
  if (opts.json) return printJson(data);
9995
- const order = data?.[0];
9996
- process.stdout.write(
9997
- `Algo order placed: ${order?.["algoId"]} (${order?.["sCode"] === "0" ? "OK" : order?.["sMsg"]})
9998
- `
9999
- );
10221
+ emitWriteResult4(data?.[0], "Algo order placed", "algoId");
10000
10222
  }
10001
10223
  async function cmdOptionAlgoAmend(run, opts) {
10002
10224
  const result = await run("option_amend_algo_order", {
@@ -10010,21 +10232,13 @@ async function cmdOptionAlgoAmend(run, opts) {
10010
10232
  });
10011
10233
  const data = getData6(result);
10012
10234
  if (opts.json) return printJson(data);
10013
- const r = data?.[0];
10014
- process.stdout.write(
10015
- `Algo order amended: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10016
- `
10017
- );
10235
+ emitWriteResult4(data?.[0], "Algo order amended", "algoId");
10018
10236
  }
10019
10237
  async function cmdOptionAlgoCancel(run, opts) {
10020
10238
  const result = await run("option_cancel_algo_orders", { orders: [{ instId: opts.instId, algoId: opts.algoId }] });
10021
10239
  const data = getData6(result);
10022
10240
  if (opts.json) return printJson(data);
10023
- const r = data?.[0];
10024
- process.stdout.write(
10025
- `Algo order cancelled: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10026
- `
10027
- );
10241
+ emitWriteResult4(data?.[0], "Algo order cancelled", "algoId");
10028
10242
  }
10029
10243
  async function cmdOptionAlgoOrders(run, opts) {
10030
10244
  const result = await run("option_get_algo_orders", {
@@ -10035,7 +10249,7 @@ async function cmdOptionAlgoOrders(run, opts) {
10035
10249
  const orders = getData6(result);
10036
10250
  if (opts.json) return printJson(orders);
10037
10251
  if (!(orders ?? []).length) {
10038
- process.stdout.write("No algo orders\n");
10252
+ outputLine("No algo orders");
10039
10253
  return;
10040
10254
  }
10041
10255
  printTable(
@@ -10136,21 +10350,18 @@ function prompt(rl, question) {
10136
10350
  function cmdConfigShow(json) {
10137
10351
  const config = readFullConfig();
10138
10352
  if (json) return printJson(config);
10139
- process.stdout.write(`Config: ${configFilePath()}
10140
-
10141
- `);
10142
- process.stdout.write(`default_profile: ${config.default_profile ?? "(not set)"}
10143
-
10144
- `);
10353
+ outputLine(`Config: ${configFilePath()}`);
10354
+ outputLine("");
10355
+ outputLine(`default_profile: ${config.default_profile ?? "(not set)"}`);
10356
+ outputLine("");
10145
10357
  for (const [name, profile] of Object.entries(config.profiles)) {
10146
- process.stdout.write(`[${name}]
10147
- `);
10358
+ outputLine(`[${name}]`);
10148
10359
  printKv({
10149
10360
  api_key: profile.api_key ? maskSecret(profile.api_key) : "(not set)",
10150
10361
  demo: profile.demo ?? false,
10151
10362
  base_url: profile.base_url ?? "(default)"
10152
10363
  }, 2);
10153
- process.stdout.write("\n");
10364
+ outputLine("");
10154
10365
  }
10155
10366
  }
10156
10367
  function cmdConfigSet(key, value) {
@@ -10158,11 +10369,9 @@ function cmdConfigSet(key, value) {
10158
10369
  if (key === "default_profile") {
10159
10370
  config.default_profile = value;
10160
10371
  writeCliConfig(config);
10161
- process.stdout.write(`default_profile set to "${value}"
10162
- `);
10372
+ outputLine(`default_profile set to "${value}"`);
10163
10373
  } else {
10164
- process.stderr.write(`Unknown config key: ${key}
10165
- `);
10374
+ errorLine(`Unknown config key: ${key}`);
10166
10375
  process.exitCode = 1;
10167
10376
  }
10168
10377
  }
@@ -10198,24 +10407,22 @@ function buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo) {
10198
10407
  }
10199
10408
  async function cmdConfigInit(lang = "en") {
10200
10409
  const t = messages[lang];
10201
- process.stdout.write(`${t.title}
10202
-
10203
- `);
10410
+ outputLine(t.title);
10411
+ outputLine("");
10204
10412
  const rl = createInterface({ input: process.stdin, output: process.stdout });
10205
10413
  try {
10206
- process.stdout.write(`${t.selectSite}
10207
- `);
10208
- process.stdout.write(" 1) Global (www.okx.com) [default]\n");
10209
- process.stdout.write(" 2) EEA (my.okx.com)\n");
10210
- process.stdout.write(" 3) US (app.okx.com)\n");
10414
+ outputLine(t.selectSite);
10415
+ outputLine(" 1) Global (www.okx.com) [default]");
10416
+ outputLine(" 2) EEA (my.okx.com)");
10417
+ outputLine(" 3) US (app.okx.com)");
10211
10418
  const siteRaw = (await prompt(rl, t.sitePrompt)).trim();
10212
10419
  const siteKey = parseSiteKey(siteRaw);
10213
10420
  const demoRaw = (await prompt(rl, t.demoPrompt)).trim().toLowerCase();
10214
10421
  const demo = demoRaw !== "n";
10215
10422
  const apiUrl = buildApiUrl(siteKey, demo);
10216
10423
  const hintText = demo ? t.hintDemo : t.hintLive;
10217
- process.stdout.write(t.createApiKey(apiUrl));
10218
- process.stdout.write(t.hint(hintText));
10424
+ output(t.createApiKey(apiUrl));
10425
+ output(t.hint(hintText));
10219
10426
  try {
10220
10427
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
10221
10428
  spawnSync2(opener, [apiUrl], { stdio: "ignore", shell: process.platform === "win32" });
@@ -10228,35 +10435,30 @@ async function cmdConfigInit(lang = "en") {
10228
10435
  if (config.profiles[profileName]) {
10229
10436
  const overwrite = (await prompt(rl, t.profileExists(profileName))).trim().toLowerCase();
10230
10437
  if (overwrite !== "y") {
10231
- process.stdout.write(`${t.cancelled}
10232
- `);
10438
+ outputLine(t.cancelled);
10233
10439
  return;
10234
10440
  }
10235
10441
  }
10236
10442
  const apiKey = (await prompt(rl, "API Key: ")).trim();
10237
10443
  if (!apiKey) {
10238
- process.stderr.write(`${t.emptyApiKey}
10239
- `);
10444
+ errorLine(t.emptyApiKey);
10240
10445
  process.exitCode = 1;
10241
10446
  return;
10242
10447
  }
10243
10448
  const secretKey = (await prompt(rl, "Secret Key: ")).trim();
10244
10449
  if (!secretKey) {
10245
- process.stderr.write(`${t.emptySecretKey}
10246
- `);
10450
+ errorLine(t.emptySecretKey);
10247
10451
  process.exitCode = 1;
10248
10452
  return;
10249
10453
  }
10250
10454
  const passphrase = (await prompt(rl, "Passphrase: ")).trim();
10251
10455
  if (!passphrase) {
10252
- process.stderr.write(`${t.emptyPassphrase}
10253
- `);
10456
+ errorLine(t.emptyPassphrase);
10254
10457
  process.exitCode = 1;
10255
10458
  return;
10256
10459
  }
10257
10460
  if (demo) {
10258
- process.stdout.write(`${t.demoSelected}
10259
- `);
10461
+ outputLine(t.demoSelected);
10260
10462
  }
10261
10463
  const profileEntry = buildProfileEntry(siteKey, apiKey, secretKey, passphrase, demo);
10262
10464
  config.profiles[profileName] = profileEntry;
@@ -10266,18 +10468,18 @@ async function cmdConfigInit(lang = "en") {
10266
10468
  const configPath = configFilePath();
10267
10469
  try {
10268
10470
  writeCliConfig(config);
10269
- process.stdout.write(t.saved(configPath));
10270
- process.stdout.write(t.defaultProfile(profileName));
10271
- process.stdout.write(t.usage);
10471
+ output(t.saved(configPath));
10472
+ output(t.defaultProfile(profileName));
10473
+ output(t.usage);
10272
10474
  } catch (err) {
10273
10475
  const message = err instanceof Error ? err.message : String(err);
10274
10476
  const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
10275
- process.stderr.write(t.writeFailed(message));
10477
+ errorOutput(t.writeFailed(message));
10276
10478
  if (isPermission) {
10277
- process.stderr.write(t.permissionDenied(configPath));
10479
+ errorOutput(t.permissionDenied(configPath));
10278
10480
  }
10279
- process.stderr.write(t.manualWrite(configPath));
10280
- process.stdout.write(stringify(config) + "\n");
10481
+ errorOutput(t.manualWrite(configPath));
10482
+ outputLine(stringify(config));
10281
10483
  process.exitCode = 1;
10282
10484
  }
10283
10485
  } finally {
@@ -10301,9 +10503,8 @@ function cmdConfigAddProfile(kvPairs, force) {
10301
10503
  if (!sk) missing.push("SK");
10302
10504
  if (!pp) missing.push("PP");
10303
10505
  if (missing.length > 0) {
10304
- process.stderr.write(`Error: missing required parameter(s): ${missing.join(", ")}
10305
- `);
10306
- process.stderr.write("Usage: okx config add-profile AK=<key> SK=<secret> PP=<passphrase> [site=global|eea|us] [demo=true|false] [name=<name>] [--force]\n");
10506
+ errorLine(`Error: missing required parameter(s): ${missing.join(", ")}`);
10507
+ errorLine("Usage: okx config add-profile AK=<key> SK=<secret> PP=<passphrase> [site=global|eea|us] [demo=true|false] [name=<name>] [--force]");
10307
10508
  process.exitCode = 1;
10308
10509
  return;
10309
10510
  }
@@ -10313,8 +10514,7 @@ function cmdConfigAddProfile(kvPairs, force) {
10313
10514
  const profileName = params["NAME"] ?? defaultName;
10314
10515
  const config = readFullConfig();
10315
10516
  if (config.profiles[profileName] && !force) {
10316
- process.stderr.write(`Error: profile "${profileName}" already exists. Use --force to overwrite.
10317
- `);
10517
+ errorLine(`Error: profile "${profileName}" already exists. Use --force to overwrite.`);
10318
10518
  process.exitCode = 1;
10319
10519
  return;
10320
10520
  }
@@ -10323,65 +10523,54 @@ function cmdConfigAddProfile(kvPairs, force) {
10323
10523
  config.profiles[profileName] = entry;
10324
10524
  config.default_profile = profileName;
10325
10525
  writeCliConfig(config);
10326
- process.stdout.write(`Profile "${profileName}" saved to ${configFilePath()}
10327
- `);
10328
- process.stdout.write(`Default profile set to: ${profileName}
10329
- `);
10526
+ outputLine(`Profile "${profileName}" saved to ${configFilePath()}`);
10527
+ outputLine(`Default profile set to: ${profileName}`);
10330
10528
  }
10331
10529
  function cmdConfigListProfile() {
10332
10530
  const config = readFullConfig();
10333
10531
  const entries = Object.entries(config.profiles);
10334
10532
  if (entries.length === 0) {
10335
- process.stdout.write("No profiles found. Run: okx config add-profile AK=<key> SK=<secret> PP=<passphrase>\n");
10533
+ outputLine("No profiles found. Run: okx config add-profile AK=<key> SK=<secret> PP=<passphrase>");
10336
10534
  return;
10337
10535
  }
10338
- process.stdout.write(`Config: ${configFilePath()}
10339
-
10340
- `);
10536
+ outputLine(`Config: ${configFilePath()}`);
10537
+ outputLine("");
10341
10538
  for (const [name, profile] of entries) {
10342
10539
  const isDefault = name === config.default_profile;
10343
10540
  const marker = isDefault ? " *" : "";
10344
10541
  const site = profile.site ?? inferSiteFromBaseUrl(profile.base_url);
10345
10542
  const mode = profile.demo !== false ? "demo (\u6A21\u62DF\u76D8)" : "live (\u5B9E\u76D8)";
10346
- process.stdout.write(`[${name}]${marker}
10347
- `);
10348
- process.stdout.write(` api_key: ${maskSecret(profile.api_key)}
10349
- `);
10350
- process.stdout.write(` secret_key: ${maskSecret(profile.secret_key)}
10351
- `);
10352
- process.stdout.write(` passphrase: ${maskSecret(profile.passphrase)}
10353
- `);
10354
- process.stdout.write(` site: ${site}
10355
- `);
10356
- process.stdout.write(` mode: ${mode}
10357
- `);
10358
- process.stdout.write("\n");
10543
+ outputLine(`[${name}]${marker}`);
10544
+ outputLine(` api_key: ${maskSecret(profile.api_key)}`);
10545
+ outputLine(` secret_key: ${maskSecret(profile.secret_key)}`);
10546
+ outputLine(` passphrase: ${maskSecret(profile.passphrase)}`);
10547
+ outputLine(` site: ${site}`);
10548
+ outputLine(` mode: ${mode}`);
10549
+ outputLine("");
10359
10550
  }
10360
10551
  }
10361
10552
  function cmdConfigUse(profileName) {
10362
10553
  if (!profileName) {
10363
- process.stderr.write("Error: profile name is required.\nUsage: okx config use <profile-name>\n");
10554
+ errorLine("Error: profile name is required.");
10555
+ errorLine("Usage: okx config use <profile-name>");
10364
10556
  process.exitCode = 1;
10365
10557
  return;
10366
10558
  }
10367
10559
  const config = readFullConfig();
10368
10560
  const available = Object.keys(config.profiles);
10369
10561
  if (!config.profiles[profileName]) {
10370
- process.stderr.write(`Error: profile "${profileName}" does not exist.
10371
- `);
10562
+ errorLine(`Error: profile "${profileName}" does not exist.`);
10372
10563
  if (available.length > 0) {
10373
- process.stderr.write(`Available profiles: ${available.join(", ")}
10374
- `);
10564
+ errorLine(`Available profiles: ${available.join(", ")}`);
10375
10565
  } else {
10376
- process.stderr.write("No profiles configured. Run: okx config add-profile AK=<key> SK=<secret> PP=<passphrase>\n");
10566
+ errorLine("No profiles configured. Run: okx config add-profile AK=<key> SK=<secret> PP=<passphrase>");
10377
10567
  }
10378
10568
  process.exitCode = 1;
10379
10569
  return;
10380
10570
  }
10381
10571
  config.default_profile = profileName;
10382
10572
  writeCliConfig(config);
10383
- process.stdout.write(`Default profile set to: "${profileName}"
10384
- `);
10573
+ outputLine(`Default profile set to: "${profileName}"`);
10385
10574
  }
10386
10575
 
10387
10576
  // src/commands/earn.ts
@@ -10398,7 +10587,7 @@ function printDataList(data, json, emptyMsg, mapper) {
10398
10587
  return;
10399
10588
  }
10400
10589
  if (!data.length) {
10401
- process.stdout.write(emptyMsg + "\n");
10590
+ outputLine(emptyMsg);
10402
10591
  return;
10403
10592
  }
10404
10593
  printTable(data.map(mapper));
@@ -10422,7 +10611,7 @@ async function cmdEarnSavingsPurchase(run, opts) {
10422
10611
  }
10423
10612
  const r = data[0];
10424
10613
  if (!r) {
10425
- process.stdout.write("No response data\n");
10614
+ outputLine("No response data");
10426
10615
  return;
10427
10616
  }
10428
10617
  printKv({ ccy: r["ccy"], amt: r["amt"], side: r["side"], rate: r["rate"] });
@@ -10435,7 +10624,7 @@ async function cmdEarnSavingsRedeem(run, opts) {
10435
10624
  }
10436
10625
  const r = data[0];
10437
10626
  if (!r) {
10438
- process.stdout.write("No response data\n");
10627
+ outputLine("No response data");
10439
10628
  return;
10440
10629
  }
10441
10630
  printKv({ ccy: r["ccy"], amt: r["amt"], side: r["side"] });
@@ -10447,8 +10636,7 @@ async function cmdEarnSetLendingRate(run, opts) {
10447
10636
  return;
10448
10637
  }
10449
10638
  const r = data[0];
10450
- process.stdout.write(`Lending rate set: ${r?.["ccy"]} \u2192 ${r?.["rate"]}
10451
- `);
10639
+ outputLine(`Lending rate set: ${r?.["ccy"]} \u2192 ${r?.["rate"]}`);
10452
10640
  }
10453
10641
  async function cmdEarnLendingHistory(run, opts) {
10454
10642
  const data = extractData(await run("earn_get_lending_history", { ccy: opts.ccy, limit: opts.limit }));
@@ -10480,6 +10668,14 @@ async function cmdEarnLendingRateHistory(run, opts) {
10480
10668
  }
10481
10669
 
10482
10670
  // src/commands/bot.ts
10671
+ function emitWriteResult5(item, label, idKey) {
10672
+ const isError = item?.["sCode"] !== "0" && item?.["sCode"] !== 0;
10673
+ if (isError) {
10674
+ errorLine(`Error: ${item?.["sMsg"]} (sCode ${item?.["sCode"]})`);
10675
+ } else {
10676
+ outputLine(`${label}: ${item?.[idKey]} (OK)`);
10677
+ }
10678
+ }
10483
10679
  function getData7(result) {
10484
10680
  return result.data;
10485
10681
  }
@@ -10493,7 +10689,7 @@ async function cmdGridOrders(run, opts) {
10493
10689
  const orders = getData7(result) ?? [];
10494
10690
  if (opts.json) return printJson(orders);
10495
10691
  if (!orders.length) {
10496
- process.stdout.write("No grid bots\n");
10692
+ outputLine("No grid bots");
10497
10693
  return;
10498
10694
  }
10499
10695
  printTable(
@@ -10517,7 +10713,7 @@ async function cmdGridDetails(run, opts) {
10517
10713
  });
10518
10714
  const detail = (getData7(result) ?? [])[0];
10519
10715
  if (!detail) {
10520
- process.stdout.write("Bot not found\n");
10716
+ outputLine("Bot not found");
10521
10717
  return;
10522
10718
  }
10523
10719
  if (opts.json) return printJson(detail);
@@ -10546,7 +10742,7 @@ async function cmdGridSubOrders(run, opts) {
10546
10742
  const orders = getData7(result) ?? [];
10547
10743
  if (opts.json) return printJson(orders);
10548
10744
  if (!orders.length) {
10549
- process.stdout.write("No sub-orders\n");
10745
+ outputLine("No sub-orders");
10550
10746
  return;
10551
10747
  }
10552
10748
  printTable(
@@ -10580,10 +10776,7 @@ async function cmdGridCreate(run, opts) {
10580
10776
  const data = getData7(result);
10581
10777
  if (opts.json) return printJson(data);
10582
10778
  const r = data?.[0];
10583
- process.stdout.write(
10584
- `Grid bot created: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10585
- `
10586
- );
10779
+ emitWriteResult5(data?.[0], "Grid bot created", "algoId");
10587
10780
  }
10588
10781
  async function cmdGridStop(run, opts) {
10589
10782
  const result = await run("grid_stop_order", {
@@ -10595,10 +10788,7 @@ async function cmdGridStop(run, opts) {
10595
10788
  const data = getData7(result);
10596
10789
  if (opts.json) return printJson(data);
10597
10790
  const r = data?.[0];
10598
- process.stdout.write(
10599
- `Grid bot stopped: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10600
- `
10601
- );
10791
+ emitWriteResult5(data?.[0], "Grid bot stopped", "algoId");
10602
10792
  }
10603
10793
  async function cmdDcaCreate(run, opts) {
10604
10794
  const result = await run("dca_create_order", {
@@ -10621,10 +10811,7 @@ async function cmdDcaCreate(run, opts) {
10621
10811
  const data = getData7(result);
10622
10812
  if (opts.json) return printJson(data);
10623
10813
  const r = data?.[0];
10624
- process.stdout.write(
10625
- `DCA bot created: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10626
- `
10627
- );
10814
+ emitWriteResult5(data?.[0], "DCA bot created", "algoId");
10628
10815
  }
10629
10816
  async function cmdDcaStop(run, opts) {
10630
10817
  const result = await run("dca_stop_order", {
@@ -10633,10 +10820,7 @@ async function cmdDcaStop(run, opts) {
10633
10820
  const data = getData7(result);
10634
10821
  if (opts.json) return printJson(data);
10635
10822
  const r = data?.[0];
10636
- process.stdout.write(
10637
- `DCA bot stopped: ${r?.["algoId"]} (${r?.["sCode"] === "0" ? "OK" : r?.["sMsg"]})
10638
- `
10639
- );
10823
+ emitWriteResult5(data?.[0], "DCA bot stopped", "algoId");
10640
10824
  }
10641
10825
  async function cmdDcaOrders(run, opts) {
10642
10826
  const result = await run("dca_get_orders", {
@@ -10647,7 +10831,7 @@ async function cmdDcaOrders(run, opts) {
10647
10831
  const orders = getData7(result) ?? [];
10648
10832
  if (opts.json) return printJson(orders);
10649
10833
  if (!orders.length) {
10650
- process.stdout.write("No DCA bots\n");
10834
+ outputLine("No DCA bots");
10651
10835
  return;
10652
10836
  }
10653
10837
  printTable(
@@ -10667,7 +10851,7 @@ async function cmdDcaDetails(run, opts) {
10667
10851
  });
10668
10852
  const detail = (getData7(result) ?? [])[0];
10669
10853
  if (!detail) {
10670
- process.stdout.write("DCA bot not found\n");
10854
+ outputLine("DCA bot not found");
10671
10855
  return;
10672
10856
  }
10673
10857
  if (opts.json) return printJson(detail);
@@ -10695,7 +10879,7 @@ async function cmdDcaSubOrders(run, opts) {
10695
10879
  const orders = getData7(result) ?? [];
10696
10880
  if (opts.json) return printJson(orders);
10697
10881
  if (!orders.length) {
10698
- process.stdout.write("No sub-orders\n");
10882
+ outputLine("No sub-orders");
10699
10883
  return;
10700
10884
  }
10701
10885
  printTable(
@@ -10784,7 +10968,7 @@ async function cmdDcdPairs(run, json) {
10784
10968
  return;
10785
10969
  }
10786
10970
  if (!data.length) {
10787
- process.stdout.write("No currency pairs available\n");
10971
+ outputLine("No currency pairs available");
10788
10972
  return;
10789
10973
  }
10790
10974
  printTable(data.map((r) => ({
@@ -10849,7 +11033,7 @@ async function cmdDcdProducts(run, opts) {
10849
11033
  return;
10850
11034
  }
10851
11035
  if (!data.length) {
10852
- process.stdout.write("No products matched\n");
11036
+ outputLine("No products matched");
10853
11037
  return;
10854
11038
  }
10855
11039
  printTable(data.map((r) => ({
@@ -10869,7 +11053,7 @@ async function cmdDcdRedeemExecute(run, opts) {
10869
11053
  const quoteData = extractArray(quoteResult);
10870
11054
  const q = quoteData[0];
10871
11055
  if (!q) {
10872
- process.stdout.write("Failed to get redeem quote\n");
11056
+ outputLine("Failed to get redeem quote");
10873
11057
  return;
10874
11058
  }
10875
11059
  const redeemResult = await run("dcd_redeem", {
@@ -10879,7 +11063,7 @@ async function cmdDcdRedeemExecute(run, opts) {
10879
11063
  const redeemData = extractArray(redeemResult);
10880
11064
  const r = redeemData[0];
10881
11065
  if (!r) {
10882
- process.stdout.write("No response data\n");
11066
+ outputLine("No response data");
10883
11067
  return;
10884
11068
  }
10885
11069
  if (opts.json) {
@@ -10902,7 +11086,7 @@ async function cmdDcdOrderState(run, opts) {
10902
11086
  }
10903
11087
  const r = data[0];
10904
11088
  if (!r) {
10905
- process.stdout.write("Order not found\n");
11089
+ outputLine("Order not found");
10906
11090
  return;
10907
11091
  }
10908
11092
  printKv({
@@ -10932,7 +11116,7 @@ async function cmdDcdOrders(run, opts) {
10932
11116
  return;
10933
11117
  }
10934
11118
  if (!data.length) {
10935
- process.stdout.write("No orders found\n");
11119
+ outputLine("No orders found");
10936
11120
  return;
10937
11121
  }
10938
11122
  printTable(data.map((r) => ({
@@ -10963,7 +11147,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
10963
11147
  const r = tradeData[0];
10964
11148
  const q = result["quote"];
10965
11149
  if (!r) {
10966
- process.stdout.write("No quote returned\n");
11150
+ outputLine("No quote returned");
10967
11151
  return;
10968
11152
  }
10969
11153
  const ordId = r["ordId"];
@@ -10980,7 +11164,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
10980
11164
  return;
10981
11165
  }
10982
11166
  if (q) {
10983
- process.stdout.write("Quote:\n");
11167
+ outputLine("Quote:");
10984
11168
  printKv({
10985
11169
  quoteId: q["quoteId"],
10986
11170
  annualizedYield: q["annualizedYield"] ? `${q["annualizedYield"]}%` : "\u2014",
@@ -10988,12 +11172,13 @@ async function cmdDcdQuoteAndBuy(run, opts) {
10988
11172
  notionalSz: q["notionalSz"],
10989
11173
  notionalCcy: q["notionalCcy"]
10990
11174
  });
10991
- process.stdout.write("\n");
11175
+ outputLine("");
10992
11176
  }
10993
- process.stdout.write("Order placed:\n");
11177
+ outputLine("Order placed:");
10994
11178
  printKv({ ordId: r["ordId"], quoteId: r["quoteId"], state: r["state"] ?? r["status"] });
10995
11179
  if (stateRow) {
10996
- process.stdout.write("\nOrder state:\n");
11180
+ outputLine("");
11181
+ outputLine("Order state:");
10997
11182
  printKv({
10998
11183
  ordId: stateRow["ordId"],
10999
11184
  state: stateRow["state"],
@@ -11008,7 +11193,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
11008
11193
  // src/index.ts
11009
11194
  var _require3 = createRequire3(import.meta.url);
11010
11195
  var CLI_VERSION2 = _require3("../package.json").version;
11011
- var GIT_HASH2 = true ? "3157372" : "dev";
11196
+ var GIT_HASH2 = true ? "c27439d" : "dev";
11012
11197
  function handleConfigCommand(action, rest, json, lang, force) {
11013
11198
  if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
11014
11199
  if (action === "show") return cmdConfigShow(json);
@@ -11017,8 +11202,7 @@ function handleConfigCommand(action, rest, json, lang, force) {
11017
11202
  if (action === "add-profile") return cmdConfigAddProfile(rest, force ?? false);
11018
11203
  if (action === "list-profile") return cmdConfigListProfile();
11019
11204
  if (action === "use") return cmdConfigUse(rest[0]);
11020
- process.stderr.write(`Unknown config command: ${action}
11021
- `);
11205
+ errorLine(`Unknown config command: ${action}`);
11022
11206
  process.exitCode = 1;
11023
11207
  }
11024
11208
  function handleSetupCommand(v) {
@@ -11027,11 +11211,8 @@ function handleSetupCommand(v) {
11027
11211
  return;
11028
11212
  }
11029
11213
  if (!SUPPORTED_CLIENTS.includes(v.client)) {
11030
- process.stderr.write(
11031
- `Unknown client: "${v.client}"
11032
- Supported: ${SUPPORTED_CLIENTS.join(", ")}
11033
- `
11034
- );
11214
+ errorLine(`Unknown client: "${v.client}"`);
11215
+ errorLine(`Supported: ${SUPPORTED_CLIENTS.join(", ")}`);
11035
11216
  process.exitCode = 1;
11036
11217
  return;
11037
11218
  }
@@ -11055,6 +11236,18 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
11055
11236
  return cmdMarketOpenInterest(run, { instType: v.instType, instId: v.instId, json });
11056
11237
  if (action === "stock-tokens")
11057
11238
  return cmdMarketStockTokens(run, { instType: v.instType, instId: v.instId, json });
11239
+ if (action === "indicator") {
11240
+ const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
11241
+ const backtestTime = v["backtest-time"] !== void 0 ? Number(v["backtest-time"]) : void 0;
11242
+ return cmdMarketIndicator(run, rest[0], rest[1], {
11243
+ bar: v.bar,
11244
+ params: v.params,
11245
+ list: v.list,
11246
+ limit,
11247
+ backtestTime,
11248
+ json
11249
+ });
11250
+ }
11058
11251
  }
11059
11252
  function handleMarketDataCommand(run, action, rest, v, json) {
11060
11253
  const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
@@ -11195,6 +11388,7 @@ function handleSpotCommand(run, action, rest, v, json) {
11195
11388
  side: v.side,
11196
11389
  ordType: v.ordType,
11197
11390
  sz: v.sz,
11391
+ tgtCcy: v.tgtCcy,
11198
11392
  px: v.px,
11199
11393
  tpTriggerPx: v.tpTriggerPx,
11200
11394
  tpOrdPx: v.tpOrdPx,
@@ -11304,6 +11498,7 @@ function handleSwapCommand(run, action, rest, v, json) {
11304
11498
  posSide: v.posSide,
11305
11499
  px: v.px,
11306
11500
  tdMode: v.tdMode ?? "cross",
11501
+ tgtCcy: v.tgtCcy,
11307
11502
  tpTriggerPx: v.tpTriggerPx,
11308
11503
  tpOrdPx: v.tpOrdPx,
11309
11504
  slTriggerPx: v.slTriggerPx,
@@ -11505,6 +11700,7 @@ function handleFuturesCommand(run, action, rest, v, json) {
11505
11700
  ordType: v.ordType,
11506
11701
  sz: v.sz,
11507
11702
  tdMode: v.tdMode ?? "cross",
11703
+ tgtCcy: v.tgtCcy,
11508
11704
  posSide: v.posSide,
11509
11705
  px: v.px,
11510
11706
  reduceOnly: v.reduceOnly,
@@ -11633,9 +11829,8 @@ function handleEarnCommand(run, submodule, rest, v, json) {
11633
11829
  if (submodule === "savings") return handleEarnSavingsCommand(run, action, innerRest, v, json);
11634
11830
  if (submodule === "onchain") return handleEarnOnchainCommand(run, action, v, json);
11635
11831
  if (submodule === "dcd") return handleEarnDcdCommand(run, action, v, json);
11636
- process.stderr.write(`Unknown earn sub-module: ${submodule}
11637
- Valid: savings, onchain, dcd
11638
- `);
11832
+ errorLine(`Unknown earn sub-module: ${submodule}`);
11833
+ errorLine("Valid: savings, onchain, dcd");
11639
11834
  process.exitCode = 1;
11640
11835
  }
11641
11836
  function handleEarnSavingsCommand(run, action, rest, v, json) {
@@ -11647,8 +11842,7 @@ function handleEarnSavingsCommand(run, action, rest, v, json) {
11647
11842
  if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
11648
11843
  if (action === "rate-summary") return cmdEarnLendingRateSummary(run, rest[0] ?? v.ccy, json);
11649
11844
  if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
11650
- process.stderr.write(`Unknown earn savings command: ${action}
11651
- `);
11845
+ errorLine(`Unknown earn savings command: ${action}`);
11652
11846
  process.exitCode = 1;
11653
11847
  }
11654
11848
  function handleEarnOnchainCommand(run, action, v, json) {
@@ -11658,8 +11852,7 @@ function handleEarnOnchainCommand(run, action, v, json) {
11658
11852
  if (action === "cancel") return cmdOnchainEarnCancel(run, v).then((r) => outputResult(r, json));
11659
11853
  if (action === "orders") return cmdOnchainEarnActiveOrders(run, v).then((r) => outputResult(r, json));
11660
11854
  if (action === "history") return cmdOnchainEarnOrderHistory(run, v).then((r) => outputResult(r, json));
11661
- process.stderr.write(`Unknown earn onchain command: ${action}
11662
- `);
11855
+ errorLine(`Unknown earn onchain command: ${action}`);
11663
11856
  process.exitCode = 1;
11664
11857
  }
11665
11858
  function parseDcdOpts(v) {
@@ -11714,16 +11907,15 @@ function handleEarnDcdCommand(run, action, v, json) {
11714
11907
  limit,
11715
11908
  json
11716
11909
  });
11717
- process.stderr.write(`Unknown earn dcd command: ${action}
11718
- Valid: pairs, products, quote-and-buy, redeem-execute, order, orders
11719
- `);
11910
+ errorLine(`Unknown earn dcd command: ${action}`);
11911
+ errorLine("Valid: pairs, products, quote-and-buy, redeem-execute, order, orders");
11720
11912
  process.exitCode = 1;
11721
11913
  }
11722
11914
  function outputResult(result, json) {
11723
11915
  if (json) {
11724
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
11916
+ outputLine(JSON.stringify(result, null, 2));
11725
11917
  } else {
11726
- process.stdout.write(JSON.stringify(result.data, null, 2) + "\n");
11918
+ outputLine(JSON.stringify(result.data, null, 2));
11727
11919
  }
11728
11920
  }
11729
11921
  function printHelpForLevel(positionals) {
@@ -11732,24 +11924,15 @@ function printHelpForLevel(positionals) {
11732
11924
  else if (!subgroup) printHelp(module);
11733
11925
  else printHelp(module, subgroup);
11734
11926
  }
11735
- function printVerboseConfigSummary(config, profile) {
11736
- let authLabel = "\u2717";
11737
- if (config.hasAuth && config.apiKey) {
11738
- authLabel = `\u2713(${config.apiKey.slice(0, 3)}***${config.apiKey.slice(-3)})`;
11739
- } else if (config.hasAuth) {
11740
- authLabel = "\u2713";
11741
- }
11742
- process.stderr.write(
11743
- `[verbose] config: profile=${profile ?? "default"} site=${config.site} base=${config.baseUrl} auth=${authLabel} demo=${config.demo ? "on" : "off"} modules=${config.modules.join(",")}
11744
- `
11745
- );
11746
- }
11747
11927
  async function main() {
11928
+ setOutput({
11929
+ out: (m) => process.stdout.write(m),
11930
+ err: (m) => process.stderr.write(m)
11931
+ });
11748
11932
  checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION2);
11749
11933
  const { values, positionals } = parseCli(process.argv.slice(2));
11750
11934
  if (values.version) {
11751
- process.stdout.write(`${CLI_VERSION2} (${GIT_HASH2})
11752
- `);
11935
+ outputLine(`${CLI_VERSION2} (${GIT_HASH2})`);
11753
11936
  return;
11754
11937
  }
11755
11938
  if (values.help || positionals.length === 0) {
@@ -11761,11 +11944,25 @@ async function main() {
11761
11944
  const json = v.json ?? false;
11762
11945
  if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
11763
11946
  if (module === "setup") return handleSetupCommand(v);
11947
+ if (module === "diagnose") {
11948
+ let config2;
11949
+ try {
11950
+ config2 = loadProfileConfig({ profile: v.profile, demo: v.demo, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
11951
+ } catch {
11952
+ }
11953
+ return cmdDiagnose(config2, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
11954
+ }
11764
11955
  const config = loadProfileConfig({ profile: v.profile, demo: v.demo, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
11765
- if (config.verbose) printVerboseConfigSummary(config, v.profile);
11766
- if (module === "diagnose") return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
11767
11956
  const client = new OkxRestClient(config);
11768
- const run = createToolRunner(client, config);
11957
+ const baseRunner = createToolRunner(client, config);
11958
+ const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
11959
+ const run = async (toolName, args) => {
11960
+ const result = await baseRunner(toolName, args);
11961
+ if (writeToolNames.has(toolName)) {
11962
+ markFailedIfSCodeError(result.data);
11963
+ }
11964
+ return result;
11965
+ };
11769
11966
  const moduleHandlers = {
11770
11967
  market: () => handleMarketCommand(run, action, rest, v, json),
11771
11968
  account: () => handleAccountCommand(run, action, rest, v, json),
@@ -11778,20 +11975,15 @@ async function main() {
11778
11975
  };
11779
11976
  const handler = moduleHandlers[module];
11780
11977
  if (handler) return handler();
11781
- process.stderr.write(`Unknown command: ${module} ${action ?? ""}
11782
- `);
11978
+ errorLine(`Unknown command: ${module} ${action ?? ""}`);
11783
11979
  process.exitCode = 1;
11784
11980
  }
11785
11981
  main().catch((error) => {
11786
11982
  const payload = toToolErrorPayload(error);
11787
- process.stderr.write(`Error: ${payload.message}
11788
- `);
11789
- if (payload.traceId) process.stderr.write(`TraceId: ${payload.traceId}
11790
- `);
11791
- if (payload.suggestion) process.stderr.write(`Hint: ${payload.suggestion}
11792
- `);
11793
- process.stderr.write(`Version: @okx_ai/okx-trade-cli@${CLI_VERSION2}
11794
- `);
11983
+ errorLine(`Error: ${payload.message}`);
11984
+ if (payload.traceId) errorLine(`TraceId: ${payload.traceId}`);
11985
+ if (payload.suggestion) errorLine(`Hint: ${payload.suggestion}`);
11986
+ errorLine(`Version: @okx_ai/okx-trade-cli@${CLI_VERSION2}`);
11795
11987
  process.exitCode = 1;
11796
11988
  });
11797
11989
  export {