@okx_ai/okx-trade-cli 1.3.4-beta.2 → 1.3.5-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
@@ -1157,8 +1157,8 @@ var PilotManager = class {
1157
1157
  * Apply a Pilot node: set up the custom Agent + base URL.
1158
1158
  *
1159
1159
  * node.ip may be a real IP or a domain (CNAME like *.aliyunddos1021.com).
1160
- * - Real IP use directly in lookup callback
1161
- * - Domain dns.lookup on every connection to get a fresh IP
1160
+ * - Real IP -> use directly in lookup callback
1161
+ * - Domain -> dns.lookup on every connection to get a fresh IP
1162
1162
  */
1163
1163
  applyNode(node, protocol) {
1164
1164
  this.pilotNode = node;
@@ -1508,29 +1508,29 @@ var RateLimiter = class {
1508
1508
  }
1509
1509
  };
1510
1510
  var OKX_CODE_BEHAVIORS = {
1511
- // Rate limit throw RateLimitError
1511
+ // Rate limit -> throw RateLimitError
1512
1512
  "50011": { retry: true, suggestion: "Rate limited. Back off and retry after a delay." },
1513
1513
  "50061": { retry: true, suggestion: "Too many connections. Reduce request frequency and retry." },
1514
- // Server temporarily unavailable retryable
1514
+ // Server temporarily unavailable -> retryable
1515
1515
  "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
1516
1516
  "50004": { retry: true, suggestion: "Endpoint request timeout. Retry later." },
1517
1517
  "50013": { retry: true, suggestion: "System busy. Retry after 1-2 seconds." },
1518
1518
  "50026": { retry: true, suggestion: "System error. Retry in a few minutes." },
1519
- // Region / compliance restriction do not retry
1519
+ // Region / compliance restriction -> do not retry
1520
1520
  "51155": { retry: false, suggestion: "Feature unavailable in your region (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
1521
1521
  "51734": { retry: false, suggestion: "Feature not supported for your KYC country (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
1522
- // Account issues do not retry
1522
+ // Account issues -> do not retry
1523
1523
  "50007": { retry: false, suggestion: "Account suspended. Contact OKX support. Do not retry." },
1524
1524
  "50009": { retry: false, suggestion: "Account blocked by risk control. Contact OKX support. Do not retry." },
1525
1525
  "51009": { retry: false, suggestion: "Account mode not supported for this operation. Check account settings." },
1526
- // API key permission / expiry do not retry
1526
+ // API key permission / expiry -> do not retry
1527
1527
  "50100": { retry: false, suggestion: "API key lacks required permissions. Update API key permissions." },
1528
1528
  "50110": { retry: false, suggestion: "API key expired. Generate a new API key." },
1529
- // Insufficient funds / margin do not retry
1530
- "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." },
1529
+ // Insufficient funds / margin -> do not retry
1530
+ "51008": { retry: false, suggestion: "Insufficient balance in trading account. Check funding account via account_get_asset_balance - funds may be there. Use account_transfer (from=18, to=6) to move funds to trading account, then retry." },
1531
1531
  "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." },
1532
1532
  "51127": { retry: false, suggestion: "Insufficient available margin. Reduce position, add margin, or transfer from funding account (account_transfer from=18 to=6)." },
1533
- // Instrument unavailable do not retry
1533
+ // Instrument unavailable -> do not retry
1534
1534
  "51021": { retry: false, suggestion: "Instrument does not exist. Check instId." },
1535
1535
  "51022": { retry: false, suggestion: "Instrument not available for trading." },
1536
1536
  "51027": { retry: false, suggestion: "Contract has expired." }
@@ -1614,11 +1614,11 @@ var OkxRestClient = class _OkxRestClient {
1614
1614
  }
1615
1615
  }
1616
1616
  /**
1617
- * Dynamic auth determines auth method per request.
1617
+ * Dynamic auth - determines auth method per request.
1618
1618
  *
1619
- * 1. API key in config HMAC signing (no OAuth fallback)
1620
- * 2. OAuth token via okx-auth binary Bearer token
1621
- * 3. Neither throw ConfigError
1619
+ * 1. API key in config -> HMAC signing (no OAuth fallback)
1620
+ * 2. OAuth token via okx-auth binary -> Bearer token
1621
+ * 3. Neither -> throw ConfigError
1622
1622
  */
1623
1623
  async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
1624
1624
  if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
@@ -1772,7 +1772,7 @@ var OkxRestClient = class _OkxRestClient {
1772
1772
  };
1773
1773
  }
1774
1774
  // ---------------------------------------------------------------------------
1775
- // Binary (non-JSON) download reuses auth, proxy, rate-limit, verbose
1775
+ // Binary (non-JSON) download - reuses auth, proxy, rate-limit, verbose
1776
1776
  // ---------------------------------------------------------------------------
1777
1777
  static DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
1778
1778
  // 50 MB
@@ -2384,7 +2384,7 @@ var INDICATOR_CODE_OVERRIDES = {
2384
2384
  // default: NVI_PVI
2385
2385
  "top-long-short": "TOPLONGSHORT"
2386
2386
  // default: TOP_LONG_SHORT
2387
- // Note: range-filter RANGE_FILTER is correct via the default rule; no override needed.
2387
+ // Note: range-filter -> RANGE_FILTER is correct via the default rule; no override needed.
2388
2388
  };
2389
2389
  var KNOWN_INDICATORS = [
2390
2390
  // Moving Averages
@@ -2633,7 +2633,7 @@ var MODULES = [
2633
2633
  "skills"
2634
2634
  ];
2635
2635
  var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES, "skills"];
2636
- var SKILLS_MARKETPLACE_DESC = "OKX Skills Marketplace \u2014 search, install, and manage agent skills";
2636
+ var SKILLS_MARKETPLACE_DESC = "OKX Skills Marketplace - search, install, and manage agent skills";
2637
2637
  var MODULE_DESCRIPTIONS = {
2638
2638
  market: "Market data (ticker, orderbook, candles, trades)",
2639
2639
  spot: "Spot trading (orders, algo orders)",
@@ -2641,18 +2641,18 @@ var MODULE_DESCRIPTIONS = {
2641
2641
  futures: "Futures trading (orders, positions, algo orders, leverage)",
2642
2642
  option: "Options trading (orders, positions, greeks)",
2643
2643
  account: "Account balance, positions, bills, and configuration",
2644
- "earn.savings": "Simple Earn \u2014 flexible savings, fixed-term, and lending",
2645
- "earn.onchain": "On-chain Earn \u2014 staking and DeFi products",
2646
- "earn.dcd": "DCD (Dual Currency Deposit) \u2014 structured products with fixed yield",
2647
- "earn.autoearn": "Auto-earn \u2014 automatically lend, stake, or earn on idle assets",
2648
- "earn.flash": "Flash Earn \u2014 short-window high-yield earn projects",
2649
- event: "Event contracts \u2014 binary prediction markets (YES/NO, UP/DOWN)",
2650
- "bot.grid": "Grid trading bot \u2014 create, monitor, and stop grid orders",
2651
- "bot.dca": "DCA (Martingale) bot \u2014 spot or contract recurring buys",
2644
+ "earn.savings": "Simple Earn - flexible savings, fixed-term, and lending",
2645
+ "earn.onchain": "On-chain Earn - staking and DeFi products",
2646
+ "earn.dcd": "DCD (Dual Currency Deposit) - structured products with fixed yield",
2647
+ "earn.autoearn": "Auto-earn - automatically lend, stake, or earn on idle assets",
2648
+ "earn.flash": "Flash Earn - short-window high-yield earn projects",
2649
+ event: "Event contracts - binary prediction markets (YES/NO, UP/DOWN)",
2650
+ "bot.grid": "Grid trading bot - create, monitor, and stop grid orders",
2651
+ "bot.dca": "DCA (Martingale) bot - spot or contract recurring buys",
2652
2652
  news: "Crypto news, sentiment analysis, and coin trend tracking",
2653
- smartmoney: "Smart money signals \u2014 trader leaderboard, consensus signals, and position analysis",
2653
+ smartmoney: "Smart money signals - trader leaderboard, consensus signals, and position analysis",
2654
2654
  skills: SKILLS_MARKETPLACE_DESC,
2655
- earn: "Earn products \u2014 Simple Earn, On-chain Earn, DCD, Flash Earn, and Auto-Earn",
2655
+ earn: "Earn products - Simple Earn, On-chain Earn, DCD, Flash Earn, and Auto-Earn",
2656
2656
  bot: "Trading bot strategies (grid, dca)",
2657
2657
  config: "Manage CLI configuration profiles",
2658
2658
  setup: "Set up client integrations (Cursor, Windsurf, Claude, etc.)",
@@ -2713,7 +2713,7 @@ function registerAccountTools() {
2713
2713
  },
2714
2714
  type: {
2715
2715
  type: "string",
2716
- description: "0=main account (default), 1=main\u2192sub, 2=sub\u2192main, 3=sub\u2192sub"
2716
+ description: "0=main account (default), 1=main->sub, 2=sub->main, 3=sub->sub"
2717
2717
  },
2718
2718
  subAcct: {
2719
2719
  type: "string",
@@ -3297,7 +3297,7 @@ function computeContracts(p) {
3297
3297
  );
3298
3298
  }
3299
3299
  const contractsStr = contractsRounded.toFixed(lotSzDecimals);
3300
- const conversionNote = isMarginMode ? `Converting ${sz} USDT margin (${leverStr}x leverage) \u2192 ${contractsStr} contracts (notional value \u2248 ${(contractsRounded * contractValue).toFixed(2)} USDT, ctVal=${ctValStr}, lastPx=${lastStr}, lever=${leverStr}, minSz=${minSzStr}, lotSz=${lotSzStr})` : `Converting ${sz} USDT \u2192 ${contractsStr} contracts (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr})`;
3300
+ const conversionNote = isMarginMode ? `Converting ${sz} USDT margin (${leverStr}x leverage) -> ${contractsStr} contracts (notional value \u2248 ${(contractsRounded * contractValue).toFixed(2)} USDT, ctVal=${ctValStr}, lastPx=${lastStr}, lever=${leverStr}, minSz=${minSzStr}, lotSz=${lotSzStr})` : `Converting ${sz} USDT -> ${contractsStr} contracts (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr})`;
3301
3301
  return { contractsStr, conversionNote };
3302
3302
  }
3303
3303
  async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
@@ -3345,7 +3345,7 @@ function registerAlgoTradeTools() {
3345
3345
  {
3346
3346
  name: "swap_place_algo_order",
3347
3347
  module: "swap",
3348
- description: "Place a SWAP/FUTURES algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3348
+ description: "Place a SWAP/FUTURES algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously - first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3349
3349
  isWrite: true,
3350
3350
  inputSchema: {
3351
3351
  type: "object",
@@ -3705,7 +3705,7 @@ function registerFuturesAlgoTools() {
3705
3705
  {
3706
3706
  name: "futures_place_algo_order",
3707
3707
  module: "futures",
3708
- description: "Place a FUTURES delivery algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3708
+ description: "Place a FUTURES delivery algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously - first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3709
3709
  isWrite: true,
3710
3710
  inputSchema: {
3711
3711
  type: "object",
@@ -3930,7 +3930,7 @@ function registerFuturesAlgoTools() {
3930
3930
  {
3931
3931
  name: "futures_amend_algo_order",
3932
3932
  module: "futures",
3933
- description: "Amend a pending FUTURES delivery algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order \u2014 look up algoId via futures_get_algo_orders first.",
3933
+ description: "Amend a pending FUTURES delivery algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via futures_get_algo_orders first.",
3934
3934
  isWrite: true,
3935
3935
  inputSchema: {
3936
3936
  type: "object",
@@ -4383,7 +4383,7 @@ function registerSkillsTools() {
4383
4383
  {
4384
4384
  name: "skills_get_categories",
4385
4385
  module: "skills",
4386
- description: "List all available skill categories in OKX Skills Marketplace. Use the returned categoryId as input to skills_search for category filtering. Do NOT use for searching or downloading skills \u2014 use skills_search or skills_download.",
4386
+ description: "List all available skill categories in OKX Skills Marketplace. Use the returned categoryId as input to skills_search for category filtering. Do NOT use for searching or downloading skills - use skills_search or skills_download.",
4387
4387
  inputSchema: {
4388
4388
  type: "object",
4389
4389
  properties: {},
@@ -4395,7 +4395,7 @@ function registerSkillsTools() {
4395
4395
  {
4396
4396
  name: "skills_search",
4397
4397
  module: "skills",
4398
- description: "Search for skills in OKX Skills Marketplace by keyword or category. To get valid category IDs, call skills_get_categories first. Returns skill names for use with skills_download. Do NOT use for downloading \u2014 use skills_download.",
4398
+ description: "Search for skills in OKX Skills Marketplace by keyword or category. To get valid category IDs, call skills_get_categories first. Returns skill names for use with skills_download. Do NOT use for downloading - use skills_download.",
4399
4399
  inputSchema: {
4400
4400
  type: "object",
4401
4401
  properties: {
@@ -4424,7 +4424,7 @@ function registerSkillsTools() {
4424
4424
  {
4425
4425
  name: "skills_download",
4426
4426
  module: "skills",
4427
- description: "Download a skill package from OKX Skills Marketplace to a local directory. Always call skills_search first to confirm the skill name exists. Downloads the latest approved version. NOTE: Downloads third-party developer content \u2014 does NOT install to agents. For full installation use CLI: okx skill add <name>. Use when the user wants to inspect or manually install a skill package.",
4427
+ description: "Download a skill package from OKX Skills Marketplace to a local directory. Always call skills_search first to confirm the skill name exists. Downloads the latest approved version. NOTE: Downloads third-party developer content - does NOT install to agents. For full installation use CLI: okx skill add <name>. Use when the user wants to inspect or manually install a skill package.",
4428
4428
  inputSchema: {
4429
4429
  type: "object",
4430
4430
  properties: {
@@ -4705,7 +4705,7 @@ function registerGridTools() {
4705
4705
  {
4706
4706
  name: "grid_amend_order",
4707
4707
  module: "bot.grid",
4708
- description: "Amend a running grid bot. [CAUTION] Modifies a running bot. Use grid_list_orders to confirm the bot is running and obtain the algoId before calling.\nSupports two modes, which can be combined in a single call:\n\u2022 Price-range mode (maxPx+minPx+gridNum): change upper/lower price boundary and grid count. Contract grid: if new range requires more margin, pass topUpAmt; omit to auto-use the minimum required. Spot grid: topUpAmt is not supported.\n\u2022 TP/SL mode (instId + any of tpTriggerPx/slTriggerPx/tpRatio/slRatio): update take-profit and/or stop-loss. Pass '-1' to explicitly clear an existing TP or SL. tpTriggerPx/slTriggerPx are absolute prices; tpRatio/slRatio are profit ratios (e.g. '0.1' = 10%).\nWhen both sets of params are provided, both APIs are called sequentially.\nDo NOT use to create a new grid bot \u2014 use grid_create_order instead. Do NOT use to stop a grid bot \u2014 use grid_stop_order instead.",
4708
+ description: "Amend a running grid bot. [CAUTION] Modifies a running bot. Use grid_list_orders to confirm the bot is running and obtain the algoId before calling.\nSupports two modes, which can be combined in a single call:\n\u2022 Price-range mode (maxPx+minPx+gridNum): change upper/lower price boundary and grid count. Contract grid: if new range requires more margin, pass topUpAmt; omit to auto-use the minimum required. Spot grid: topUpAmt is not supported.\n\u2022 TP/SL mode (instId + any of tpTriggerPx/slTriggerPx/tpRatio/slRatio): update take-profit and/or stop-loss. Pass '-1' to explicitly clear an existing TP or SL. tpTriggerPx/slTriggerPx are absolute prices; tpRatio/slRatio are profit ratios (e.g. '0.1' = 10%).\nWhen both sets of params are provided, both APIs are called sequentially.\nDo NOT use to create a new grid bot - use grid_create_order instead. Do NOT use to stop a grid bot - use grid_stop_order instead.",
4709
4709
  isWrite: true,
4710
4710
  inputSchema: {
4711
4711
  type: "object",
@@ -4783,7 +4783,7 @@ function registerGridTools() {
4783
4783
  maxPx,
4784
4784
  minPx: requireString(args, "minPx"),
4785
4785
  gridNum: requireString(args, "gridNum"),
4786
- // API field is "topupAmount" (lowercase u) different from TP/SL mode's "topUpAmt"
4786
+ // API field is "topupAmount" (lowercase u) - different from TP/SL mode's "topUpAmt"
4787
4787
  // Contract grid only; omitting lets the API use the minimum required
4788
4788
  topupAmount: readString(args, "topUpAmt")
4789
4789
  }),
@@ -4804,7 +4804,7 @@ function registerGridTools() {
4804
4804
  tpRatio: readString(args, "tpRatio"),
4805
4805
  slRatio: readString(args, "slRatio"),
4806
4806
  topUpAmt: readString(args, "topUpAmt")
4807
- // API field is "topUpAmt" (uppercase U) different from price-range mode's "topupAmount"
4807
+ // API field is "topUpAmt" (uppercase U) - different from price-range mode's "topupAmount"
4808
4808
  }),
4809
4809
  privateRateLimit("grid_amend_order", 20),
4810
4810
  true
@@ -4828,7 +4828,7 @@ function registerGridTools() {
4828
4828
  {
4829
4829
  name: "grid_stop_order",
4830
4830
  module: "bot.grid",
4831
- description: "Stop a running grid bot. [CAUTION] This stops the strategy and handles open orders/positions according to stopType. Default (stopType='1') closes all positions immediately \u2014 use this for a clean exit. stopType='2' stops the strategy without selling: spot grid keeps all base assets as-is (no sell-back to quote); contract grid cancels all grid orders but leaves the position open for manual close later.",
4831
+ description: "[CAUTION] Stop a grid bot or close its remaining open position \u2014 real trades, irreversible. Workflow: (1) If the user has not specified which bot to stop, call grid_get_orders first and ask the user to confirm which bot before proceeding. (2) Call grid_get_order_details to check the current 'state' field. (3) If state='running' \u2192 call this tool: stopType='1' (default, clean exit) \u2014 spot grid sells all base assets back to quote; contract grid market-closes all positions. stopType='2' (keep assets) \u2014 spot grid keeps base assets as-is; contract grid cancels grid orders but leaves the position open. (4) If state='no_close_position' \u2192 call this tool with stopType='1' to close the remaining open position.",
4832
4832
  isWrite: true,
4833
4833
  inputSchema: {
4834
4834
  type: "object",
@@ -4843,7 +4843,7 @@ function registerGridTools() {
4843
4843
  stopType: {
4844
4844
  type: "string",
4845
4845
  enum: ["1", "2"],
4846
- description: "'1' (default): stop strategy and sell \u2014 spot grid sells all base assets back to quote; contract grid market-closes all positions. '2': stop strategy without selling \u2014 spot grid keeps base assets as-is; contract grid cancels all grid orders but leaves the position open. After stopType='2', the remaining position can be closed manually from the Positions page."
4846
+ description: "'1' (default): stop strategy and sell - spot grid sells all base assets back to quote; contract grid market-closes all positions. '2': stop strategy without selling - spot grid keeps base assets as-is; contract grid cancels all grid orders but leaves the position open. After stopType='2', the remaining position can be closed manually from the Positions page."
4847
4847
  }
4848
4848
  },
4849
4849
  required: ["algoId", "algoOrdType", "instId"]
@@ -4860,7 +4860,7 @@ function registerGridTools() {
4860
4860
  })],
4861
4861
  privateRateLimit("grid_stop_order", 20),
4862
4862
  true
4863
- // retryOnNetworkError: safe to retry already-stopped returns an error but does not harm state
4863
+ // retryOnNetworkError: safe to retry - already-stopped returns an error but does not harm state
4864
4864
  );
4865
4865
  return normalizeWrite(response);
4866
4866
  }
@@ -5006,7 +5006,7 @@ function registerDcaTools() {
5006
5006
  {
5007
5007
  name: "dca_stop_order",
5008
5008
  module: "bot.dca",
5009
- description: "Stop a running DCA bot. [CAUTION] spot_dca needs stopType: 1=sell, 2=keep.",
5009
+ description: "[CAUTION] Stop a DCA bot or close its remaining open position \u2014 real trades, irreversible. Workflow: (1) If the user has not specified which bot to stop, call dca_get_orders first and ask the user to confirm which bot before proceeding. (2) Call dca_get_order_details to check the current 'state' field. (3) If state='running' \u2192 call this tool. (4) If state='no_close_position' \u2192 call this tool with stopType='1' to close the remaining open position. spot_dca requires stopType: 1=sell all tokens, 2=keep tokens.",
5010
5010
  isWrite: true,
5011
5011
  inputSchema: {
5012
5012
  type: "object",
@@ -5161,7 +5161,7 @@ function registerEarnTools() {
5161
5161
  {
5162
5162
  name: "earn_get_savings_balance",
5163
5163
  module: "earn.savings",
5164
- description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history. earn_get_lending_rate_history also returns fixed-term (\u5B9A\u671F) product offers, so one call gives a complete view of both flexible and fixed options. Do NOT use for fixed-term (\u5B9A\u671F) order queries \u2014 use earn_get_fixed_order_list instead.",
5164
+ description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history. earn_get_lending_rate_history also returns fixed-term (\u5B9A\u671F) product offers, so one call gives a complete view of both flexible and fixed options. Do NOT use for fixed-term (\u5B9A\u671F) order queries - use earn_get_fixed_order_list instead.",
5165
5165
  isWrite: false,
5166
5166
  inputSchema: {
5167
5167
  type: "object",
@@ -5185,7 +5185,7 @@ function registerEarnTools() {
5185
5185
  {
5186
5186
  name: "earn_get_fixed_order_list",
5187
5187
  module: "earn.savings",
5188
- description: "Get Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) lending order list. Returns orders sorted by creation time descending. Use this to check status of fixed-term lending orders (pending/earning/expired/settled/cancelled). Do NOT use for flexible earn balance \u2014 use earn_get_savings_balance instead. If the result is empty, do NOT display any fixed-term section in the output.",
5188
+ description: "Get Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) lending order list. Returns orders sorted by creation time descending. Use this to check status of fixed-term lending orders (pending/earning/expired/settled/cancelled). Do NOT use for flexible earn balance - use earn_get_savings_balance instead. If the result is empty, do NOT display any fixed-term section in the output.",
5189
5189
  isWrite: false,
5190
5190
  inputSchema: {
5191
5191
  type: "object",
@@ -5237,7 +5237,7 @@ function registerEarnTools() {
5237
5237
  },
5238
5238
  rate: {
5239
5239
  type: "string",
5240
- description: "Minimum lending rate threshold (annual, decimal). e.g. 0.01 = 1%. Only matched when market rate \u2265 this value. Defaults to 0.01. Keep at 0.01 to maximize matching probability \u2014 do NOT raise this to increase yield, as actual yield (lendingRate) is determined by market supply/demand, not this setting."
5240
+ description: "Minimum lending rate threshold (annual, decimal). e.g. 0.01 = 1%. Only matched when market rate \u2265 this value. Defaults to 0.01. Keep at 0.01 to maximize matching probability - do NOT raise this to increase yield, as actual yield (lendingRate) is determined by market supply/demand, not this setting."
5241
5241
  }
5242
5242
  },
5243
5243
  required: ["ccy", "amt"]
@@ -5304,7 +5304,7 @@ function registerEarnTools() {
5304
5304
  },
5305
5305
  rate: {
5306
5306
  type: "string",
5307
- description: "Minimum lending rate threshold (annual, decimal). e.g. 0.01 = 1%. Only matched when market rate \u2265 this value. Keep at 0.01 to maximize matching probability \u2014 do NOT raise this to increase yield, as actual yield (lendingRate) is determined by market supply/demand, not this setting."
5307
+ description: "Minimum lending rate threshold (annual, decimal). e.g. 0.01 = 1%. Only matched when market rate \u2265 this value. Keep at 0.01 to maximize matching probability - do NOT raise this to increase yield, as actual yield (lendingRate) is determined by market supply/demand, not this setting."
5308
5308
  }
5309
5309
  },
5310
5310
  required: ["ccy", "rate"]
@@ -5366,7 +5366,7 @@ function registerEarnTools() {
5366
5366
  {
5367
5367
  name: "earn_fixed_purchase",
5368
5368
  module: "earn.savings",
5369
- description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation \u2014 do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity \u2014 no early redemption allowed.",
5369
+ description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation - do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity - no early redemption allowed.",
5370
5370
  isWrite: true,
5371
5371
  inputSchema: {
5372
5372
  type: "object",
@@ -5425,7 +5425,7 @@ function registerEarnTools() {
5425
5425
  term,
5426
5426
  offer: offerWithSoldOut,
5427
5427
  currentFlexibleRate: rateArr[0]?.["lendingRate"] ?? null,
5428
- warning: "\u26A0\uFE0F Orders still in 'pending' state can be cancelled before matching completes. Once the status changes to 'earning', funds are LOCKED until maturity \u2014 early redemption is NOT allowed. Please call again with confirm=true to proceed."
5428
+ warning: "\u26A0\uFE0F Orders still in 'pending' state can be cancelled before matching completes. Once the status changes to 'earning', funds are LOCKED until maturity - early redemption is NOT allowed. Please call again with confirm=true to proceed."
5429
5429
  };
5430
5430
  }
5431
5431
  assertNotDemo(context.config, "earn_fixed_purchase");
@@ -5440,7 +5440,7 @@ function registerEarnTools() {
5440
5440
  {
5441
5441
  name: "earn_fixed_redeem",
5442
5442
  module: "earn.savings",
5443
- description: "Redeem Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) order. [CAUTION] Redeems a fixed-term lending order. Always redeems the full order amount. Only orders in 'pending' (\u5339\u914D\u4E2D) state can be redeemed \u2014 orders in 'earning' state are locked until maturity and cannot be redeemed early. Do NOT use for flexible earn redemption \u2014 use earn_savings_redeem instead.",
5443
+ description: "Redeem Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) order. [CAUTION] Redeems a fixed-term lending order. Always redeems the full order amount. Only orders in 'pending' (\u5339\u914D\u4E2D) state can be redeemed - orders in 'earning' state are locked until maturity and cannot be redeemed early. Do NOT use for flexible earn redemption - use earn_savings_redeem instead.",
5444
5444
  isWrite: true,
5445
5445
  inputSchema: {
5446
5446
  type: "object",
@@ -5468,7 +5468,7 @@ function registerEarnTools() {
5468
5468
  {
5469
5469
  name: "earn_get_lending_rate_history",
5470
5470
  module: "earn.savings",
5471
- description: "Query Simple Earn lending rates and fixed-term offers. Use this tool when the user asks about Simple Earn products, current or historical lending rates, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns lending rate history (lendingRate field, newest-first) AND available fixed-term (\u5B9A\u671F) offers with APR, term, min amount, and quota \u2014 one call gives a complete view of both flexible and fixed options. In fixedOffers: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). To get current flexible APY: use limit=1 and read lendingRate.",
5471
+ description: "Query Simple Earn lending rates and fixed-term offers. Use this tool when the user asks about Simple Earn products, current or historical lending rates, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns lending rate history (lendingRate field, newest-first) AND available fixed-term (\u5B9A\u671F) offers with APR, term, min amount, and quota - one call gives a complete view of both flexible and fixed options. In fixedOffers: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). To get current flexible APY: use limit=1 and read lendingRate.",
5472
5472
  isWrite: false,
5473
5473
  inputSchema: {
5474
5474
  type: "object",
@@ -5801,7 +5801,7 @@ function registerOnchainEarnTools() {
5801
5801
  }
5802
5802
  var DCD_CODE_BEHAVIORS = {
5803
5803
  "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
5804
- "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug \u2014 check request parameters." },
5804
+ "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug - check request parameters." },
5805
5805
  "50014": { retry: false, suggestion: "Missing required parameter. Check that all required fields are provided." },
5806
5806
  "50016": { retry: false, suggestion: "notionalCcy does not match productId option type. Use baseCcy for CALL, quoteCcy for PUT." },
5807
5807
  "50026": { retry: true, suggestion: "DCD system error. Retry in a few minutes." },
@@ -6176,7 +6176,7 @@ function registerFlashEarnTools() {
6176
6176
  {
6177
6177
  name: "earn_get_flash_earn_projects",
6178
6178
  module: "earn.flash",
6179
- description: "Get Flash Earn projects. Use this to browse upcoming or in-progress Flash Earn opportunities. Do NOT use for purchase or redeem actions \u2014 Flash Earn is query-only in this module.",
6179
+ description: "Get Flash Earn projects. Use this to browse upcoming or in-progress Flash Earn opportunities. Do NOT use for purchase or redeem actions - Flash Earn is query-only in this module.",
6180
6180
  isWrite: false,
6181
6181
  inputSchema: {
6182
6182
  type: "object",
@@ -6572,7 +6572,7 @@ function handlePlaceOrderError(base, rawArgs, endpoint) {
6572
6572
  const seriesId = extractSeriesId(instId);
6573
6573
  const expiryMs = inferExpiryMsFromInstId(instId);
6574
6574
  const isExpired = expiryMs !== null && expiryMs < Date.now();
6575
- const reason = isExpired ? `The contract (${instId}) has expired.` : `The contract (${instId}) was not found \u2014 it may not exist or has not started yet.`;
6575
+ const reason = isExpired ? `The contract (${instId}) has expired.` : `The contract (${instId}) was not found - it may not exist or has not started yet.`;
6576
6576
  throw new OkxApiError(
6577
6577
  `${reason} Ask the user if they'd like to place the same order on the next session. If yes, call event_get_markets with seriesId=${seriesId} and state=live to find available contracts.`,
6578
6578
  { code: sCode, endpoint }
@@ -6588,17 +6588,17 @@ var OUTCOME_SCHEMA = {
6588
6588
  UP/DOWN direction contracts: UP (price rises during the period) or DOWN (price falls).
6589
6589
  YES/NO price-target or touch contracts: YES (condition met) or NO (condition not met).
6590
6590
  Check the series type from event_get_series to determine which applies.
6591
- NOTE: px is the event contract price (0.01\u20130.99), NOT the underlying asset price. It reflects market-implied probability when actively trading.`
6591
+ NOTE: px is the event contract price (0.01-0.99), NOT the underlying asset price. It reflects market-implied probability when actively trading.`
6592
6592
  };
6593
6593
  function registerEventContractTools() {
6594
6594
  return [
6595
6595
  // -----------------------------------------------------------------------
6596
- // Read-only browse (user-facing) + series / events / markets (internal)
6596
+ // Read-only - browse (user-facing) + series / events / markets (internal)
6597
6597
  // -----------------------------------------------------------------------
6598
6598
  {
6599
6599
  name: "event_browse",
6600
6600
  module: "event",
6601
- description: "Browse currently active (in-progress) event contracts. Call when user asks what event contracts are available to trade. Returns only in-progress contracts (floorStrike set). If a live quote field px is present, it is the event contract price (0.01\u20130.99), not the underlying asset price; it reflects the market-implied probability when actively trading. Grouped by settlement type and underlying. Do NOT use for querying contracts within a specific series \u2014 use event_get_markets with seriesId instead.",
6601
+ description: "Browse currently active (in-progress) event contracts. Call when user asks what event contracts are available to trade. Returns only in-progress contracts (floorStrike set). If a live quote field px is present, it is the event contract price (0.01-0.99), not the underlying asset price; it reflects the market-implied probability when actively trading. Grouped by settlement type and underlying. Do NOT use for querying contracts within a specific series - use event_get_markets with seriesId instead.",
6602
6602
  isWrite: false,
6603
6603
  inputSchema: {
6604
6604
  type: "object",
@@ -6717,7 +6717,7 @@ function registerEventContractTools() {
6717
6717
  {
6718
6718
  name: "event_get_markets",
6719
6719
  module: "event",
6720
- description: "List tradeable contracts within a series. state=live for active contracts, state=expired for settlement results. floorStrike=strike price; px (when present) is the event contract price (0.01\u20130.99), not the underlying asset price \u2014 reflects the market-implied probability when actively trading; outcome pre-translated (pending/YES/NO/UP/DOWN); timestamps UTC+8. Do NOT use for discovering what series are available across all underlyings \u2014 use event_browse instead.",
6720
+ description: "List tradeable contracts within a series. state=live for active contracts, state=expired for settlement results. floorStrike=strike price; px (when present) is the event contract price (0.01-0.99), not the underlying asset price - reflects the market-implied probability when actively trading; outcome pre-translated (pending/YES/NO/UP/DOWN); timestamps UTC+8. Do NOT use for discovering what series are available across all underlyings - use event_browse instead.",
6721
6721
  isWrite: false,
6722
6722
  inputSchema: {
6723
6723
  type: "object",
@@ -6798,7 +6798,7 @@ function registerEventContractTools() {
6798
6798
  {
6799
6799
  name: "event_get_orders",
6800
6800
  module: "event",
6801
- description: "Query event contract orders (open, 7d history, or 3-month archive). outcome pre-translated (YES/NO/UP/DOWN). Do NOT use for trade executions \u2014 use event_get_fills for fill records and settlement outcomes.",
6801
+ description: "Query event contract orders (open, 7d history, or 3-month archive). outcome pre-translated (YES/NO/UP/DOWN). Do NOT use for trade executions - use event_get_fills for fill records and settlement outcomes.",
6802
6802
  isWrite: false,
6803
6803
  inputSchema: {
6804
6804
  type: "object",
@@ -6856,7 +6856,7 @@ function registerEventContractTools() {
6856
6856
  {
6857
6857
  name: "event_get_fills",
6858
6858
  module: "event",
6859
- description: "Get event contract fill history (trade executions and settlement payouts). archive=true for up to 3mo, false (default) for last 3d. outcome pre-translated (YES/NO/UP/DOWN). Each record includes a 'type' field: 'fill' (opening trade) or 'settlement' (expiry payout with settlementResult win/loss and pnl). Do NOT use for order status \u2014 use event_get_orders instead.",
6859
+ description: "Get event contract fill history (trade executions and settlement payouts). archive=true for up to 3mo, false (default) for last 3d. outcome pre-translated (YES/NO/UP/DOWN). Each record includes a 'type' field: 'fill' (opening trade) or 'settlement' (expiry payout with settlementResult win/loss and pnl). Do NOT use for order status - use event_get_orders instead.",
6860
6860
  isWrite: false,
6861
6861
  inputSchema: {
6862
6862
  type: "object",
@@ -6898,15 +6898,15 @@ function registerEventContractTools() {
6898
6898
  }
6899
6899
  },
6900
6900
  // -----------------------------------------------------------------------
6901
- // Private write
6901
+ // Private - write
6902
6902
  // -----------------------------------------------------------------------
6903
6903
  {
6904
6904
  name: "event_place_order",
6905
6905
  module: "event",
6906
6906
  description: `Place an event contract order. [CAUTION] Places a real order. Before placing, call event_get_markets(seriesId, state=live) to obtain the instId of the target contract.
6907
6907
  - outcome: UP/YES (bet price goes up/condition met) or DOWN/NO (bet price goes down/condition not met)
6908
- - For limit orders: px is the event contract price (0.01\u20130.99), NOT the underlying asset price. It reflects market-implied probability when actively trading
6909
- - tdMode is always isolated; speedBump is auto-set per exchange requirement \u2014 do not pass either`,
6908
+ - For limit orders: px is the event contract price (0.01-0.99), NOT the underlying asset price. It reflects market-implied probability when actively trading
6909
+ - tdMode is always isolated; speedBump is auto-set per exchange requirement - do not pass either`,
6910
6910
  isWrite: true,
6911
6911
  inputSchema: {
6912
6912
  type: "object",
@@ -6932,7 +6932,7 @@ function registerEventContractTools() {
6932
6932
  },
6933
6933
  px: {
6934
6934
  type: "string",
6935
- description: "Event contract price (0.01\u20130.99). Required when ordType=limit. Do NOT use for market orders."
6935
+ description: "Event contract price (0.01-0.99). Required when ordType=limit. Do NOT use for market orders."
6936
6936
  }
6937
6937
  },
6938
6938
  required: ["instId", "side", "outcome", "sz"]
@@ -6985,7 +6985,7 @@ function registerEventContractTools() {
6985
6985
  properties: {
6986
6986
  instId: { type: "string", description: "Event contract instrument ID" },
6987
6987
  ordId: { type: "string", description: "Order ID to amend" },
6988
- newPx: { type: "string", description: "New event contract price (0.01\u20130.99). Omit to keep current." },
6988
+ newPx: { type: "string", description: "New event contract price (0.01-0.99). Omit to keep current." },
6989
6989
  newSz: { type: "string", description: "New size in contracts (omit to keep current)" }
6990
6990
  },
6991
6991
  required: ["instId", "ordId"]
@@ -7039,7 +7039,7 @@ function registerEventContractTools() {
7039
7039
  if (sCode === "51001") {
7040
7040
  const expiryMs = inferExpiryMsFromInstId(instId);
7041
7041
  const isExpired = expiryMs !== null && expiryMs < Date.now();
7042
- const reason = isExpired ? `The contract (${instId}) has already expired \u2014 the order was auto-cancelled at settlement. Check event_get_fills to confirm the outcome.` : `Instrument (${instId}) not found. Verify the instId with event_get_markets before retrying.`;
7042
+ const reason = isExpired ? `The contract (${instId}) has already expired - the order was auto-cancelled at settlement. Check event_get_fills to confirm the outcome.` : `Instrument (${instId}) not found. Verify the instId with event_get_markets before retrying.`;
7043
7043
  throw new OkxApiError(reason, { code: sCode, endpoint: response.endpoint });
7044
7044
  }
7045
7045
  }
@@ -7074,25 +7074,25 @@ var SIGNAL_POOL_FILTER_PROPS = {
7074
7074
  type: "string",
7075
7075
  enum: ["PNL_ANY", "PNL_TOP50", "PNL_TOP20", "PNL_TOP5"],
7076
7076
  default: "PNL_ANY",
7077
- description: "PnL percentile gate applied on top of `sortBy`. Naming: `TOP{N}` = top N% percentile (NOT an absolute PnL value). PNL_ANY = no filter; PNL_TOP50 = PnL \u2265 P50 (median); PNL_TOP20 = \u2265 P80; PNL_TOP5 = \u2265 P95. PnL distribution is long-tailed \u2014 use percentile, not absolute thresholds."
7077
+ description: "PnL percentile gate. Naming: `TOP{N}` = top N% percentile (NOT an absolute PnL value). PNL_ANY = no filter; PNL_TOP50 = PnL \u2265 P50 (median); PNL_TOP20 = \u2265 P80; PNL_TOP5 = \u2265 P95. PnL distribution is long-tailed - use percentile, not absolute thresholds."
7078
7078
  },
7079
7079
  winRateTier: {
7080
7080
  type: "string",
7081
7081
  enum: ["WR_ANY", "WR_GE_50", "WR_GE_80"],
7082
7082
  default: "WR_ANY",
7083
- description: "Minimum win-rate gate (absolute thresholds, NOT percentile). Naming: `GE_{N}` = career win-rate \u2265 N% \u2014 distinct from `pnlTier`/`aumTier` `TOP{N}` which are percentiles. WR_ANY = no filter; WR_GE_50 = \u2265 50%; WR_GE_80 = \u2265 80%."
7083
+ description: "Minimum win-rate gate (absolute thresholds, NOT percentile). Naming: `GE_{N}` = career win-rate \u2265 N% - distinct from `pnlTier`/`aumTier` `TOP{N}` which are percentiles. WR_ANY = no filter; WR_GE_50 = \u2265 50%; WR_GE_80 = \u2265 80%."
7084
7084
  },
7085
7085
  maxDrawdownTier: {
7086
7086
  type: "string",
7087
7087
  enum: ["MR_ANY", "MR_LE_20", "MR_LE_50"],
7088
7088
  default: "MR_ANY",
7089
- description: "Maximum-drawdown gate (absolute thresholds, NOT percentile; smaller drawdown = lower risk). Naming: `LE_{N}` = drawdown \u2264 N% \u2014 distinct from `pnlTier`/`aumTier` `TOP{N}` which are percentiles. MR_ANY = no filter; MR_LE_20 = drawdown \u2264 20%; MR_LE_50 = \u2264 50%."
7089
+ description: "Maximum-drawdown gate (absolute thresholds, NOT percentile; smaller drawdown = lower risk). Naming: `LE_{N}` = drawdown \u2264 N% - distinct from `pnlTier`/`aumTier` `TOP{N}` which are percentiles. MR_ANY = no filter; MR_LE_20 = drawdown \u2264 20%; MR_LE_50 = \u2264 50%."
7090
7090
  },
7091
7091
  aumTier: {
7092
7092
  type: "string",
7093
7093
  enum: ["AUM_ANY", "AUM_TOP50", "AUM_TOP20", "AUM_TOP5"],
7094
7094
  default: "AUM_ANY",
7095
- description: "AUM (Assets Under Management) percentile gate. Naming: `TOP{N}` = top N% percentile (NOT an absolute USD amount). AUM_ANY = no filter; AUM_TOP50 = AUM \u2265 P50; AUM_TOP20 = \u2265 P80; AUM_TOP5 = \u2265 P95. AUM is long-tailed \u2014 use percentile, not absolute USD."
7095
+ description: "AUM (Assets Under Management) percentile gate. Naming: `TOP{N}` = top N% percentile (NOT an absolute USD amount). AUM_ANY = no filter; AUM_TOP50 = AUM \u2265 P50; AUM_TOP20 = \u2265 P80; AUM_TOP5 = \u2265 P95. AUM is long-tailed - use percentile, not absolute USD."
7096
7096
  }
7097
7097
  };
7098
7098
  var LEADERBOARD_POOL_FILTER_PROPS = {
@@ -7110,19 +7110,19 @@ var LEADERBOARD_POOL_FILTER_PROPS = {
7110
7110
  },
7111
7111
  minPnl: {
7112
7112
  type: "string",
7113
- description: 'Minimum absolute PnL in USD as a string, e.g. `"10000"` \u2192 traders with PnL \u2265 $10,000. Numeric threshold \u2014 distinct from the signal-side `pnlTier` percentile enum.'
7113
+ description: 'Minimum absolute PnL in USD as a string, e.g. `"10000"` -> traders with PnL \u2265 $10,000. Numeric threshold - distinct from the signal-side `pnlTier` percentile enum.'
7114
7114
  },
7115
7115
  minWinRate: {
7116
7116
  type: "string",
7117
- description: 'Minimum win-rate as a decimal in 0~1 range, passed as a string, e.g. `"0.8"` \u2192 traders with win-rate \u2265 80%. Numeric threshold \u2014 distinct from the signal-side `winRateTier` enum.'
7117
+ description: 'Minimum win-rate as a decimal in 0~1 range, passed as a string, e.g. `"0.8"` -> traders with win-rate \u2265 80%. Numeric threshold - distinct from the signal-side `winRateTier` enum.'
7118
7118
  },
7119
7119
  maxDrawdown: {
7120
7120
  type: "string",
7121
- description: 'Maximum drawdown as a decimal, passed as a string, e.g. `"0.1"` \u2192 traders with drawdown \u2264 10%. Lower = lower risk. Numeric threshold \u2014 distinct from the signal-side `maxDrawdownTier` enum.'
7121
+ description: 'Maximum drawdown as a decimal, passed as a string, e.g. `"0.1"` -> traders with drawdown \u2264 10%. Lower = lower risk. Numeric threshold - distinct from the signal-side `maxDrawdownTier` enum.'
7122
7122
  },
7123
7123
  minAum: {
7124
7124
  type: "string",
7125
- description: 'Minimum AUM (Assets Under Management) in USD as a string, e.g. `"1000"` \u2192 traders with AUM \u2265 $1,000. Numeric threshold \u2014 distinct from the signal-side `aumTier` percentile enum.'
7125
+ description: 'Minimum AUM (Assets Under Management) in USD as a string, e.g. `"1000"` -> traders with AUM \u2265 $1,000. Numeric threshold - distinct from the signal-side `aumTier` percentile enum.'
7126
7126
  }
7127
7127
  };
7128
7128
  var LEADERBOARD_FILTER_UPSTREAM_NAMES = {
@@ -7256,11 +7256,11 @@ var PAGINATION_PROP = {
7256
7256
  }
7257
7257
  };
7258
7258
  var TRADER_ITEM_PROPS = {
7259
- authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other smartmoney_get_trader_* tools." },
7259
+ authorId: { type: "string", description: "Trader's unique ID - pass to other smartmoney_get_trader_* tools." },
7260
7260
  nickName: { type: "string", description: "Display nickname." },
7261
7261
  pnl: { type: "string", description: "Absolute PnL in USD over the requested `period` (numeric string)." },
7262
7262
  pnlRatio: { type: "string", description: 'PnL as a decimal ratio over the requested `period` (e.g. "0.35" = +35%).' },
7263
- asset: { type: "string", description: "AUM (Assets Under Management) in USD \u2014 same field that the input `minAum` filter applies to." },
7263
+ asset: { type: "string", description: "AUM (Assets Under Management) in USD - same field that the input `minAum` filter applies to." },
7264
7264
  winRate: { type: "string", description: "Lifetime win-rate as a decimal (0~1)." },
7265
7265
  maxDrawdown: { type: "string", description: 'Max drawdown as a decimal (e.g. "0.2" = 20%). Lower = lower risk.' },
7266
7266
  onboardDuration: { type: "string", description: "Days the trader has been onboarded on the leaderboard (numeric string)." },
@@ -7289,7 +7289,7 @@ var SIGNAL_ITEM_PROPS = {
7289
7289
  },
7290
7290
  tradersQualified: {
7291
7291
  type: "integer",
7292
- description: "Pool size after applying tier filters (incl. those without positions on this instrument)."
7292
+ description: "Final aggregation pool size after tier filters + top-N truncation by `sortBy`. Bounded by the request's pool size limit (`lmtNum` for `_by_filter` variants, `authorIds.length` for `_by_trader` variants). Smaller than the bound only when the upstream candidate pool (traders passing all tier filters) contains fewer entries than the bound \u2014 typical for low-volume instruments or very strict tier combos."
7293
7293
  },
7294
7294
  longTraders: { type: "integer", description: "Number of pool traders currently long this asset (incl. double-sided)." },
7295
7295
  shortTraders: { type: "integer", description: "Number of pool traders currently short this asset (incl. double-sided)." },
@@ -7299,19 +7299,19 @@ var SIGNAL_ITEM_PROPS = {
7299
7299
  properties: {
7300
7300
  longNotionalUsdt: {
7301
7301
  type: "string",
7302
- description: "Sum of long-side notional in USDT, weighted by each trader's ENTRY PRICE (price_avg), not mark price. Moves only when positions are opened / closed / scaled \u2014 stays constant when positions are unchanged."
7302
+ description: "Sum of long-side notional in USDT, weighted by each trader's ENTRY PRICE (price_avg), not mark price. Moves only when positions are opened / closed / scaled - stays constant when positions are unchanged."
7303
7303
  },
7304
7304
  shortNotionalUsdt: {
7305
7305
  type: "string",
7306
- description: "Sum of short-side notional in USDT, weighted by each trader's ENTRY PRICE (price_avg), not mark price. Moves only when positions are opened / closed / scaled \u2014 stays constant when positions are unchanged."
7306
+ description: "Sum of short-side notional in USDT, weighted by each trader's ENTRY PRICE (price_avg), not mark price. Moves only when positions are opened / closed / scaled - stays constant when positions are unchanged."
7307
7307
  },
7308
7308
  netNotionalUsdt: {
7309
7309
  type: "string",
7310
- description: "Net directional notional in USDT = long \u2212 short. Weighted by each trader's ENTRY PRICE (price_avg), not mark price \u2014 reflects position scaling, not underlying price movement."
7310
+ description: "Net directional notional in USDT = long \u2212 short. Weighted by each trader's ENTRY PRICE (price_avg), not mark price - reflects position scaling, not underlying price movement."
7311
7311
  },
7312
7312
  totalNotionalUsdt: {
7313
7313
  type: "string",
7314
- description: "Gross notional in USDT = long + short. Weighted by each trader's ENTRY PRICE (price_avg), not mark price \u2014 reflects position scaling (open / close / add), not underlying price movement. Stays constant across buckets when traders hold positions unchanged."
7314
+ description: "Gross notional in USDT = long + short. Weighted by each trader's ENTRY PRICE (price_avg), not mark price - reflects position scaling (open / close / add), not underlying price movement. Stays constant across buckets when traders hold positions unchanged."
7315
7315
  },
7316
7316
  totalNotionalVs24h: {
7317
7317
  type: "string",
@@ -7341,7 +7341,7 @@ var SIGNAL_ITEM_PROPS = {
7341
7341
  },
7342
7342
  weightedLongRatio: {
7343
7343
  type: "string",
7344
- description: "Notional-weighted long ratio = \u03A3(long_notional) / \u03A3(notional). Notional uses each trader's ENTRY PRICE (price_avg), not mark price \u2014 ratio shifts only when positions are scaled. NULL when no notional."
7344
+ description: "Notional-weighted long ratio = \u03A3(long_notional) / \u03A3(notional). Notional uses each trader's ENTRY PRICE (price_avg), not mark price - ratio shifts only when positions are scaled. NULL when no notional."
7345
7345
  },
7346
7346
  weightedShortRatio: {
7347
7347
  type: "string",
@@ -7376,7 +7376,7 @@ var SIGNAL_HISTORY_ITEM_PROPS = {
7376
7376
  },
7377
7377
  weightedLongRatio: {
7378
7378
  type: "string",
7379
- description: "Notional-weighted long ratio at this bucket = \u03A3(long_notional) / \u03A3(notional). Decimal 0~1. Notional uses each trader's ENTRY PRICE (price_avg), not mark price \u2014 ratio shifts only when positions are scaled."
7379
+ description: "Notional-weighted long ratio at this bucket = \u03A3(long_notional) / \u03A3(notional). Decimal 0~1. Notional uses each trader's ENTRY PRICE (price_avg), not mark price - ratio shifts only when positions are scaled."
7380
7380
  },
7381
7381
  weightedShortRatio: {
7382
7382
  type: "string",
@@ -7396,13 +7396,16 @@ var SIGNAL_HISTORY_ITEM_PROPS = {
7396
7396
  },
7397
7397
  netNotionalUsdt: {
7398
7398
  type: "string",
7399
- description: "Net directional notional in USDT at this bucket = long notional \u2212 short notional. Weighted by each trader's ENTRY PRICE (price_avg), not mark price \u2014 reflects position scaling, not underlying price movement."
7399
+ description: "Net directional notional in USDT at this bucket = long notional \u2212 short notional. Weighted by each trader's ENTRY PRICE (price_avg), not mark price - reflects position scaling, not underlying price movement."
7400
7400
  },
7401
7401
  totalNotionalUsdt: {
7402
7402
  type: "string",
7403
- description: "Gross notional in USDT at this bucket = long notional + short notional. Weighted by each trader's ENTRY PRICE (price_avg), not mark price \u2014 tracks capital deployed (rising = adding, falling = retreating). Stays constant across buckets when traders hold positions unchanged."
7403
+ description: "Gross notional in USDT at this bucket = long notional + short notional. Weighted by each trader's ENTRY PRICE (price_avg), not mark price - tracks capital deployed (rising = adding, falling = retreating). Stays constant across buckets when traders hold positions unchanged."
7404
+ },
7405
+ tradersQualified: {
7406
+ type: "integer",
7407
+ description: "Final aggregation pool size in this bucket after tier filters + top-N truncation by `sortBy`. Bounded by the request's pool size limit (`lmtNum` for `_by_filter` variants, `authorIds.length` for `_by_trader` variants). The funnel + top-N selection runs once per query (not per bucket); each bucket reuses the same selected pool for position aggregation. Smaller than the bound only when the upstream candidate pool underflows."
7404
7408
  },
7405
- tradersQualified: { type: "integer", description: "Pool size after applying tier filters (includes traders without a position)." },
7406
7409
  dataVersion: { type: "string", description: "Snapshot version key in `yyyyMMddHH` UTC (10-digit, e.g. `2026042820`)." }
7407
7410
  };
7408
7411
  function registerSmartmoneyTools() {
@@ -7414,7 +7417,7 @@ function registerSmartmoneyTools() {
7414
7417
  {
7415
7418
  name: "smartmoney_get_traders_by_filter",
7416
7419
  module: "smartmoney",
7417
- description: "Leaderboard ranking of OKX smart-money traders, filtered by pool conditions and ranked by `sortBy`. Use when: discovering top performers by criteria (PnL / win-rate / drawdown / AUM). See also: `smartmoney_get_performance_by_trader` (lookup by ID), `smartmoney_search_trader` (lookup by nickname). Note: `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8, different from signal tools' 10-digit UTC `asOfTime`/`dataVersion` \u2014 do not cross-pass.",
7420
+ description: "Leaderboard ranking of OKX smart-money traders, filtered by pool conditions and ranked by `sortBy`. Use when: discovering top performers by criteria (PnL / win-rate / drawdown / AUM). See also: `smartmoney_get_performance_by_trader` (lookup by ID), `smartmoney_search_trader` (lookup by nickname). Note: `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8, different from signal tools' 10-digit UTC `asOfTime`/`dataVersion` - do not cross-pass.",
7418
7421
  isWrite: false,
7419
7422
  outputSchema: envelope(
7420
7423
  { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
@@ -7431,16 +7434,16 @@ function registerSmartmoneyTools() {
7431
7434
  properties: {
7432
7435
  updateTime: {
7433
7436
  type: "string",
7434
- description: 'Snapshot version key \u2014 12-digit `yyyyMMddHHmm` (UTC+8) as a string, e.g. `"202604301815"`. Omit to query the latest snapshot (refreshed every ~5 min).'
7437
+ description: 'Snapshot version key - 12-digit `yyyyMMddHHmm` (UTC+8) as a string, e.g. `"202604301815"`. Omit to query the latest snapshot (refreshed every ~5 min).'
7435
7438
  },
7436
7439
  ...LEADERBOARD_POOL_FILTER_PROPS,
7437
7440
  after: {
7438
7441
  type: "string",
7439
- description: 'Pagination cursor (older page) \u2014 pass the `authorId` of the last item from the previous page as a string, e.g. `"872913470357110787"`. Cursor anchors on `authorId` while preserving the current `sortBy` order.'
7442
+ description: 'Pagination cursor (older page) - pass the `authorId` of the last item from the previous page as a string, e.g. `"872913470357110787"`. Cursor anchors on `authorId` while preserving the current `sortBy` order.'
7440
7443
  },
7441
7444
  before: {
7442
7445
  type: "string",
7443
- description: 'Pagination cursor (newer page) \u2014 pass the `authorId` of the first item from the previous page as a string, e.g. `"872913470357110787"`. Cursor anchors on `authorId` while preserving the current `sortBy` order.'
7446
+ description: 'Pagination cursor (newer page) - pass the `authorId` of the first item from the previous page as a string, e.g. `"872913470357110787"`. Cursor anchors on `authorId` while preserving the current `sortBy` order.'
7444
7447
  },
7445
7448
  limit: {
7446
7449
  type: "integer",
@@ -7480,7 +7483,7 @@ function registerSmartmoneyTools() {
7480
7483
  {
7481
7484
  name: "smartmoney_get_performance_by_trader",
7482
7485
  module: "smartmoney",
7483
- description: "PnL / win-rate / drawdown profile for one or more traders looked up by `authorIds`. Use when: caller already has trader IDs and needs their performance metrics. See also: `smartmoney_search_trader` (resolve nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (criteria-based discovery). Note: response `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8 \u2014 do not pass to signal-side tools' `asOfTime` (10-digit UTC).",
7486
+ description: "PnL / win-rate / drawdown profile for one or more traders looked up by `authorIds`. Use when: caller already has trader IDs and needs their performance metrics. See also: `smartmoney_search_trader` (resolve nickname -> authorId), `smartmoney_get_traders_by_filter` (criteria-based discovery). Note: response `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8 - do not pass to signal-side tools' `asOfTime` (10-digit UTC).",
7484
7487
  isWrite: false,
7485
7488
  outputSchema: envelope(
7486
7489
  { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
@@ -7550,7 +7553,7 @@ function registerSmartmoneyTools() {
7550
7553
  {
7551
7554
  name: "smartmoney_get_trader_positions",
7552
7555
  module: "smartmoney",
7553
- description: "Currently-open positions held by a single trader (direction, size, leverage, entry, conviction). Use when: inspecting what a top trader is holding RIGHT NOW. See also: `smartmoney_get_trader_positions_history` (closed positions), `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7556
+ description: "Currently-open positions held by a single trader (direction, size, leverage, entry, conviction). Use when: inspecting what a top trader is holding RIGHT NOW. See also: `smartmoney_get_trader_positions_history` (closed positions), `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7554
7557
  isWrite: false,
7555
7558
  outputSchema: envelope({
7556
7559
  type: "array",
@@ -7570,9 +7573,9 @@ function registerSmartmoneyTools() {
7570
7573
  direction: {
7571
7574
  type: "string",
7572
7575
  enum: ["long", "short"],
7573
- description: 'Derived clean direction (`long` | `short`) \u2014 handler computes this from `posSide` + sign of `pos` so agents do not have to branch on the `posSide="net"` net-mode case.'
7576
+ description: 'Derived clean direction (`long` | `short`) - handler computes this from `posSide` + sign of `pos` so agents do not have to branch on the `posSide="net"` net-mode case.'
7574
7577
  },
7575
- posCcy: { type: "string", description: 'Position currency \u2014 the asset being held, e.g. "BTC".' },
7578
+ posCcy: { type: "string", description: 'Position currency - the asset being held, e.g. "BTC".' },
7576
7579
  quoteCcy: { type: "string", description: 'Quote currency the position is priced/settled in, e.g. "USDT".' },
7577
7580
  pos: {
7578
7581
  type: "string",
@@ -7601,7 +7604,7 @@ function registerSmartmoneyTools() {
7601
7604
  },
7602
7605
  instId: {
7603
7606
  type: "string",
7604
- description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
7607
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") - the handler extracts the base currency for the upstream filter.'
7605
7608
  }
7606
7609
  },
7607
7610
  required: ["authorId"]
@@ -7631,7 +7634,7 @@ function registerSmartmoneyTools() {
7631
7634
  {
7632
7635
  name: "smartmoney_get_trader_positions_history",
7633
7636
  module: "smartmoney",
7634
- description: "Closed-position history of a single trader, paginated by `posId` cursor. Use when: studying realized PnL pattern, holding duration, win/loss streaks, or how positions ended (closed vs liquidated). See also: `smartmoney_get_trader_positions` (currently-open), `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7637
+ description: "Closed-position history of a single trader, paginated by `posId` cursor. Use when: studying realized PnL pattern, holding duration, win/loss streaks, or how positions ended (closed vs liquidated). See also: `smartmoney_get_trader_positions` (currently-open), `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7635
7638
  isWrite: false,
7636
7639
  outputSchema: envelope(
7637
7640
  {
@@ -7647,7 +7650,7 @@ function registerSmartmoneyTools() {
7647
7650
  },
7648
7651
  ctVal: {
7649
7652
  type: "string",
7650
- description: "Contract face value \u2014 USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
7653
+ description: "Contract face value - USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
7651
7654
  },
7652
7655
  posSide: {
7653
7656
  type: "string",
@@ -7714,15 +7717,15 @@ function registerSmartmoneyTools() {
7714
7717
  },
7715
7718
  instId: {
7716
7719
  type: "string",
7717
- description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
7720
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") - the handler extracts the base currency for the upstream filter.'
7718
7721
  },
7719
7722
  after: {
7720
7723
  type: "string",
7721
- description: 'Pagination cursor (older) \u2014 returns positions with `posId` smaller than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7724
+ description: 'Pagination cursor (older) - returns positions with `posId` smaller than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7722
7725
  },
7723
7726
  before: {
7724
7727
  type: "string",
7725
- description: 'Pagination cursor (newer) \u2014 returns positions with `posId` greater than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7728
+ description: 'Pagination cursor (newer) - returns positions with `posId` greater than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7726
7729
  },
7727
7730
  limit: {
7728
7731
  type: "integer",
@@ -7768,7 +7771,7 @@ function registerSmartmoneyTools() {
7768
7771
  {
7769
7772
  name: "smartmoney_get_trader_orders_history",
7770
7773
  module: "smartmoney",
7771
- description: "Recent orders/fills placed by a single trader (direction, size, price, leverage), paginated by `ordId` cursor. Aligned with the cross-module `*_get_orders` family. Use when: tracking a top trader's latest trade activity. See also: `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7774
+ description: "Recent orders/fills placed by a single trader (direction, size, price, leverage), paginated by `ordId` cursor. Aligned with the cross-module `*_get_orders` family. Use when: tracking a top trader's latest trade activity. See also: `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7772
7775
  isWrite: false,
7773
7776
  outputSchema: envelope(
7774
7777
  {
@@ -7832,15 +7835,15 @@ function registerSmartmoneyTools() {
7832
7835
  },
7833
7836
  instId: {
7834
7837
  type: "string",
7835
- description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
7838
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") - the handler extracts the base currency for the upstream filter.'
7836
7839
  },
7837
7840
  after: {
7838
7841
  type: "string",
7839
- description: 'Pagination cursor (older) \u2014 returns trades with `ordId` smaller than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7842
+ description: 'Pagination cursor (older) - returns trades with `ordId` smaller than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7840
7843
  },
7841
7844
  before: {
7842
7845
  type: "string",
7843
- description: 'Pagination cursor (newer) \u2014 returns trades with `ordId` greater than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7846
+ description: 'Pagination cursor (newer) - returns trades with `ordId` greater than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7844
7847
  },
7845
7848
  limit: {
7846
7849
  type: "integer",
@@ -7894,7 +7897,7 @@ function registerSmartmoneyTools() {
7894
7897
  items: {
7895
7898
  type: "object",
7896
7899
  properties: {
7897
- authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other `smartmoney_get_trader_*` tools." },
7900
+ authorId: { type: "string", description: "Trader's unique ID - pass to other `smartmoney_get_trader_*` tools." },
7898
7901
  nickName: { type: "string", description: "Display nickname matched against the keyword." },
7899
7902
  followerCount: { type: "string", description: "OKX-platform follower count (numeric string; Twitter followers excluded). Sort key." }
7900
7903
  }
@@ -7936,7 +7939,7 @@ function registerSmartmoneyTools() {
7936
7939
  {
7937
7940
  name: "smartmoney_get_signal_overview_by_filter",
7938
7941
  module: "smartmoney",
7939
- description: "Multi-asset smart-money consensus signals (long/short ratio, weighted entry, capital flow, deltas vs 1h/24h/7d), aggregated over a tier-filtered trader pool (PnL / win-rate / drawdown / AUM). Pick instruments via `topInstruments` OR `instCcyList` \u2014 exactly one. Snapshot time auto-resolved to current hour. **Linear (USDT/USDS-margined) contracts only \u2014 coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted from the aggregation.** Use when: latest cross-asset consensus from a criteria-defined pool. See also: `smartmoney_get_signal_overview_by_trader` (restrict pool to specific traders), `smartmoney_get_signal_trend_by_filter` (time-series instead of latest snapshot).",
7942
+ description: "Multi-asset smart-money consensus signals (long/short ratio, weighted entry, capital flow, deltas vs 1h/24h/7d), aggregated over a tier-filtered trader pool (PnL / win-rate / drawdown / AUM). Pick instruments via `topInstruments` OR `instCcyList` - exactly one. Snapshot time auto-resolved to current hour. **Linear (USDT/USDS-margined) contracts only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted from the aggregation.** Use when: latest cross-asset consensus from a criteria-defined pool. See also: `smartmoney_get_signal_overview_by_trader` (restrict pool to specific traders), `smartmoney_get_signal_trend_by_filter` (time-series instead of latest snapshot).",
7940
7943
  isWrite: false,
7941
7944
  outputSchema: envelope({
7942
7945
  type: "array",
@@ -7957,7 +7960,7 @@ function registerSmartmoneyTools() {
7957
7960
  type: "array",
7958
7961
  items: { type: "string" },
7959
7962
  minItems: 1,
7960
- description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`. Scope: only USDT-margined and USDS-margined (linear) instruments \u2014 e.g. `BTC` covers `BTC-USDT-SWAP` + `BTC-USDS-SWAP`. Coin-margined contracts (`BTC-USD-SWAP`, `BTC-USD-DELIVERY`) are NOT included; positions a trader holds in those instruments are silently dropped from the aggregation.'
7963
+ description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`. Scope: only USDT-margined and USDS-margined (linear) instruments - e.g. `BTC` covers `BTC-USDT-SWAP` + `BTC-USDS-SWAP`. Coin-margined contracts (`BTC-USD-SWAP`, `BTC-USD-DELIVERY`) are NOT included; positions a trader holds in those instruments are silently dropped from the aggregation.'
7961
7964
  },
7962
7965
  ...SIGNAL_POOL_FILTER_PROPS,
7963
7966
  lmtNum: {
@@ -7965,7 +7968,7 @@ function registerSmartmoneyTools() {
7965
7968
  minimum: 1,
7966
7969
  maximum: 2e3,
7967
7970
  default: 100,
7968
- description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Larger pool = stronger signal but slower. Default 100 is fine for most cases."
7971
+ description: "Upper bound on `tradersQualified` (final aggregation pool size). Candidates pass through tier filters (`pnlTier` / `winRateTier` / `aumTier` / `maxDrawdownTier`), then are truncated to top-N by `sortBy` (DESC). `tradersQualified` \u2264 `lmtNum` always; equals `lmtNum` unless candidate pool underflows (rare ccy / strict tier combos). Default 100; values above ~1500 add latency without benefit (exceeds typical candidate pool size)."
7969
7972
  }
7970
7973
  },
7971
7974
  required: ["sortBy", "period"]
@@ -7977,7 +7980,7 @@ function registerSmartmoneyTools() {
7977
7980
  if (instCcyList && topInstrumentsRaw !== void 0) {
7978
7981
  throw actionableError(
7979
7982
  '"topInstruments" and "instCcyList" are mutually exclusive.',
7980
- "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7983
+ "Pass exactly one - `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7981
7984
  );
7982
7985
  }
7983
7986
  const response = await context.client.privateGet(
@@ -7996,7 +7999,7 @@ function registerSmartmoneyTools() {
7996
7999
  {
7997
8000
  name: "smartmoney_get_signal_overview_by_trader",
7998
8001
  module: "smartmoney",
7999
- description: "Multi-asset smart-money signals aggregated over a hand-picked set of traders (`authorIds`). Pick instruments via `topInstruments` OR `instCcyList`. Capability tier filters (pnlTier / winRateTier / etc.) not exposed \u2014 backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only \u2014 a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are silently excluded from the aggregation, even when those positions are large.** Use `smartmoney_get_trader_positions` if the full position book is needed. Use when: caller already knows which traders to follow and wants their cross-asset consensus at the latest hour. See also: `smartmoney_get_signal_overview_by_filter` (criteria-defined pool), `smartmoney_get_signal_trend_by_trader` (time-series), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds).",
8002
+ description: "Multi-asset smart-money signals aggregated over a hand-picked set of traders (`authorIds`). Pick instruments via `topInstruments` OR `instCcyList`. Capability tier filters (pnlTier / winRateTier / etc.) not exposed - backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only - a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are silently excluded from the aggregation, even when those positions are large.** Use `smartmoney_get_trader_positions` if the full position book is needed. Use when: caller already knows which traders to follow and wants their cross-asset consensus at the latest hour. See also: `smartmoney_get_signal_overview_by_filter` (criteria-defined pool), `smartmoney_get_signal_trend_by_trader` (time-series), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds).",
8000
8003
  isWrite: false,
8001
8004
  outputSchema: envelope({
8002
8005
  type: "array",
@@ -8023,7 +8026,7 @@ function registerSmartmoneyTools() {
8023
8026
  type: "array",
8024
8027
  items: { type: "string" },
8025
8028
  minItems: 1,
8026
- description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`. Scope: only USDT-margined and USDS-margined (linear) instruments \u2014 e.g. `BTC` covers `BTC-USDT-SWAP` + `BTC-USDS-SWAP`. Coin-margined contracts (`BTC-USD-SWAP`, `BTC-USD-DELIVERY`) are NOT included; the trader\'s positions in those instruments are silently dropped.'
8029
+ description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`. Scope: only USDT-margined and USDS-margined (linear) instruments - e.g. `BTC` covers `BTC-USDT-SWAP` + `BTC-USDS-SWAP`. Coin-margined contracts (`BTC-USD-SWAP`, `BTC-USD-DELIVERY`) are NOT included; the trader\'s positions in those instruments are silently dropped.'
8027
8030
  },
8028
8031
  sortBy: {
8029
8032
  type: "string",
@@ -8056,7 +8059,7 @@ function registerSmartmoneyTools() {
8056
8059
  if (instCcyList && topInstrumentsRaw !== void 0) {
8057
8060
  throw actionableError(
8058
8061
  '"topInstruments" and "instCcyList" are mutually exclusive.',
8059
- "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
8062
+ "Pass exactly one - `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
8060
8063
  );
8061
8064
  }
8062
8065
  const response = await context.client.privateGet(
@@ -8078,7 +8081,7 @@ function registerSmartmoneyTools() {
8078
8081
  {
8079
8082
  name: "smartmoney_get_signal_trend_by_filter",
8080
8083
  module: "smartmoney",
8081
- description: "Time-series of single-asset smart-money signal across hourly/daily buckets, aggregated over a tier-filtered trader pool. Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). **Linear (USDT/USDS-margined) contracts only \u2014 coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted.** Use when: tracking how long/short conviction and capital evolve over time (smart money adding exposure or retreating). See also: `smartmoney_get_signal_overview_by_filter` (latest snapshot only), `smartmoney_get_signal_trend_by_trader` (restrict to specific traders). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` \u2014 do not cross-pass.",
8084
+ description: "Time-series of single-asset smart-money signal across hourly/daily buckets, aggregated over a tier-filtered trader pool. Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). **Linear (USDT/USDS-margined) contracts only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted.** Use when: tracking how long/short conviction and capital evolve over time (smart money adding exposure or retreating). See also: `smartmoney_get_signal_overview_by_filter` (latest snapshot only), `smartmoney_get_signal_trend_by_trader` (restrict to specific traders). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` - do not cross-pass.",
8082
8085
  isWrite: false,
8083
8086
  outputSchema: envelope({
8084
8087
  type: "array",
@@ -8090,11 +8093,11 @@ function registerSmartmoneyTools() {
8090
8093
  properties: {
8091
8094
  instCcy: {
8092
8095
  type: "string",
8093
- description: 'Base currency to scope the time-series, e.g. "BTC". Required. Scope: USDT-margined and USDS-margined (linear) instruments only \u2014 coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are NOT included.'
8096
+ description: 'Base currency to scope the time-series, e.g. "BTC". Required. Scope: USDT-margined and USDS-margined (linear) instruments only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are NOT included.'
8094
8097
  },
8095
8098
  asOfTime: {
8096
8099
  type: "string",
8097
- description: 'Anchor snapshot time \u2014 10-digit `yyyyMMddHH` UTC as a string, e.g. `"2026050100"`. Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
8100
+ description: 'Anchor snapshot time - 10-digit `yyyyMMddHH` UTC as a string, e.g. `"2026050100"`. Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
8098
8101
  },
8099
8102
  granularity: {
8100
8103
  type: "string",
@@ -8115,7 +8118,7 @@ function registerSmartmoneyTools() {
8115
8118
  minimum: 1,
8116
8119
  maximum: 2e3,
8117
8120
  default: 100,
8118
- description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Default 100, max 2000."
8121
+ description: "Upper bound on `tradersQualified` per bucket (final aggregation pool size). Candidates pass through tier filters (`pnlTier` / `winRateTier` / `aumTier` / `maxDrawdownTier`), then are truncated to top-N by `sortBy` (DESC). `tradersQualified` \u2264 `lmtNum` always; equals `lmtNum` unless candidate pool underflows (rare ccy / strict tier combos). Default 100; values above ~1500 add latency without benefit (exceeds typical candidate pool size)."
8119
8122
  }
8120
8123
  },
8121
8124
  required: ["instCcy", "granularity", "sortBy", "period"]
@@ -8150,7 +8153,7 @@ function registerSmartmoneyTools() {
8150
8153
  {
8151
8154
  name: "smartmoney_get_signal_trend_by_trader",
8152
8155
  module: "smartmoney",
8153
- description: "Time-series of single-asset smart-money signal aggregated over a hand-picked set of traders (`authorIds`). Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). Capability tier filters (pnlTier / winRateTier / etc.) not exposed \u2014 backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only \u2014 a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions on the requested base ccy are silently excluded from each bucket.** Use `smartmoney_get_trader_positions` to inspect the full position book. Use when: tracking how a specific group of traders has evolved their long/short consensus over time on one coin. See also: `smartmoney_get_signal_trend_by_filter` (criteria-defined pool), `smartmoney_get_signal_overview_by_trader` (latest snapshot only), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` \u2014 do not cross-pass.",
8156
+ description: "Time-series of single-asset smart-money signal aggregated over a hand-picked set of traders (`authorIds`). Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). Capability tier filters (pnlTier / winRateTier / etc.) not exposed - backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only - a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions on the requested base ccy are silently excluded from each bucket.** Use `smartmoney_get_trader_positions` to inspect the full position book. Use when: tracking how a specific group of traders has evolved their long/short consensus over time on one coin. See also: `smartmoney_get_signal_trend_by_filter` (criteria-defined pool), `smartmoney_get_signal_overview_by_trader` (latest snapshot only), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` - do not cross-pass.",
8154
8157
  isWrite: false,
8155
8158
  outputSchema: envelope({
8156
8159
  type: "array",
@@ -8168,11 +8171,11 @@ function registerSmartmoneyTools() {
8168
8171
  },
8169
8172
  instCcy: {
8170
8173
  type: "string",
8171
- description: 'Base currency to scope the time-series, e.g. "BTC". Required. Scope: USDT-margined and USDS-margined (linear) instruments only \u2014 coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions held by the trader set are NOT included.'
8174
+ description: 'Base currency to scope the time-series, e.g. "BTC". Required. Scope: USDT-margined and USDS-margined (linear) instruments only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions held by the trader set are NOT included.'
8172
8175
  },
8173
8176
  asOfTime: {
8174
8177
  type: "string",
8175
- description: 'Anchor snapshot time \u2014 10-digit `yyyyMMddHH` UTC as a string, e.g. `"2026050100"`. Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
8178
+ description: 'Anchor snapshot time - 10-digit `yyyyMMddHH` UTC as a string, e.g. `"2026050100"`. Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
8176
8179
  },
8177
8180
  granularity: {
8178
8181
  type: "string",
@@ -8252,7 +8255,7 @@ function buildContractTradeTools(cfg) {
8252
8255
  {
8253
8256
  name: n("place_order"),
8254
8257
  module,
8255
- description: `Place ${label} order. Attach TP/SL via tpTriggerPx/slTriggerPx. Before placing, use market_get_instruments to get ctVal (contract face value) \u2014 do NOT assume contract sizes. [CAUTION] Executes real trades.`,
8258
+ description: `Place ${label} order. Attach TP/SL via tpTriggerPx/slTriggerPx. Before placing, use market_get_instruments to get ctVal (contract face value) - do NOT assume contract sizes. [CAUTION] Executes real trades.`,
8256
8259
  isWrite: true,
8257
8260
  inputSchema: {
8258
8261
  type: "object",
@@ -8592,10 +8595,10 @@ function buildContractTradeTools(cfg) {
8592
8595
  module,
8593
8596
  description: `Set leverage for a ${label} instrument or position. [CAUTION] Changes risk parameters.
8594
8597
  Scenarios (SWAP/FUTURES only):
8595
- \u2022 cross + any instId under the index \u2192 sets leverage at the index level
8596
- \u2022 isolated + buy-sell (net) posMode \u2192 instId only
8597
- \u2022 isolated + long-short (hedge) posMode \u2192 instId + posSide=long|short (BOTH directions must be set separately)
8598
- Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/FUTURES \u2014 the request will be rejected by OKX. Use account_get_config first if unsure of the account's margin mode.`,
8598
+ \u2022 cross + any instId under the index -> sets leverage at the index level
8599
+ \u2022 isolated + buy-sell (net) posMode -> instId only
8600
+ \u2022 isolated + long-short (hedge) posMode -> instId + posSide=long|short (BOTH directions must be set separately)
8601
+ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/FUTURES - the request will be rejected by OKX. Use account_get_config first if unsure of the account's margin mode.`,
8599
8602
  isWrite: true,
8600
8603
  inputSchema: {
8601
8604
  type: "object",
@@ -8603,13 +8606,13 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
8603
8606
  instId: { type: "string", description: instIdExample },
8604
8607
  lever: {
8605
8608
  type: "string",
8606
- description: "Leverage multiplier as a positive number string, e.g. '10'. Max value depends on the instrument (query market_get_instruments \u2192 lever)."
8609
+ description: "Leverage multiplier as a positive number string, e.g. '10'. Max value depends on the instrument (query market_get_instruments -> lever)."
8607
8610
  },
8608
8611
  mgnMode: { type: "string", enum: ["cross", "isolated"] },
8609
8612
  posSide: {
8610
8613
  type: "string",
8611
8614
  enum: ["long", "short"],
8612
- description: "REQUIRED when mgnMode=isolated AND the account is in hedge (long/short) position mode. Use 'long' or 'short' \u2014 setting one side does NOT auto-apply to the other. Omit entirely for one-way (net) position mode or for cross margin."
8615
+ description: "REQUIRED when mgnMode=isolated AND the account is in hedge (long/short) position mode. Use 'long' or 'short' - setting one side does NOT auto-apply to the other. Omit entirely for one-way (net) position mode or for cross margin."
8613
8616
  }
8614
8617
  },
8615
8618
  required: ["instId", "lever", "mgnMode"]
@@ -9425,12 +9428,12 @@ var OI_BARS = ["5m", "15m", "1H", "4H", "1D"];
9425
9428
  function registerMarketFilterTools() {
9426
9429
  return [
9427
9430
  // ─────────────────────────────────────────────────────────────────────────
9428
- // market_filter /api/v5/aigc/mcp/market-filter
9431
+ // market_filter - /api/v5/aigc/mcp/market-filter
9429
9432
  // ─────────────────────────────────────────────────────────────────────────
9430
9433
  {
9431
9434
  name: "market_filter",
9432
9435
  module: "market",
9433
- description: "Screen / rank instruments across SPOT, SWAP, or FUTURES by multi-dimensional criteria: price range, 24h change %, market cap, 24h volume (USD), funding rate (SWAP), open interest (USD), listing time. Returns ranked rows with full ticker snapshot. Use to find top movers, high-OI contracts, newly listed tokens, etc. No credentials required. Do NOT use to get OI change rankings across contracts \u2014 use market_filter_oi_change instead. Do NOT use to get OI time series for a single instrument \u2014 use market_get_oi_history instead.",
9436
+ description: "Screen / rank instruments across SPOT, SWAP, or FUTURES by multi-dimensional criteria: price range, 24h change %, market cap, 24h volume (USD), funding rate (SWAP), open interest (USD), listing time. Returns ranked rows with full ticker snapshot. Use to find top movers, high-OI contracts, newly listed tokens, etc. No credentials required. Do NOT use to get OI change rankings across contracts - use market_filter_oi_change instead. Do NOT use to get OI time series for a single instrument - use market_get_oi_history instead.",
9434
9437
  isWrite: false,
9435
9438
  inputSchema: {
9436
9439
  type: "object",
@@ -9514,7 +9517,7 @@ function registerMarketFilterTools() {
9514
9517
  sortBy: {
9515
9518
  type: "string",
9516
9519
  enum: ["last", "chg24hPct", "marketCapUsd", "volUsd24h", "fundingRate", "oiUsd", "listTime"],
9517
- description: "Sort field. Default: volUsd24h. Note: marketCapUsd is only meaningful for SPOT (null for SWAP/FUTURES). To rank by OI *change* (oiDeltaPct / absOiDeltaPct), use market_filter_oi_change \u2014 market_filter only sorts by the current snapshot."
9520
+ description: "Sort field. Default: volUsd24h. Note: marketCapUsd is only meaningful for SPOT (null for SWAP/FUTURES). To rank by OI *change* (oiDeltaPct / absOiDeltaPct), use market_filter_oi_change - market_filter only sorts by the current snapshot."
9518
9521
  },
9519
9522
  sortOrder: {
9520
9523
  type: "string",
@@ -9562,12 +9565,12 @@ function registerMarketFilterTools() {
9562
9565
  }
9563
9566
  },
9564
9567
  // ─────────────────────────────────────────────────────────────────────────
9565
- // market_get_oi_history /api/v5/aigc/mcp/oi-history
9568
+ // market_get_oi_history - /api/v5/aigc/mcp/oi-history
9566
9569
  // ─────────────────────────────────────────────────────────────────────────
9567
9570
  {
9568
9571
  name: "market_get_oi_history",
9569
9572
  module: "market",
9570
- description: "Get open interest (OI) history time series for a single SWAP or FUTURES instrument. Returns per-bar OI in contracts, base currency and USD, plus bar-over-bar delta and delta %. Useful for tracking how OI evolves around price moves. No credentials required. Do NOT use to compare OI changes across multiple contracts \u2014 use market_filter_oi_change instead. Do NOT use to screen instruments by current OI level \u2014 use market_filter instead.",
9573
+ description: "Get open interest (OI) history time series for a single SWAP or FUTURES instrument. Returns per-bar OI in contracts, base currency and USD, plus bar-over-bar delta and delta %. Useful for tracking how OI evolves around price moves. No credentials required. Do NOT use to compare OI changes across multiple contracts - use market_filter_oi_change instead. Do NOT use to screen instruments by current OI level - use market_filter instead.",
9571
9574
  isWrite: false,
9572
9575
  inputSchema: {
9573
9576
  type: "object",
@@ -9609,12 +9612,12 @@ function registerMarketFilterTools() {
9609
9612
  }
9610
9613
  },
9611
9614
  // ─────────────────────────────────────────────────────────────────────────
9612
- // market_filter_oi_change /api/v5/aigc/mcp/oi-change-filter
9615
+ // market_filter_oi_change - /api/v5/aigc/mcp/oi-change-filter
9613
9616
  // ─────────────────────────────────────────────────────────────────────────
9614
9617
  {
9615
9618
  name: "market_filter_oi_change",
9616
9619
  module: "market",
9617
- description: "Find SWAP or FUTURES instruments with significant open interest changes over a given bar window. Returns ranked rows with current OI (USD), previous OI (USD), OI delta (USD and %), price change %, 24h volume and funding rate. Ideal for spotting unusual accumulation/distribution or confirming trend momentum. No credentials required. Do NOT use to get OI time series for a single instrument \u2014 use market_get_oi_history instead. Do NOT use to screen by current OI absolute level or other non-OI metrics \u2014 use market_filter instead.",
9620
+ description: "Find SWAP or FUTURES instruments with significant open interest changes over a given bar window. Returns ranked rows with current OI (USD), previous OI (USD), OI delta (USD and %), price change %, 24h volume and funding rate. Ideal for spotting unusual accumulation/distribution or confirming trend momentum. No credentials required. Do NOT use to get OI time series for a single instrument - use market_get_oi_history instead. Do NOT use to screen by current OI absolute level or other non-OI metrics - use market_filter instead.",
9618
9621
  isWrite: false,
9619
9622
  inputSchema: {
9620
9623
  type: "object",
@@ -9646,7 +9649,7 @@ function registerMarketFilterTools() {
9646
9649
  sortBy: {
9647
9650
  type: "string",
9648
9651
  enum: ["oiUsd", "oiDeltaUsd", "oiDeltaPct", "absOiDeltaPct", "volUsd24h", "fundingRate", "last"],
9649
- description: "Sort field. Default: oiDeltaPct (largest movers first, signed \u2014 longs and shorts separate). Use absOiDeltaPct to sort by |oiDeltaPct| (largest-magnitude moves regardless of direction). fundingRate is also supported for SWAP. Do NOT use the market_filter tool's sort fields (chg24hPct, marketCapUsd, listTime) here \u2014 they are not in the OI-change Row."
9652
+ description: "Sort field. Default: oiDeltaPct (largest movers first, signed - longs and shorts separate). Use absOiDeltaPct to sort by |oiDeltaPct| (largest-magnitude moves regardless of direction). fundingRate is also supported for SWAP. Do NOT use the market_filter tool's sort fields (chg24hPct, marketCapUsd, listTime) here - they are not in the OI-change Row."
9650
9653
  },
9651
9654
  sortOrder: {
9652
9655
  type: "string",
@@ -9681,7 +9684,7 @@ function registerMarketFilterTools() {
9681
9684
  }
9682
9685
  },
9683
9686
  // ─────────────────────────────────────────────────────────────────────────
9684
- // market_get_pair_spread /api/v5/aigc/mcp/pair-spread
9687
+ // market_get_pair_spread - /api/v5/aigc/mcp/pair-spread
9685
9688
  // ─────────────────────────────────────────────────────────────────────────
9686
9689
  {
9687
9690
  name: "market_get_pair_spread",
@@ -9967,7 +9970,7 @@ var D_COINS_SENTIMENT = 'Comma-separated uppercase ticker symbols, max 20 (e.g.
9967
9970
  var D_LANGUAGE = "Content language: zh-CN or en-US. Infer from user's message. No server default.";
9968
9971
  var D_BEGIN = "Start time, Unix epoch milliseconds. API defaults to 72 hours ago when omitted. Pass explicitly for older topics (e.g. 'last 30 days'). Max range: 180 days. Parse relative time if given.";
9969
9972
  var D_END = "End time, Unix epoch milliseconds. Parse relative time if given. Omit for no upper bound.";
9970
- var D_IMPORTANCE = "Importance filter: 'low' returns all news (both low and high importance); 'high' narrows to major/breaking news only. Omitted \u2192 server default (high-only). Default to 'low' for broad browsing; pass 'high' only when the user explicitly asks for major news.";
9973
+ var D_IMPORTANCE = "Importance filter: 'low' returns all news (both low and high importance); 'high' narrows to major/breaking news only. Omitted -> server default (high-only). Default to 'low' for broad browsing; pass 'high' only when the user explicitly asks for major news.";
9971
9974
  var D_PLATFORM = "Filter by news source. Use values from news_get_domains (e.g. blockbeats, odaily_flash). Omit for all sources.";
9972
9975
  var D_LIMIT = "Number of results (default 10, max 50).";
9973
9976
  function registerNewsTools() {
@@ -10066,7 +10069,7 @@ function registerNewsTools() {
10066
10069
  {
10067
10070
  name: "news_search",
10068
10071
  module: "news",
10069
- description: "Search crypto news by keyword with optional filters. Use when user provides specific search terms: 'SEC ETF news', 'stablecoin regulation'. Keyword is optional \u2014 pass sentiment alone to browse by sentiment direction. For coin-only queries prefer news_get_by_coin.",
10072
+ description: "Search crypto news by keyword with optional filters. Use when user provides specific search terms: 'SEC ETF news', 'stablecoin regulation'. Keyword is optional - pass sentiment alone to browse by sentiment direction. For coin-only queries prefer news_get_by_coin.",
10070
10073
  isWrite: false,
10071
10074
  inputSchema: {
10072
10075
  type: "object",
@@ -10176,7 +10179,7 @@ function registerNewsTools() {
10176
10179
  {
10177
10180
  name: "news_get_coin_sentiment",
10178
10181
  module: "news",
10179
- description: "Get sentiment snapshot or time-series trend for coins. Returns bullish/bearish ratios and mention counts. Pass trendPoints for trend data (1h\u219224 points, 4h\u21926, 24h\u21927). Use when user asks about coin sentiment, sentiment trend, or how bullish/bearish a coin is. For ranking all coins by sentiment, use news_get_sentiment_ranking instead.",
10182
+ description: "Get sentiment snapshot or time-series trend for coins. Returns bullish/bearish ratios and mention counts. Pass trendPoints for trend data (1h->24 points, 4h->6, 24h->7). Use when user asks about coin sentiment, sentiment trend, or how bullish/bearish a coin is. For ranking all coins by sentiment, use news_get_sentiment_ranking instead.",
10180
10183
  isWrite: false,
10181
10184
  inputSchema: {
10182
10185
  type: "object",
@@ -10189,7 +10192,7 @@ function registerNewsTools() {
10189
10192
  },
10190
10193
  trendPoints: {
10191
10194
  type: "number",
10192
- description: "Trend data points. Pass for time-series trend; omit for snapshot. Guide: 1h\u219224, 4h\u21926, 24h\u21927."
10195
+ description: "Trend data points. Pass for time-series trend; omit for snapshot. Guide: 1h->24, 4h->6, 24h->7."
10193
10196
  }
10194
10197
  },
10195
10198
  required: ["coins"]
@@ -10256,7 +10259,7 @@ function registerNewsTools() {
10256
10259
  {
10257
10260
  name: "news_list_calendar_regions",
10258
10261
  module: "news",
10259
- description: "List all valid region values for the economic calendar. Returns a string array of snake_case region codes. Call this when economic-calendar returns empty results to verify the region value, or to help the user pick a valid region. Do NOT use to list news source platforms \u2014 use news_get_domains instead.",
10262
+ description: "List all valid region values for the economic calendar. Returns a string array of snake_case region codes. Call this when economic-calendar returns empty results to verify the region value, or to help the user pick a valid region. Do NOT use to list news source platforms - use news_get_domains instead.",
10260
10263
  isWrite: false,
10261
10264
  inputSchema: { type: "object", properties: {}, required: [] },
10262
10265
  handler: async () => ({ data: CALENDAR_REGIONS })
@@ -10264,15 +10267,15 @@ function registerNewsTools() {
10264
10267
  {
10265
10268
  name: "news_get_economic_calendar",
10266
10269
  module: "news",
10267
- description: "Get macro-economic calendar data (GDP, CPI, NFP, interest rate decisions, PMI, etc.). Returns scheduled and released economic events with forecast, previous, and actual values. Use when user asks about economic calendar, macro data, or specific indicators like NFP/CPI/GDP/FOMC. Do NOT use for news articles or sentiment \u2014 use news_get_latest or news_search instead.",
10270
+ description: "Get macro-economic calendar data (GDP, CPI, NFP, interest rate decisions, PMI, etc.). Returns scheduled and released economic events with forecast, previous, and actual values. Use when user asks about economic calendar, macro data, or specific indicators like NFP/CPI/GDP/FOMC. Do NOT use for news articles or sentiment - use news_get_latest or news_search instead.",
10268
10271
  isWrite: false,
10269
10272
  inputSchema: {
10270
10273
  type: "object",
10271
10274
  properties: {
10272
10275
  region: { type: "string", description: "Country/region filter in snake_case (e.g. united_states, euro_area, japan). Invalid values return empty results silently. If empty results, call news_list_calendar_regions to verify the value." },
10273
10276
  importance: { type: "string", enum: ["1", "2", "3"], description: "Importance level: 1=low, 2=medium, 3=high. Omit for all levels." },
10274
- before: { type: "string", description: "Lower time bound \u2014 returns events NEWER than this timestamp (reversed semantics). Pair with 'after' for future-event windows. Unix ms." },
10275
- after: { type: "string", description: "Upper time bound \u2014 returns events OLDER than this timestamp (reversed semantics). Default=now (returns past events). Pair with 'before' for a bounded window. Unix ms." },
10277
+ before: { type: "string", description: "Lower time bound - returns events NEWER than this timestamp (reversed semantics). Pair with 'after' for future-event windows. Unix ms." },
10278
+ after: { type: "string", description: "Upper time bound - returns events OLDER than this timestamp (reversed semantics). Default=now (returns past events). Pair with 'before' for a bounded window. Unix ms." },
10276
10279
  limit: { type: "number", minimum: 1, maximum: 100, description: "Number of results (default 100, max 100)." }
10277
10280
  },
10278
10281
  required: []
@@ -10430,7 +10433,7 @@ function registerOptionAlgoTools() {
10430
10433
  {
10431
10434
  name: "option_amend_algo_order",
10432
10435
  module: "option",
10433
- description: "Amend a pending OPTION algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order \u2014 look up algoId via option_get_algo_orders first.",
10436
+ description: "Amend a pending OPTION algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via option_get_algo_orders first.",
10434
10437
  isWrite: true,
10435
10438
  inputSchema: {
10436
10439
  type: "object",
@@ -10593,7 +10596,7 @@ function registerOptionTools() {
10593
10596
  {
10594
10597
  name: "option_place_order",
10595
10598
  module: "option",
10596
- description: "Place OPTION order. instId: {uly}-{expiry}-{strike}-C/P, e.g. BTC-USD-241227-50000-C. Before placing, use market_get_instruments to get ctVal (contract face value) \u2014 do NOT assume contract sizes. [CAUTION] Executes real trades.",
10599
+ description: "Place OPTION order. instId: {uly}-{expiry}-{strike}-C/P, e.g. BTC-USD-241227-50000-C. Before placing, use market_get_instruments to get ctVal (contract face value) - do NOT assume contract sizes. [CAUTION] Executes real trades.",
10597
10600
  isWrite: true,
10598
10601
  inputSchema: {
10599
10602
  type: "object",
@@ -11367,7 +11370,7 @@ function registerSpotTradeTools() {
11367
11370
  {
11368
11371
  name: "spot_amend_algo_order",
11369
11372
  module: "spot",
11370
- description: "Amend a pending spot algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order \u2014 look up algoId via spot_get_algo_orders first.",
11373
+ description: "Amend a pending spot algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via spot_get_algo_orders first.",
11371
11374
  isWrite: true,
11372
11375
  inputSchema: {
11373
11376
  type: "object",
@@ -11436,7 +11439,7 @@ function registerSpotTradeTools() {
11436
11439
  {
11437
11440
  name: "spot_get_algo_orders",
11438
11441
  module: "spot",
11439
- description: "Query spot algo orders (TP/SL) \u2014 pending or history.",
11442
+ description: "Query spot algo orders (TP/SL) - pending or history.",
11440
11443
  isWrite: false,
11441
11444
  inputSchema: {
11442
11445
  type: "object",
@@ -11729,16 +11732,16 @@ function registerSpotTradeTools() {
11729
11732
  }
11730
11733
  },
11731
11734
  // ── set_leverage (SPOT margin: instId-level isolated OR ccy-level cross) ──
11732
- // Covers OKX scenarios 15 (everything except SWAP/FUTURES, which are in
11735
+ // Covers OKX scenarios 1-5 (everything except SWAP/FUTURES, which are in
11733
11736
  // contract-trade.ts). Callers supply exactly one of {instId, ccy}:
11734
- // • instId + isolated scenario 1 (pair-level margin)
11735
- // • instId + cross scenario 3 (contract-mode pair-level cross margin)
11736
- // • ccy + cross scenarios 2 / 4 / 5 (spot/multi-ccy/PM currency-level cross)
11737
+ // • instId + isolated -> scenario 1 (pair-level margin)
11738
+ // • instId + cross -> scenario 3 (contract-mode pair-level cross margin)
11739
+ // • ccy + cross -> scenarios 2 / 4 / 5 (spot/multi-ccy/PM currency-level cross)
11737
11740
  // Not applicable: posSide (spot has no long/short hedge).
11738
11741
  {
11739
11742
  name: "spot_set_leverage",
11740
11743
  module: "spot",
11741
- description: "Set leverage for SPOT margin trading. Provide exactly ONE of instId (pair-level) or ccy (currency-level cross, requires borrow-enabled account / multi-ccy / portfolio margin). [CAUTION] Changes risk parameters.\nScenarios:\n \u2022 instId + mgnMode=isolated \u2192 pair-level isolated margin\n \u2022 instId + mgnMode=cross \u2192 pair-level cross margin (contract-mode account)\n \u2022 ccy + mgnMode=cross \u2192 currency-level cross margin (spot-with-borrow / multi-ccy / portfolio margin)\nWhen ccy is supplied, mgnMode MUST be cross. posSide is never applicable to spot margin.",
11744
+ description: "Set leverage for SPOT margin trading. Provide exactly ONE of instId (pair-level) or ccy (currency-level cross, requires borrow-enabled account / multi-ccy / portfolio margin). [CAUTION] Changes risk parameters.\nScenarios:\n \u2022 instId + mgnMode=isolated -> pair-level isolated margin\n \u2022 instId + mgnMode=cross -> pair-level cross margin (contract-mode account)\n \u2022 ccy + mgnMode=cross -> currency-level cross margin (spot-with-borrow / multi-ccy / portfolio margin)\nWhen ccy is supplied, mgnMode MUST be cross. posSide is never applicable to spot margin.",
11742
11745
  isWrite: true,
11743
11746
  inputSchema: {
11744
11747
  type: "object",
@@ -11753,7 +11756,7 @@ function registerSpotTradeTools() {
11753
11756
  },
11754
11757
  lever: {
11755
11758
  type: "string",
11756
- description: "Leverage multiplier as a positive number string, e.g. '3'. Max depends on the pair (query market_get_instruments \u2192 lever) or the account policy for ccy-level."
11759
+ description: "Leverage multiplier as a positive number string, e.g. '3'. Max depends on the pair (query market_get_instruments -> lever) or the account policy for ccy-level."
11757
11760
  },
11758
11761
  mgnMode: {
11759
11762
  type: "string",
@@ -11774,7 +11777,7 @@ function registerSpotTradeTools() {
11774
11777
  }
11775
11778
  if (instId && ccy) {
11776
11779
  throw new ValidationError(
11777
- `Parameters "instId" and "ccy" are mutually exclusive \u2014 provide only one. instId sets pair-level leverage; ccy sets currency-level cross margin leverage.`
11780
+ `Parameters "instId" and "ccy" are mutually exclusive - provide only one. instId sets pair-level leverage; ccy sets currency-level cross margin leverage.`
11778
11781
  );
11779
11782
  }
11780
11783
  const leverRaw = requireString(args, "lever");
@@ -11821,7 +11824,7 @@ function registerSwapTradeTools() {
11821
11824
  {
11822
11825
  name: "swap_amend_algo_order",
11823
11826
  module: "swap",
11824
- description: "Amend a pending SWAP/FUTURES algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order \u2014 look up algoId via swap_get_algo_orders first.",
11827
+ description: "Amend a pending SWAP/FUTURES algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via swap_get_algo_orders first.",
11825
11828
  isWrite: true,
11826
11829
  inputSchema: {
11827
11830
  type: "object",
@@ -11963,9 +11966,9 @@ function readFullConfig() {
11963
11966
  throw new ConfigError(
11964
11967
  `Failed to parse ${path42}: ${err instanceof Error ? err.message : String(err)}`,
11965
11968
  `If your passphrase or keys contain special characters:
11966
- - Contains # \\ " \u2192 use single quotes: passphrase = 'your#pass'
11967
- - Contains ' \u2192 use double quotes: passphrase = "your'pass"
11968
- - Contains both \u2192 use triple quotes: passphrase = '''your'#pass'''
11969
+ - Contains # \\ " -> use single quotes: passphrase = 'your#pass'
11970
+ - Contains ' -> use double quotes: passphrase = "your'pass"
11971
+ - Contains both -> use triple quotes: passphrase = '''your'#pass'''
11969
11972
  Or re-run: okx config init`
11970
11973
  );
11971
11974
  }
@@ -12108,6 +12111,9 @@ async function loadConfig(cli) {
12108
12111
  }
12109
12112
  var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
12110
12113
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
12114
+ var NEGATIVE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
12115
+ var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
12116
+ var FETCH_TIMEOUT_MS = 3e3;
12111
12117
  function readCache2() {
12112
12118
  try {
12113
12119
  if (existsSync4(CACHE_FILE)) {
@@ -12124,7 +12130,73 @@ function writeCache2(cache) {
12124
12130
  } catch {
12125
12131
  }
12126
12132
  }
12133
+ function resolveNpmRegistry() {
12134
+ const envRegistry = process.env.npm_config_registry;
12135
+ if (envRegistry) {
12136
+ return envRegistry.endsWith("/") ? envRegistry : `${envRegistry}/`;
12137
+ }
12138
+ for (const filePath of buildNpmrcCandidates()) {
12139
+ const registry = readNpmrcRegistry(filePath);
12140
+ if (registry) {
12141
+ return registry.endsWith("/") ? registry : `${registry}/`;
12142
+ }
12143
+ }
12144
+ return DEFAULT_REGISTRY;
12145
+ }
12146
+ function buildNpmrcCandidates() {
12147
+ const paths = [];
12148
+ const seen = /* @__PURE__ */ new Set();
12149
+ const add = (p) => {
12150
+ if (!seen.has(p)) {
12151
+ seen.add(p);
12152
+ paths.push(p);
12153
+ }
12154
+ };
12155
+ let dir = process.cwd();
12156
+ const root = dir.startsWith("/") ? "/" : dir.slice(0, 3);
12157
+ while (true) {
12158
+ add(join8(dir, ".npmrc"));
12159
+ if (dir === root) break;
12160
+ const parent = join8(dir, "..");
12161
+ if (parent === dir) break;
12162
+ dir = parent;
12163
+ }
12164
+ add(join8(homedir6(), ".npmrc"));
12165
+ if (process.platform !== "win32") {
12166
+ add("/etc/npmrc");
12167
+ }
12168
+ return paths;
12169
+ }
12170
+ function readNpmrcRegistry(filePath) {
12171
+ try {
12172
+ if (!existsSync4(filePath)) return null;
12173
+ const lines = readFileSync5(filePath, "utf-8").split(/\r?\n/);
12174
+ for (const line of lines) {
12175
+ const trimmed = line.trim();
12176
+ if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
12177
+ const eqIdx = trimmed.indexOf("=");
12178
+ const key = trimmed.slice(0, eqIdx).trim();
12179
+ const value = trimmed.slice(eqIdx + 1).trim();
12180
+ if (key === "registry" && value) return value;
12181
+ }
12182
+ } catch {
12183
+ }
12184
+ return null;
12185
+ }
12186
+ async function fetchFromRegistry(packageName, suffix = "") {
12187
+ const registry = resolveNpmRegistry();
12188
+ const url = `${registry}${encodeURIComponent(packageName)}${suffix}`;
12189
+ try {
12190
+ return await fetch(url, {
12191
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
12192
+ headers: { accept: "application/json" }
12193
+ });
12194
+ } catch {
12195
+ return null;
12196
+ }
12197
+ }
12127
12198
  function isNewerVersion(current, latest) {
12199
+ if (!latest) return false;
12128
12200
  const parse2 = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10));
12129
12201
  const [cMaj, cMin, cPat] = parse2(current);
12130
12202
  const [lMaj, lMin, lPat] = parse2(latest);
@@ -12134,14 +12206,8 @@ function isNewerVersion(current, latest) {
12134
12206
  }
12135
12207
  async function fetchDistTags(packageName) {
12136
12208
  try {
12137
- const controller = new AbortController();
12138
- const timeout = setTimeout(() => controller.abort(), 3e3);
12139
- const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
12140
- signal: controller.signal,
12141
- headers: { accept: "application/json" }
12142
- });
12143
- clearTimeout(timeout);
12144
- if (!res.ok) return null;
12209
+ const res = await fetchFromRegistry(packageName);
12210
+ if (!res || !res.ok) return null;
12145
12211
  const data = await res.json();
12146
12212
  return data["dist-tags"] ?? null;
12147
12213
  } catch {
@@ -12150,14 +12216,8 @@ async function fetchDistTags(packageName) {
12150
12216
  }
12151
12217
  async function fetchLatestVersion(packageName) {
12152
12218
  try {
12153
- const controller = new AbortController();
12154
- const timeout = setTimeout(() => controller.abort(), 3e3);
12155
- const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
12156
- signal: controller.signal,
12157
- headers: { accept: "application/json" }
12158
- });
12159
- clearTimeout(timeout);
12160
- if (!res.ok) return null;
12219
+ const res = await fetchFromRegistry(packageName, "/latest");
12220
+ if (!res || !res.ok) return null;
12161
12221
  const data = await res.json();
12162
12222
  return data.version ?? null;
12163
12223
  } catch {
@@ -12166,26 +12226,31 @@ async function fetchLatestVersion(packageName) {
12166
12226
  }
12167
12227
  function refreshCacheInBackground(packageName) {
12168
12228
  fetchLatestVersion(packageName).then((latest) => {
12169
- if (!latest) return;
12170
12229
  const cache = readCache2();
12171
- cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
12230
+ if (latest) {
12231
+ cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
12232
+ } else {
12233
+ cache[packageName] = { latestVersion: null, checkedAt: Date.now(), failed: true };
12234
+ }
12172
12235
  writeCache2(cache);
12173
12236
  }).catch(() => {
12174
12237
  });
12175
12238
  }
12176
12239
  function checkForUpdates(packageName, currentVersion) {
12240
+ if (process.env.OKX_UPDATE_CHECK === "false") return;
12177
12241
  const cache = readCache2();
12178
12242
  const entry = cache[packageName];
12179
- if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
12243
+ if (entry && entry.latestVersion && isNewerVersion(currentVersion, entry.latestVersion)) {
12180
12244
  process.stderr.write(
12181
12245
  `
12182
- Update available for ${packageName}: ${currentVersion} \u2192 ${entry.latestVersion}
12246
+ Update available for ${packageName}: ${currentVersion} -> ${entry.latestVersion}
12183
12247
  Run: npm install -g ${packageName}
12184
12248
 
12185
12249
  `
12186
12250
  );
12187
12251
  }
12188
- if (!entry || Date.now() - entry.checkedAt > CHECK_INTERVAL_MS) {
12252
+ const ttl = entry?.failed ? NEGATIVE_CHECK_INTERVAL_MS : CHECK_INTERVAL_MS;
12253
+ if (!entry || Date.now() - entry.checkedAt > ttl) {
12189
12254
  refreshCacheInBackground(packageName);
12190
12255
  }
12191
12256
  }
@@ -12370,7 +12435,7 @@ function mergeJsonConfig(configPath, serverName, entry) {
12370
12435
  }
12371
12436
  const backupPath = configPath + ".bak";
12372
12437
  fs3.copyFileSync(configPath, backupPath);
12373
- process.stdout.write(` Backup \u2192 ${backupPath}
12438
+ process.stdout.write(` Backup -> ${backupPath}
12374
12439
  `);
12375
12440
  }
12376
12441
  if (typeof data.mcpServers !== "object" || data.mcpServers === null) {
@@ -12441,7 +12506,7 @@ function validateRedirect(res, requestUrl, redirectCount, maxRedirects) {
12441
12506
  }
12442
12507
  const location = res.headers.location;
12443
12508
  if (requestUrl.startsWith("https") && !location.startsWith("https")) {
12444
- throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
12509
+ throw new Error("Refused HTTPS -> HTTP redirect downgrade");
12445
12510
  }
12446
12511
  return location;
12447
12512
  }
@@ -13039,10 +13104,10 @@ async function cmdAuthLogin(args) {
13039
13104
  status: "skipped",
13040
13105
  reason: "api_key_configured",
13041
13106
  profile: apiKeyProfile,
13042
- message: `API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`
13107
+ message: `API key already configured (profile: ${apiKeyProfile}). OAuth login skipped - API key will be used automatically.`
13043
13108
  }));
13044
13109
  } else {
13045
- outputLine(`API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`);
13110
+ outputLine(`API key already configured (profile: ${apiKeyProfile}). OAuth login skipped - API key will be used automatically.`);
13046
13111
  }
13047
13112
  return;
13048
13113
  }
@@ -13561,7 +13626,7 @@ function checkToolCount(report, configuredClients, getSpecs = allToolSpecs) {
13561
13626
  if (totalCount > limits.total) {
13562
13627
  warn(
13563
13628
  "tool count",
13564
- `${totalCount} tools loaded \u2014 exceeds ${name} limit (${limits.total} total / ${limits.perServer} per server)`,
13629
+ `${totalCount} tools loaded - exceeds ${name} limit (${limits.total} total / ${limits.perServer} per server)`,
13565
13630
  [
13566
13631
  `Use --modules to reduce: okx-trade-mcp --modules ${defaultModulesArg} (${defaultCount} tools)`
13567
13632
  ]
@@ -13571,7 +13636,7 @@ function checkToolCount(report, configuredClients, getSpecs = allToolSpecs) {
13571
13636
  } else if (totalCount > limits.perServer) {
13572
13637
  warn(
13573
13638
  "tool count",
13574
- `${totalCount} tools loaded \u2014 exceeds ${name} per-server limit (${limits.perServer})`,
13639
+ `${totalCount} tools loaded - exceeds ${name} per-server limit (${limits.perServer})`,
13575
13640
  [
13576
13641
  `Use --modules to reduce: okx-trade-mcp --modules ${defaultModulesArg} (${defaultCount} tools)`
13577
13642
  ]
@@ -13638,7 +13703,7 @@ function checkMcpLogs(report) {
13638
13703
  } catch (_e) {
13639
13704
  }
13640
13705
  }
13641
- ok("log file", "(not found \u2014 logs only appear after MCP server has been started)");
13706
+ ok("log file", "(not found - logs only appear after MCP server has been started)");
13642
13707
  report.add("mcp_log", "not_found");
13643
13708
  }
13644
13709
  function parseHandshakeResponse(line) {
@@ -13714,7 +13779,7 @@ async function checkStdioHandshake(entryPath, report) {
13714
13779
  const parsed = parseHandshakeResponse(line);
13715
13780
  if (!parsed) continue;
13716
13781
  if (parsed.ok) {
13717
- ok("handshake", `OK \u2014 ${parsed.serverName} v${parsed.serverVer}`);
13782
+ ok("handshake", `OK - ${parsed.serverName} v${parsed.serverVer}`);
13718
13783
  report.add("handshake", `OK ${parsed.serverName}@${parsed.serverVer}`);
13719
13784
  } else {
13720
13785
  fail("handshake", `JSON-RPC error: ${parsed.errMsg}`, [
@@ -13741,7 +13806,7 @@ async function checkStdioHandshake(entryPath, report) {
13741
13806
  function checkModuleLoading(entryPath, report) {
13742
13807
  section("Module Loading");
13743
13808
  if (!entryPath) {
13744
- ok("module load", "(skipped \u2014 entry point not found)");
13809
+ ok("module load", "(skipped - entry point not found)");
13745
13810
  report.add("module_load", "skipped");
13746
13811
  return true;
13747
13812
  }
@@ -13784,7 +13849,7 @@ async function cmdDiagnoseMcp(options = {}) {
13784
13849
  handshakePassed = await checkStdioHandshake(entryPath, report);
13785
13850
  } else {
13786
13851
  section("stdio Handshake");
13787
- ok("handshake", "(skipped \u2014 entry point not available)");
13852
+ ok("handshake", "(skipped - entry point not available)");
13788
13853
  report.add("handshake", "skipped");
13789
13854
  handshakePassed = true;
13790
13855
  }
@@ -13803,7 +13868,7 @@ async function cmdDiagnoseMcp(options = {}) {
13803
13868
 
13804
13869
  // src/commands/diagnose.ts
13805
13870
  var CLI_VERSION = readCliVersion();
13806
- var GIT_HASH = true ? "0566db8f" : "dev";
13871
+ var GIT_HASH = true ? "ce35abd7" : "dev";
13807
13872
  function maskKey2(key) {
13808
13873
  if (!key) return "(not set)";
13809
13874
  if (key.length <= 8) return "****";
@@ -14060,13 +14125,13 @@ async function checkPilot(report) {
14060
14125
  report.add("pilot_binary", `installed (${local.platform ?? "unknown"})`);
14061
14126
  const cdnChecksum = await fetchCdnChecksum(void 0, 5e3);
14062
14127
  if (!cdnChecksum) {
14063
- warn("Pilot checksum", "CDN unreachable \u2014 cannot verify");
14128
+ warn("Pilot checksum", "CDN unreachable - cannot verify");
14064
14129
  report.add("pilot_checksum", "CDN unreachable");
14065
14130
  } else if (cdnChecksum.sha256 === local.sha256) {
14066
14131
  ok("Pilot checksum", `match (${cdnChecksum.source})`);
14067
14132
  report.add("pilot_checksum", `match (${cdnChecksum.source})`);
14068
14133
  } else {
14069
- warn("Pilot checksum", "mismatch \u2014 update available", ["Run: okx pilot install"]);
14134
+ warn("Pilot checksum", "mismatch - update available", ["Run: okx pilot install"]);
14070
14135
  report.add("pilot_checksum", "mismatch");
14071
14136
  }
14072
14137
  try {
@@ -14095,9 +14160,9 @@ function checkConfigFile(report) {
14095
14160
  const msg = e instanceof Error ? e.message : String(e);
14096
14161
  fail("Config parse", msg, [
14097
14162
  `If passphrase contains special characters (# \\ " '), wrap in quotes:`,
14098
- ` Contains # \\ " \u2192 passphrase = 'value'`,
14099
- ` Contains ' \u2192 passphrase = "value"`,
14100
- " Contains both \u2192 passphrase = '''value'''",
14163
+ ` Contains # \\ " -> passphrase = 'value'`,
14164
+ ` Contains ' -> passphrase = "value"`,
14165
+ " Contains both -> passphrase = '''value'''",
14101
14166
  "Or re-run: okx config init"
14102
14167
  ]);
14103
14168
  report.add("config_parse", `FAIL ${msg}`);
@@ -14196,13 +14261,13 @@ function printResult(result, json) {
14196
14261
  break;
14197
14262
  case "update-available":
14198
14263
  process.stderr.write(
14199
- `[info] Update available: ${result.currentVersion} \u2192 ${result.latestVersion}
14264
+ `[info] Update available: ${result.currentVersion} -> ${result.latestVersion}
14200
14265
  Run: okx upgrade
14201
14266
  `
14202
14267
  );
14203
14268
  break;
14204
14269
  case "updated":
14205
- process.stderr.write(`[ok] Upgraded: ${result.currentVersion} \u2192 ${result.latestVersion}
14270
+ process.stderr.write(`[ok] Upgraded: ${result.currentVersion} -> ${result.latestVersion}
14206
14271
  `);
14207
14272
  break;
14208
14273
  case "error":
@@ -14606,7 +14671,7 @@ var CLI_REGISTRY = {
14606
14671
  leverage: {
14607
14672
  toolName: "swap_set_leverage",
14608
14673
  usage: "okx swap leverage --instId <id> --lever <positive-number> --mgnMode <cross|isolated> [--posSide <long|short>]",
14609
- description: "Set leverage for a swap instrument. posSide is REQUIRED when mgnMode=isolated and account is in hedge mode \u2014 must be set for BOTH long and short separately. Not supported for portfolio margin + cross."
14674
+ description: "Set leverage for a swap instrument. posSide is REQUIRED when mgnMode=isolated and account is in hedge mode - must be set for BOTH long and short separately. Not supported for portfolio margin + cross."
14610
14675
  },
14611
14676
  "get-leverage": {
14612
14677
  toolName: "swap_get_leverage",
@@ -14705,7 +14770,7 @@ var CLI_REGISTRY = {
14705
14770
  leverage: {
14706
14771
  toolName: "futures_set_leverage",
14707
14772
  usage: "okx futures leverage --instId <id> --lever <positive-number> --mgnMode <cross|isolated> [--posSide <long|short>]",
14708
- description: "Set leverage for a futures instrument. posSide is REQUIRED when mgnMode=isolated and account is in hedge mode \u2014 must be set for BOTH long and short separately. Not supported for portfolio margin + cross."
14773
+ description: "Set leverage for a futures instrument. posSide is REQUIRED when mgnMode=isolated and account is in hedge mode - must be set for BOTH long and short separately. Not supported for portfolio margin + cross."
14709
14774
  },
14710
14775
  batch: {
14711
14776
  toolName: "futures_batch_orders",
@@ -14832,10 +14897,10 @@ var CLI_REGISTRY = {
14832
14897
  },
14833
14898
  // ── earn ───────────────────────────────────────────────────────────────────
14834
14899
  earn: {
14835
- description: "Earn products \u2014 Simple Earn, On-chain Earn, DCD, Flash Earn, and Auto-Earn",
14900
+ description: "Earn products - Simple Earn, On-chain Earn, DCD, Flash Earn, and Auto-Earn",
14836
14901
  subgroups: {
14837
14902
  savings: {
14838
- description: "Simple Earn \u2014 flexible savings, fixed-term, and lending",
14903
+ description: "Simple Earn - flexible savings, fixed-term, and lending",
14839
14904
  commands: {
14840
14905
  balance: {
14841
14906
  toolName: "earn_get_savings_balance",
@@ -14885,7 +14950,7 @@ var CLI_REGISTRY = {
14885
14950
  }
14886
14951
  },
14887
14952
  onchain: {
14888
- description: "On-chain Earn \u2014 staking and DeFi products",
14953
+ description: "On-chain Earn - staking and DeFi products",
14889
14954
  commands: {
14890
14955
  offers: {
14891
14956
  toolName: "onchain_earn_get_offers",
@@ -14920,7 +14985,7 @@ var CLI_REGISTRY = {
14920
14985
  }
14921
14986
  },
14922
14987
  "auto-earn": {
14923
- description: "Auto-earn \u2014 automatically lend, stake, or earn on idle assets",
14988
+ description: "Auto-earn - automatically lend, stake, or earn on idle assets",
14924
14989
  commands: {
14925
14990
  status: {
14926
14991
  // CLI reads from account_get_balance; earn_auto_set is covered by 'on' command below
@@ -14942,7 +15007,7 @@ var CLI_REGISTRY = {
14942
15007
  }
14943
15008
  },
14944
15009
  "flash-earn": {
14945
- description: "Flash Earn \u2014 browse short-window earn projects by status",
15010
+ description: "Flash Earn - browse short-window earn projects by status",
14946
15011
  commands: {
14947
15012
  projects: {
14948
15013
  toolName: "earn_get_flash_earn_projects",
@@ -14952,7 +15017,7 @@ var CLI_REGISTRY = {
14952
15017
  }
14953
15018
  },
14954
15019
  dcd: {
14955
- description: "DCD (Dual Currency Deposit) \u2014 structured products with fixed yield",
15020
+ description: "DCD (Dual Currency Deposit) - structured products with fixed yield",
14956
15021
  commands: {
14957
15022
  pairs: {
14958
15023
  toolName: "dcd_get_currency_pairs",
@@ -14993,7 +15058,7 @@ var CLI_REGISTRY = {
14993
15058
  description: "Trading bot strategies (grid, dca)",
14994
15059
  subgroups: {
14995
15060
  grid: {
14996
- description: "Grid trading bot \u2014 create, monitor, and stop grid orders",
15061
+ description: "Grid trading bot - create, monitor, and stop grid orders",
14997
15062
  commands: {
14998
15063
  orders: {
14999
15064
  toolName: "grid_get_orders",
@@ -15028,7 +15093,7 @@ var CLI_REGISTRY = {
15028
15093
  }
15029
15094
  },
15030
15095
  dca: {
15031
- description: "DCA (Martingale) bot \u2014 spot or contract recurring buys",
15096
+ description: "DCA (Martingale) bot - spot or contract recurring buys",
15032
15097
  commands: {
15033
15098
  orders: {
15034
15099
  toolName: "dca_get_orders",
@@ -15061,7 +15126,7 @@ var CLI_REGISTRY = {
15061
15126
  },
15062
15127
  // ── event ──────────────────────────────────────────────────────────────────
15063
15128
  event: {
15064
- description: "Event contracts \u2014 binary prediction markets (YES/NO, UP/DOWN)",
15129
+ description: "Event contracts - binary prediction markets (YES/NO, UP/DOWN)",
15065
15130
  commands: {
15066
15131
  browse: {
15067
15132
  toolName: "event_browse",
@@ -15112,7 +15177,7 @@ var CLI_REGISTRY = {
15112
15177
  },
15113
15178
  // ── smartmoney ─────────────────────────────────────────────────────────────
15114
15179
  smartmoney: {
15115
- description: "Smart money analytics \u2014 trader leaderboard, consensus signals, and position analysis",
15180
+ description: "Smart money analytics - trader leaderboard, consensus signals, and position analysis",
15116
15181
  commands: {
15117
15182
  "traders-by-filter": {
15118
15183
  toolName: "smartmoney_get_traders_by_filter",
@@ -15284,7 +15349,7 @@ var CLI_REGISTRY = {
15284
15349
  },
15285
15350
  // ── skill ──────────────────────────────────────────────────────────────────
15286
15351
  skill: {
15287
- description: "OKX Skills Marketplace \u2014 search, install, and manage agent skills",
15352
+ description: "OKX Skills Marketplace - search, install, and manage agent skills",
15288
15353
  commands: {
15289
15354
  search: {
15290
15355
  toolName: "skills_search",
@@ -15450,7 +15515,7 @@ function cmdListTools(json) {
15450
15515
  }
15451
15516
  const lines = [
15452
15517
  "",
15453
- `OKX CLI v${data.version} \u2014 ${data.totalTools} tool-backed commands across ${data.modules.length} modules`,
15518
+ `OKX CLI v${data.version} - ${data.totalTools} tool-backed commands across ${data.modules.length} modules`,
15454
15519
  "",
15455
15520
  "Modules:"
15456
15521
  ];
@@ -16046,12 +16111,12 @@ var CLI_OPTIONS = {
16046
16111
  instCcyList: { type: "string" },
16047
16112
  topInstruments: { type: "string" },
16048
16113
  asOfTime: { type: "string" },
16049
- // smartmoney pool filters leaderboard (numeric thresholds)
16114
+ // smartmoney pool filters - leaderboard (numeric thresholds)
16050
16115
  minPnl: { type: "string" },
16051
16116
  minWinRate: { type: "string" },
16052
16117
  maxDrawdown: { type: "string" },
16053
16118
  minAum: { type: "string" },
16054
- // smartmoney pool filters signal endpoints (enum tiers)
16119
+ // smartmoney pool filters - signal endpoints (enum tiers)
16055
16120
  pnlTier: { type: "string" },
16056
16121
  winRateTier: { type: "string" },
16057
16122
  maxDrawdownTier: { type: "string" },
@@ -16134,7 +16199,7 @@ var CLI_OPTIONS = {
16134
16199
  settleCcy: { type: "string" },
16135
16200
  ts: { type: "string" },
16136
16201
  minAbsOiDeltaPct: { type: "string" },
16137
- // diagnostics cli/mcp/all/output are diagnose-specific; verbose is shared
16202
+ // diagnostics - cli/mcp/all/output are diagnose-specific; verbose is shared
16138
16203
  verbose: { type: "boolean", default: false },
16139
16204
  mcp: { type: "boolean", default: false },
16140
16205
  // diagnose --mcp only: MCP server checks
@@ -16474,7 +16539,7 @@ async function cmdMarketInstrumentsByCategory(run, opts) {
16474
16539
  "7": "Bonds"
16475
16540
  };
16476
16541
  const label = CATEGORY_LABELS[opts.instCategory] ?? opts.instCategory;
16477
- process.stdout.write(`instCategory=${opts.instCategory} (${label}) \u2014 ${items?.length ?? 0} instruments
16542
+ process.stdout.write(`instCategory=${opts.instCategory} (${label}) - ${items?.length ?? 0} instruments
16478
16543
 
16479
16544
  `);
16480
16545
  printTable(
@@ -18113,7 +18178,7 @@ import { createInterface } from "readline";
18113
18178
  import { spawnSync as spawnSync3 } from "child_process";
18114
18179
  var messages = {
18115
18180
  en: {
18116
- title: "OKX Trade CLI \u2014 Configuration Wizard",
18181
+ title: "OKX Trade CLI - Configuration Wizard",
18117
18182
  selectSite: "Select site:",
18118
18183
  sitePrompt: "Site (1/2/3, default: 1): ",
18119
18184
  demoPrompt: "Use demo trading? (Y/n) ",
@@ -18147,7 +18212,7 @@ Config saved to ${p}
18147
18212
  `
18148
18213
  },
18149
18214
  zh: {
18150
- title: "OKX Trade CLI \u2014 \u914D\u7F6E\u5411\u5BFC",
18215
+ title: "OKX Trade CLI - \u914D\u7F6E\u5411\u5BFC",
18151
18216
  selectSite: "\u8BF7\u9009\u62E9\u7AD9\u70B9:",
18152
18217
  sitePrompt: "\u7AD9\u70B9 (1/2/3, \u9ED8\u8BA4: 1): ",
18153
18218
  demoPrompt: "\u4F7F\u7528\u6A21\u62DF\u76D8\uFF1F(Y/n) ",
@@ -18494,7 +18559,7 @@ async function cmdEarnSetLendingRate(run, opts) {
18494
18559
  return;
18495
18560
  }
18496
18561
  const r = data[0];
18497
- outputLine(`Lending rate set: ${r?.["ccy"]} \u2192 ${r?.["rate"]}`);
18562
+ outputLine(`Lending rate set: ${r?.["ccy"]} -> ${r?.["rate"]}`);
18498
18563
  }
18499
18564
  async function cmdEarnLendingHistory(run, opts) {
18500
18565
  const data = extractData(await run("earn_get_lending_history", { ccy: opts.ccy, limit: opts.limit }));
@@ -18629,7 +18694,7 @@ function printPaginationHint(result) {
18629
18694
  const { hasMore, nextAfter } = pagination;
18630
18695
  if (hasMore !== true) return;
18631
18696
  const cursor = typeof nextAfter === "string" || typeof nextAfter === "number" ? String(nextAfter) : "";
18632
- errorLine(cursor ? `more results \u2014 pass --after ${cursor} for next page` : "more results \u2014 pass --after <cursor> for next page");
18697
+ errorLine(cursor ? `more results - pass --after ${cursor} for next page` : "more results - pass --after <cursor> for next page");
18633
18698
  }
18634
18699
  function signalPoolFilterArgs(o) {
18635
18700
  const result = {};
@@ -19444,8 +19509,8 @@ async function cmdDcdProducts(run, opts) {
19444
19509
  quoteCcy: r["quoteCcy"],
19445
19510
  optType: r["optType"],
19446
19511
  strike: r["strike"],
19447
- // products endpoint returns decimal (e.g. 0.3423 = 34.23%) multiply by 100
19448
- annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
19512
+ // products endpoint returns decimal (e.g. 0.3423 = 34.23%) - multiply by 100
19513
+ annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "-",
19449
19514
  minSize: r["minSize"],
19450
19515
  expTime: r["expTime"] ? new Date(Number(r["expTime"])).toLocaleDateString() : ""
19451
19516
  })));
@@ -19475,8 +19540,8 @@ async function cmdDcdRedeemExecute(run, opts) {
19475
19540
  printKv({
19476
19541
  ordId: r["ordId"],
19477
19542
  state: r["state"],
19478
- redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "\u2014",
19479
- termRate: q["termRate"] ? `${(parseFloat(q["termRate"]) * 100).toFixed(2)}%` : "\u2014"
19543
+ redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "-",
19544
+ termRate: q["termRate"] ? `${(parseFloat(q["termRate"]) * 100).toFixed(2)}%` : "-"
19480
19545
  });
19481
19546
  }
19482
19547
  async function cmdDcdOrderState(run, opts) {
@@ -19529,7 +19594,7 @@ async function cmdDcdOrders(run, opts) {
19529
19594
  quoteCcy: r["quoteCcy"],
19530
19595
  strike: r["strike"],
19531
19596
  notionalSz: r["notionalSz"],
19532
- annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
19597
+ annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "-",
19533
19598
  yieldSz: r["yieldSz"],
19534
19599
  settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : "",
19535
19600
  // scheduled settlement time
@@ -19569,7 +19634,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
19569
19634
  outputLine("Quote:");
19570
19635
  printKv({
19571
19636
  quoteId: q["quoteId"],
19572
- annualizedYield: q["annualizedYield"] ? `${(parseFloat(q["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
19637
+ annualizedYield: q["annualizedYield"] ? `${(parseFloat(q["annualizedYield"]) * 100).toFixed(2)}%` : "-",
19573
19638
  absYield: q["absYield"],
19574
19639
  notionalSz: q["notionalSz"],
19575
19640
  notionalCcy: q["notionalCcy"]
@@ -19744,9 +19809,9 @@ async function cmdSkillCheck(run, name, json) {
19744
19809
  upToDate
19745
19810
  }, null, 2));
19746
19811
  } else if (upToDate) {
19747
- outputLine(`${name}: installed v${local.version} \u2192 latest v${remote.latestVersion} (up to date)`);
19812
+ outputLine(`${name}: installed v${local.version} -> latest v${remote.latestVersion} (up to date)`);
19748
19813
  } else {
19749
- outputLine(`${name}: installed v${local.version} \u2192 latest v${remote.latestVersion} (update available)`);
19814
+ outputLine(`${name}: installed v${local.version} -> latest v${remote.latestVersion} (update available)`);
19750
19815
  outputLine(` Use \`okx skill add ${name}\` to update.`);
19751
19816
  }
19752
19817
  }
@@ -20023,9 +20088,9 @@ async function cmdEventBrowse(run, opts) {
20023
20088
  contracts.map((c) => ({
20024
20089
  "Contract": formatDisplayTitle(String(c["instId"] ?? "")),
20025
20090
  "Expiry": c["expTime"] ?? "",
20026
- "Target Price": c["floorStrike"] ? String(c["floorStrike"]) : "\u2014",
20091
+ "Target Price": c["floorStrike"] ? String(c["floorStrike"]) : "-",
20027
20092
  "Probability": fmtProbability(c["px"]),
20028
- "Outcome": fmtOutcome(c["outcome"]) || "\u2014",
20093
+ "Outcome": fmtOutcome(c["outcome"]) || "-",
20029
20094
  "instId": c["instId"]
20030
20095
  }))
20031
20096
  );
@@ -20162,7 +20227,7 @@ async function cmdEventMarkets(run, opts) {
20162
20227
  expTime: m["expTime"] ?? "",
20163
20228
  targetPrice: m["floorStrike"] ?? "",
20164
20229
  probability: fmtProbability(m["px"]),
20165
- outcome: outcome.toLowerCase() === "pending" ? "\u2014" : outcome,
20230
+ outcome: outcome.toLowerCase() === "pending" ? "-" : outcome,
20166
20231
  settleValue: m["settleValue"] ?? "",
20167
20232
  instId: id
20168
20233
  };
@@ -20215,7 +20280,7 @@ async function cmdEventFills(run, opts) {
20215
20280
  const side = String(f["side"] ?? "").toUpperCase();
20216
20281
  const outcome = fmtOrderOutcome(f["instId"], f["outcome"]).toUpperCase();
20217
20282
  const dir = `${side} ${outcome}`.trim();
20218
- return dir || "\u2014";
20283
+ return dir || "-";
20219
20284
  })(),
20220
20285
  "Fill Price": f["fillPx"],
20221
20286
  "Fill Size": f["fillSz"],
@@ -20317,7 +20382,7 @@ async function cmdEventPlace(run, opts) {
20317
20382
  const data = getData9(result);
20318
20383
  if (opts.json) return printJson(data);
20319
20384
  const order = data?.[0];
20320
- const stateHint = ordType === "market" ? "market order \u2014 typically fills immediately" : `${ordType} order \u2014 may still be live; verify with: okx event orders --instId ${opts.instId} --state live`;
20385
+ const stateHint = ordType === "market" ? "market order - typically fills immediately" : `${ordType} order - may still be live; verify with: okx event orders --instId ${opts.instId} --state live`;
20321
20386
  const period = fmtPeriodFromInstId(opts.instId);
20322
20387
  const pxPart = opts.px ? ` px: ${opts.px}` : "";
20323
20388
  process.stdout.write(
@@ -20363,7 +20428,7 @@ function handleCancelCatchError(instId, ordId, err) {
20363
20428
  if (isExpired) {
20364
20429
  process.stdout.write(
20365
20430
  `Cannot cancel: contract ${instId} has already expired.
20366
- The order was auto-cancelled at settlement \u2014 no action needed.
20431
+ The order was auto-cancelled at settlement - no action needed.
20367
20432
  `
20368
20433
  );
20369
20434
  } else {
@@ -20392,7 +20457,7 @@ async function cmdEventCancel(run, opts) {
20392
20457
  // src/index.ts
20393
20458
  var _require3 = createRequire3(import.meta.url);
20394
20459
  var CLI_VERSION2 = _require3("../package.json").version;
20395
- var GIT_HASH2 = true ? "0566db8f" : "dev";
20460
+ var GIT_HASH2 = true ? "ce35abd7" : "dev";
20396
20461
  function handlePilotCommand(action, json, force, binaryPath) {
20397
20462
  if (action === "status") return cmdPilotStatus(json, binaryPath);
20398
20463
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -20704,7 +20769,7 @@ function assertNoTpConflict(tpLevel, singleFields) {
20704
20769
  if (conflicting.length > 0) {
20705
20770
  const flagNames = conflicting.map((k) => `--${k}`).join(", ");
20706
20771
  throw new Error(
20707
- `Cannot use --tpLevel together with ${flagNames}. Use --tpLevel for split multi-tier take-profit, or single-TP flags for a single TP \u2014 not both.`
20772
+ `Cannot use --tpLevel together with ${flagNames}. Use --tpLevel for split multi-tier take-profit, or single-TP flags for a single TP - not both.`
20708
20773
  );
20709
20774
  }
20710
20775
  }
@@ -21501,7 +21566,7 @@ function handleSmartmoneyCommand(run, action, rest, v, json) {
21501
21566
  if (action === "signal-overview-by-filter") {
21502
21567
  if (v.topInstruments && v.instCcyList) {
21503
21568
  errorLine(
21504
- "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one \u2014 `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
21569
+ "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one - `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
21505
21570
  );
21506
21571
  process.exitCode = 1;
21507
21572
  return;
@@ -21522,7 +21587,7 @@ function handleSmartmoneyCommand(run, action, rest, v, json) {
21522
21587
  }
21523
21588
  if (v.topInstruments && v.instCcyList) {
21524
21589
  errorLine(
21525
- "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one \u2014 `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
21590
+ "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one - `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
21526
21591
  );
21527
21592
  process.exitCode = 1;
21528
21593
  return;