@okx_ai/okx-trade-mcp 1.3.4-beta.2 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -988,8 +988,8 @@ var PilotManager = class {
988
988
  * Apply a Pilot node: set up the custom Agent + base URL.
989
989
  *
990
990
  * node.ip may be a real IP or a domain (CNAME like *.aliyunddos1021.com).
991
- * - Real IP use directly in lookup callback
992
- * - Domain dns.lookup on every connection to get a fresh IP
991
+ * - Real IP -> use directly in lookup callback
992
+ * - Domain -> dns.lookup on every connection to get a fresh IP
993
993
  */
994
994
  applyNode(node, protocol) {
995
995
  this.pilotNode = node;
@@ -1339,29 +1339,29 @@ var RateLimiter = class {
1339
1339
  }
1340
1340
  };
1341
1341
  var OKX_CODE_BEHAVIORS = {
1342
- // Rate limit throw RateLimitError
1342
+ // Rate limit -> throw RateLimitError
1343
1343
  "50011": { retry: true, suggestion: "Rate limited. Back off and retry after a delay." },
1344
1344
  "50061": { retry: true, suggestion: "Too many connections. Reduce request frequency and retry." },
1345
- // Server temporarily unavailable retryable
1345
+ // Server temporarily unavailable -> retryable
1346
1346
  "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
1347
1347
  "50004": { retry: true, suggestion: "Endpoint request timeout. Retry later." },
1348
1348
  "50013": { retry: true, suggestion: "System busy. Retry after 1-2 seconds." },
1349
1349
  "50026": { retry: true, suggestion: "System error. Retry in a few minutes." },
1350
- // Region / compliance restriction do not retry
1350
+ // Region / compliance restriction -> do not retry
1351
1351
  "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." },
1352
1352
  "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." },
1353
- // Account issues do not retry
1353
+ // Account issues -> do not retry
1354
1354
  "50007": { retry: false, suggestion: "Account suspended. Contact OKX support. Do not retry." },
1355
1355
  "50009": { retry: false, suggestion: "Account blocked by risk control. Contact OKX support. Do not retry." },
1356
1356
  "51009": { retry: false, suggestion: "Account mode not supported for this operation. Check account settings." },
1357
- // API key permission / expiry do not retry
1357
+ // API key permission / expiry -> do not retry
1358
1358
  "50100": { retry: false, suggestion: "API key lacks required permissions. Update API key permissions." },
1359
1359
  "50110": { retry: false, suggestion: "API key expired. Generate a new API key." },
1360
- // Insufficient funds / margin do not retry
1361
- "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." },
1360
+ // Insufficient funds / margin -> do not retry
1361
+ "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." },
1362
1362
  "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." },
1363
1363
  "51127": { retry: false, suggestion: "Insufficient available margin. Reduce position, add margin, or transfer from funding account (account_transfer from=18 to=6)." },
1364
- // Instrument unavailable do not retry
1364
+ // Instrument unavailable -> do not retry
1365
1365
  "51021": { retry: false, suggestion: "Instrument does not exist. Check instId." },
1366
1366
  "51022": { retry: false, suggestion: "Instrument not available for trading." },
1367
1367
  "51027": { retry: false, suggestion: "Contract has expired." }
@@ -1445,11 +1445,11 @@ var OkxRestClient = class _OkxRestClient {
1445
1445
  }
1446
1446
  }
1447
1447
  /**
1448
- * Dynamic auth determines auth method per request.
1448
+ * Dynamic auth - determines auth method per request.
1449
1449
  *
1450
- * 1. API key in config HMAC signing (no OAuth fallback)
1451
- * 2. OAuth token via okx-auth binary Bearer token
1452
- * 3. Neither throw ConfigError
1450
+ * 1. API key in config -> HMAC signing (no OAuth fallback)
1451
+ * 2. OAuth token via okx-auth binary -> Bearer token
1452
+ * 3. Neither -> throw ConfigError
1453
1453
  */
1454
1454
  async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
1455
1455
  if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
@@ -1603,7 +1603,7 @@ var OkxRestClient = class _OkxRestClient {
1603
1603
  };
1604
1604
  }
1605
1605
  // ---------------------------------------------------------------------------
1606
- // Binary (non-JSON) download reuses auth, proxy, rate-limit, verbose
1606
+ // Binary (non-JSON) download - reuses auth, proxy, rate-limit, verbose
1607
1607
  // ---------------------------------------------------------------------------
1608
1608
  static DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
1609
1609
  // 50 MB
@@ -2215,7 +2215,7 @@ var INDICATOR_CODE_OVERRIDES = {
2215
2215
  // default: NVI_PVI
2216
2216
  "top-long-short": "TOPLONGSHORT"
2217
2217
  // default: TOP_LONG_SHORT
2218
- // Note: range-filter RANGE_FILTER is correct via the default rule; no override needed.
2218
+ // Note: range-filter -> RANGE_FILTER is correct via the default rule; no override needed.
2219
2219
  };
2220
2220
  var KNOWN_INDICATORS = [
2221
2221
  // Moving Averages
@@ -2516,7 +2516,7 @@ function registerAccountTools() {
2516
2516
  },
2517
2517
  type: {
2518
2518
  type: "string",
2519
- description: "0=main account (default), 1=main\u2192sub, 2=sub\u2192main, 3=sub\u2192sub"
2519
+ description: "0=main account (default), 1=main->sub, 2=sub->main, 3=sub->sub"
2520
2520
  },
2521
2521
  subAcct: {
2522
2522
  type: "string",
@@ -3100,7 +3100,7 @@ function computeContracts(p) {
3100
3100
  );
3101
3101
  }
3102
3102
  const contractsStr = contractsRounded.toFixed(lotSzDecimals);
3103
- 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})`;
3103
+ 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})`;
3104
3104
  return { contractsStr, conversionNote };
3105
3105
  }
3106
3106
  async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
@@ -3148,7 +3148,7 @@ function registerAlgoTradeTools() {
3148
3148
  {
3149
3149
  name: "swap_place_algo_order",
3150
3150
  module: "swap",
3151
- 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.",
3151
+ 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.",
3152
3152
  isWrite: true,
3153
3153
  inputSchema: {
3154
3154
  type: "object",
@@ -3508,7 +3508,7 @@ function registerFuturesAlgoTools() {
3508
3508
  {
3509
3509
  name: "futures_place_algo_order",
3510
3510
  module: "futures",
3511
- 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.",
3511
+ 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.",
3512
3512
  isWrite: true,
3513
3513
  inputSchema: {
3514
3514
  type: "object",
@@ -3733,7 +3733,7 @@ function registerFuturesAlgoTools() {
3733
3733
  {
3734
3734
  name: "futures_amend_algo_order",
3735
3735
  module: "futures",
3736
- 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.",
3736
+ 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.",
3737
3737
  isWrite: true,
3738
3738
  inputSchema: {
3739
3739
  type: "object",
@@ -4030,7 +4030,7 @@ function registerSkillsTools() {
4030
4030
  {
4031
4031
  name: "skills_get_categories",
4032
4032
  module: "skills",
4033
- 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.",
4033
+ 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.",
4034
4034
  inputSchema: {
4035
4035
  type: "object",
4036
4036
  properties: {},
@@ -4042,7 +4042,7 @@ function registerSkillsTools() {
4042
4042
  {
4043
4043
  name: "skills_search",
4044
4044
  module: "skills",
4045
- 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.",
4045
+ 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.",
4046
4046
  inputSchema: {
4047
4047
  type: "object",
4048
4048
  properties: {
@@ -4071,7 +4071,7 @@ function registerSkillsTools() {
4071
4071
  {
4072
4072
  name: "skills_download",
4073
4073
  module: "skills",
4074
- 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.",
4074
+ 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.",
4075
4075
  inputSchema: {
4076
4076
  type: "object",
4077
4077
  properties: {
@@ -4352,7 +4352,7 @@ function registerGridTools() {
4352
4352
  {
4353
4353
  name: "grid_amend_order",
4354
4354
  module: "bot.grid",
4355
- 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.",
4355
+ 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.",
4356
4356
  isWrite: true,
4357
4357
  inputSchema: {
4358
4358
  type: "object",
@@ -4430,7 +4430,7 @@ function registerGridTools() {
4430
4430
  maxPx,
4431
4431
  minPx: requireString(args, "minPx"),
4432
4432
  gridNum: requireString(args, "gridNum"),
4433
- // API field is "topupAmount" (lowercase u) different from TP/SL mode's "topUpAmt"
4433
+ // API field is "topupAmount" (lowercase u) - different from TP/SL mode's "topUpAmt"
4434
4434
  // Contract grid only; omitting lets the API use the minimum required
4435
4435
  topupAmount: readString(args, "topUpAmt")
4436
4436
  }),
@@ -4451,7 +4451,7 @@ function registerGridTools() {
4451
4451
  tpRatio: readString(args, "tpRatio"),
4452
4452
  slRatio: readString(args, "slRatio"),
4453
4453
  topUpAmt: readString(args, "topUpAmt")
4454
- // API field is "topUpAmt" (uppercase U) different from price-range mode's "topupAmount"
4454
+ // API field is "topUpAmt" (uppercase U) - different from price-range mode's "topupAmount"
4455
4455
  }),
4456
4456
  privateRateLimit("grid_amend_order", 20),
4457
4457
  true
@@ -4475,7 +4475,7 @@ function registerGridTools() {
4475
4475
  {
4476
4476
  name: "grid_stop_order",
4477
4477
  module: "bot.grid",
4478
- 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.",
4478
+ 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.",
4479
4479
  isWrite: true,
4480
4480
  inputSchema: {
4481
4481
  type: "object",
@@ -4490,7 +4490,7 @@ function registerGridTools() {
4490
4490
  stopType: {
4491
4491
  type: "string",
4492
4492
  enum: ["1", "2"],
4493
- 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."
4493
+ 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."
4494
4494
  }
4495
4495
  },
4496
4496
  required: ["algoId", "algoOrdType", "instId"]
@@ -4507,7 +4507,7 @@ function registerGridTools() {
4507
4507
  })],
4508
4508
  privateRateLimit("grid_stop_order", 20),
4509
4509
  true
4510
- // retryOnNetworkError: safe to retry already-stopped returns an error but does not harm state
4510
+ // retryOnNetworkError: safe to retry - already-stopped returns an error but does not harm state
4511
4511
  );
4512
4512
  return normalizeWrite(response);
4513
4513
  }
@@ -4653,7 +4653,7 @@ function registerDcaTools() {
4653
4653
  {
4654
4654
  name: "dca_stop_order",
4655
4655
  module: "bot.dca",
4656
- description: "Stop a running DCA bot. [CAUTION] spot_dca needs stopType: 1=sell, 2=keep.",
4656
+ 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.",
4657
4657
  isWrite: true,
4658
4658
  inputSchema: {
4659
4659
  type: "object",
@@ -4808,7 +4808,7 @@ function registerEarnTools() {
4808
4808
  {
4809
4809
  name: "earn_get_savings_balance",
4810
4810
  module: "earn.savings",
4811
- 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.",
4811
+ 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.",
4812
4812
  isWrite: false,
4813
4813
  inputSchema: {
4814
4814
  type: "object",
@@ -4832,7 +4832,7 @@ function registerEarnTools() {
4832
4832
  {
4833
4833
  name: "earn_get_fixed_order_list",
4834
4834
  module: "earn.savings",
4835
- 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.",
4835
+ 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.",
4836
4836
  isWrite: false,
4837
4837
  inputSchema: {
4838
4838
  type: "object",
@@ -4884,7 +4884,7 @@ function registerEarnTools() {
4884
4884
  },
4885
4885
  rate: {
4886
4886
  type: "string",
4887
- 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."
4887
+ 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."
4888
4888
  }
4889
4889
  },
4890
4890
  required: ["ccy", "amt"]
@@ -4951,7 +4951,7 @@ function registerEarnTools() {
4951
4951
  },
4952
4952
  rate: {
4953
4953
  type: "string",
4954
- 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."
4954
+ 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."
4955
4955
  }
4956
4956
  },
4957
4957
  required: ["ccy", "rate"]
@@ -5013,7 +5013,7 @@ function registerEarnTools() {
5013
5013
  {
5014
5014
  name: "earn_fixed_purchase",
5015
5015
  module: "earn.savings",
5016
- 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.",
5016
+ 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.",
5017
5017
  isWrite: true,
5018
5018
  inputSchema: {
5019
5019
  type: "object",
@@ -5072,7 +5072,7 @@ function registerEarnTools() {
5072
5072
  term,
5073
5073
  offer: offerWithSoldOut,
5074
5074
  currentFlexibleRate: rateArr[0]?.["lendingRate"] ?? null,
5075
- 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."
5075
+ 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."
5076
5076
  };
5077
5077
  }
5078
5078
  assertNotDemo(context.config, "earn_fixed_purchase");
@@ -5087,7 +5087,7 @@ function registerEarnTools() {
5087
5087
  {
5088
5088
  name: "earn_fixed_redeem",
5089
5089
  module: "earn.savings",
5090
- 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.",
5090
+ 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.",
5091
5091
  isWrite: true,
5092
5092
  inputSchema: {
5093
5093
  type: "object",
@@ -5115,7 +5115,7 @@ function registerEarnTools() {
5115
5115
  {
5116
5116
  name: "earn_get_lending_rate_history",
5117
5117
  module: "earn.savings",
5118
- 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.",
5118
+ 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.",
5119
5119
  isWrite: false,
5120
5120
  inputSchema: {
5121
5121
  type: "object",
@@ -5448,7 +5448,7 @@ function registerOnchainEarnTools() {
5448
5448
  }
5449
5449
  var DCD_CODE_BEHAVIORS = {
5450
5450
  "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
5451
- "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug \u2014 check request parameters." },
5451
+ "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug - check request parameters." },
5452
5452
  "50014": { retry: false, suggestion: "Missing required parameter. Check that all required fields are provided." },
5453
5453
  "50016": { retry: false, suggestion: "notionalCcy does not match productId option type. Use baseCcy for CALL, quoteCcy for PUT." },
5454
5454
  "50026": { retry: true, suggestion: "DCD system error. Retry in a few minutes." },
@@ -5823,7 +5823,7 @@ function registerFlashEarnTools() {
5823
5823
  {
5824
5824
  name: "earn_get_flash_earn_projects",
5825
5825
  module: "earn.flash",
5826
- 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.",
5826
+ 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.",
5827
5827
  isWrite: false,
5828
5828
  inputSchema: {
5829
5829
  type: "object",
@@ -6219,7 +6219,7 @@ function handlePlaceOrderError(base, rawArgs, endpoint) {
6219
6219
  const seriesId = extractSeriesId(instId);
6220
6220
  const expiryMs = inferExpiryMsFromInstId(instId);
6221
6221
  const isExpired = expiryMs !== null && expiryMs < Date.now();
6222
- const reason = isExpired ? `The contract (${instId}) has expired.` : `The contract (${instId}) was not found \u2014 it may not exist or has not started yet.`;
6222
+ const reason = isExpired ? `The contract (${instId}) has expired.` : `The contract (${instId}) was not found - it may not exist or has not started yet.`;
6223
6223
  throw new OkxApiError(
6224
6224
  `${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.`,
6225
6225
  { code: sCode, endpoint }
@@ -6235,17 +6235,17 @@ var OUTCOME_SCHEMA = {
6235
6235
  UP/DOWN direction contracts: UP (price rises during the period) or DOWN (price falls).
6236
6236
  YES/NO price-target or touch contracts: YES (condition met) or NO (condition not met).
6237
6237
  Check the series type from event_get_series to determine which applies.
6238
- NOTE: px is the event contract price (0.01\u20130.99), NOT the underlying asset price. It reflects market-implied probability when actively trading.`
6238
+ NOTE: px is the event contract price (0.01-0.99), NOT the underlying asset price. It reflects market-implied probability when actively trading.`
6239
6239
  };
6240
6240
  function registerEventContractTools() {
6241
6241
  return [
6242
6242
  // -----------------------------------------------------------------------
6243
- // Read-only browse (user-facing) + series / events / markets (internal)
6243
+ // Read-only - browse (user-facing) + series / events / markets (internal)
6244
6244
  // -----------------------------------------------------------------------
6245
6245
  {
6246
6246
  name: "event_browse",
6247
6247
  module: "event",
6248
- 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.",
6248
+ 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.",
6249
6249
  isWrite: false,
6250
6250
  inputSchema: {
6251
6251
  type: "object",
@@ -6364,7 +6364,7 @@ function registerEventContractTools() {
6364
6364
  {
6365
6365
  name: "event_get_markets",
6366
6366
  module: "event",
6367
- 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.",
6367
+ 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.",
6368
6368
  isWrite: false,
6369
6369
  inputSchema: {
6370
6370
  type: "object",
@@ -6445,7 +6445,7 @@ function registerEventContractTools() {
6445
6445
  {
6446
6446
  name: "event_get_orders",
6447
6447
  module: "event",
6448
- 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.",
6448
+ 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.",
6449
6449
  isWrite: false,
6450
6450
  inputSchema: {
6451
6451
  type: "object",
@@ -6503,7 +6503,7 @@ function registerEventContractTools() {
6503
6503
  {
6504
6504
  name: "event_get_fills",
6505
6505
  module: "event",
6506
- 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.",
6506
+ 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.",
6507
6507
  isWrite: false,
6508
6508
  inputSchema: {
6509
6509
  type: "object",
@@ -6545,15 +6545,15 @@ function registerEventContractTools() {
6545
6545
  }
6546
6546
  },
6547
6547
  // -----------------------------------------------------------------------
6548
- // Private write
6548
+ // Private - write
6549
6549
  // -----------------------------------------------------------------------
6550
6550
  {
6551
6551
  name: "event_place_order",
6552
6552
  module: "event",
6553
6553
  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.
6554
6554
  - outcome: UP/YES (bet price goes up/condition met) or DOWN/NO (bet price goes down/condition not met)
6555
- - 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
6556
- - tdMode is always isolated; speedBump is auto-set per exchange requirement \u2014 do not pass either`,
6555
+ - 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
6556
+ - tdMode is always isolated; speedBump is auto-set per exchange requirement - do not pass either`,
6557
6557
  isWrite: true,
6558
6558
  inputSchema: {
6559
6559
  type: "object",
@@ -6579,7 +6579,7 @@ function registerEventContractTools() {
6579
6579
  },
6580
6580
  px: {
6581
6581
  type: "string",
6582
- description: "Event contract price (0.01\u20130.99). Required when ordType=limit. Do NOT use for market orders."
6582
+ description: "Event contract price (0.01-0.99). Required when ordType=limit. Do NOT use for market orders."
6583
6583
  }
6584
6584
  },
6585
6585
  required: ["instId", "side", "outcome", "sz"]
@@ -6632,7 +6632,7 @@ function registerEventContractTools() {
6632
6632
  properties: {
6633
6633
  instId: { type: "string", description: "Event contract instrument ID" },
6634
6634
  ordId: { type: "string", description: "Order ID to amend" },
6635
- newPx: { type: "string", description: "New event contract price (0.01\u20130.99). Omit to keep current." },
6635
+ newPx: { type: "string", description: "New event contract price (0.01-0.99). Omit to keep current." },
6636
6636
  newSz: { type: "string", description: "New size in contracts (omit to keep current)" }
6637
6637
  },
6638
6638
  required: ["instId", "ordId"]
@@ -6686,7 +6686,7 @@ function registerEventContractTools() {
6686
6686
  if (sCode === "51001") {
6687
6687
  const expiryMs = inferExpiryMsFromInstId(instId);
6688
6688
  const isExpired = expiryMs !== null && expiryMs < Date.now();
6689
- 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.`;
6689
+ 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.`;
6690
6690
  throw new OkxApiError(reason, { code: sCode, endpoint: response.endpoint });
6691
6691
  }
6692
6692
  }
@@ -6721,25 +6721,25 @@ var SIGNAL_POOL_FILTER_PROPS = {
6721
6721
  type: "string",
6722
6722
  enum: ["PNL_ANY", "PNL_TOP50", "PNL_TOP20", "PNL_TOP5"],
6723
6723
  default: "PNL_ANY",
6724
- 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."
6724
+ 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."
6725
6725
  },
6726
6726
  winRateTier: {
6727
6727
  type: "string",
6728
6728
  enum: ["WR_ANY", "WR_GE_50", "WR_GE_80"],
6729
6729
  default: "WR_ANY",
6730
- 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%."
6730
+ 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%."
6731
6731
  },
6732
6732
  maxDrawdownTier: {
6733
6733
  type: "string",
6734
6734
  enum: ["MR_ANY", "MR_LE_20", "MR_LE_50"],
6735
6735
  default: "MR_ANY",
6736
- 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%."
6736
+ 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%."
6737
6737
  },
6738
6738
  aumTier: {
6739
6739
  type: "string",
6740
6740
  enum: ["AUM_ANY", "AUM_TOP50", "AUM_TOP20", "AUM_TOP5"],
6741
6741
  default: "AUM_ANY",
6742
- 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."
6742
+ 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."
6743
6743
  }
6744
6744
  };
6745
6745
  var LEADERBOARD_POOL_FILTER_PROPS = {
@@ -6757,19 +6757,19 @@ var LEADERBOARD_POOL_FILTER_PROPS = {
6757
6757
  },
6758
6758
  minPnl: {
6759
6759
  type: "string",
6760
- 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.'
6760
+ 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.'
6761
6761
  },
6762
6762
  minWinRate: {
6763
6763
  type: "string",
6764
- 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.'
6764
+ 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.'
6765
6765
  },
6766
6766
  maxDrawdown: {
6767
6767
  type: "string",
6768
- 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.'
6768
+ 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.'
6769
6769
  },
6770
6770
  minAum: {
6771
6771
  type: "string",
6772
- 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.'
6772
+ 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.'
6773
6773
  }
6774
6774
  };
6775
6775
  var LEADERBOARD_FILTER_UPSTREAM_NAMES = {
@@ -6903,11 +6903,11 @@ var PAGINATION_PROP = {
6903
6903
  }
6904
6904
  };
6905
6905
  var TRADER_ITEM_PROPS = {
6906
- authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other smartmoney_get_trader_* tools." },
6906
+ authorId: { type: "string", description: "Trader's unique ID - pass to other smartmoney_get_trader_* tools." },
6907
6907
  nickName: { type: "string", description: "Display nickname." },
6908
6908
  pnl: { type: "string", description: "Absolute PnL in USD over the requested `period` (numeric string)." },
6909
6909
  pnlRatio: { type: "string", description: 'PnL as a decimal ratio over the requested `period` (e.g. "0.35" = +35%).' },
6910
- asset: { type: "string", description: "AUM (Assets Under Management) in USD \u2014 same field that the input `minAum` filter applies to." },
6910
+ asset: { type: "string", description: "AUM (Assets Under Management) in USD - same field that the input `minAum` filter applies to." },
6911
6911
  winRate: { type: "string", description: "Lifetime win-rate as a decimal (0~1)." },
6912
6912
  maxDrawdown: { type: "string", description: 'Max drawdown as a decimal (e.g. "0.2" = 20%). Lower = lower risk.' },
6913
6913
  onboardDuration: { type: "string", description: "Days the trader has been onboarded on the leaderboard (numeric string)." },
@@ -6936,7 +6936,7 @@ var SIGNAL_ITEM_PROPS = {
6936
6936
  },
6937
6937
  tradersQualified: {
6938
6938
  type: "integer",
6939
- description: "Pool size after applying tier filters (incl. those without positions on this instrument)."
6939
+ 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."
6940
6940
  },
6941
6941
  longTraders: { type: "integer", description: "Number of pool traders currently long this asset (incl. double-sided)." },
6942
6942
  shortTraders: { type: "integer", description: "Number of pool traders currently short this asset (incl. double-sided)." },
@@ -6946,19 +6946,19 @@ var SIGNAL_ITEM_PROPS = {
6946
6946
  properties: {
6947
6947
  longNotionalUsdt: {
6948
6948
  type: "string",
6949
- 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."
6949
+ 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."
6950
6950
  },
6951
6951
  shortNotionalUsdt: {
6952
6952
  type: "string",
6953
- 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."
6953
+ 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."
6954
6954
  },
6955
6955
  netNotionalUsdt: {
6956
6956
  type: "string",
6957
- 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."
6957
+ 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."
6958
6958
  },
6959
6959
  totalNotionalUsdt: {
6960
6960
  type: "string",
6961
- 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."
6961
+ 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."
6962
6962
  },
6963
6963
  totalNotionalVs24h: {
6964
6964
  type: "string",
@@ -6988,7 +6988,7 @@ var SIGNAL_ITEM_PROPS = {
6988
6988
  },
6989
6989
  weightedLongRatio: {
6990
6990
  type: "string",
6991
- 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."
6991
+ 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."
6992
6992
  },
6993
6993
  weightedShortRatio: {
6994
6994
  type: "string",
@@ -7023,7 +7023,7 @@ var SIGNAL_HISTORY_ITEM_PROPS = {
7023
7023
  },
7024
7024
  weightedLongRatio: {
7025
7025
  type: "string",
7026
- 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."
7026
+ 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."
7027
7027
  },
7028
7028
  weightedShortRatio: {
7029
7029
  type: "string",
@@ -7043,13 +7043,16 @@ var SIGNAL_HISTORY_ITEM_PROPS = {
7043
7043
  },
7044
7044
  netNotionalUsdt: {
7045
7045
  type: "string",
7046
- 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."
7046
+ 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."
7047
7047
  },
7048
7048
  totalNotionalUsdt: {
7049
7049
  type: "string",
7050
- 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."
7050
+ 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."
7051
+ },
7052
+ tradersQualified: {
7053
+ type: "integer",
7054
+ 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."
7051
7055
  },
7052
- tradersQualified: { type: "integer", description: "Pool size after applying tier filters (includes traders without a position)." },
7053
7056
  dataVersion: { type: "string", description: "Snapshot version key in `yyyyMMddHH` UTC (10-digit, e.g. `2026042820`)." }
7054
7057
  };
7055
7058
  function registerSmartmoneyTools() {
@@ -7061,7 +7064,7 @@ function registerSmartmoneyTools() {
7061
7064
  {
7062
7065
  name: "smartmoney_get_traders_by_filter",
7063
7066
  module: "smartmoney",
7064
- 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.",
7067
+ 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.",
7065
7068
  isWrite: false,
7066
7069
  outputSchema: envelope(
7067
7070
  { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
@@ -7078,16 +7081,16 @@ function registerSmartmoneyTools() {
7078
7081
  properties: {
7079
7082
  updateTime: {
7080
7083
  type: "string",
7081
- 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).'
7084
+ 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).'
7082
7085
  },
7083
7086
  ...LEADERBOARD_POOL_FILTER_PROPS,
7084
7087
  after: {
7085
7088
  type: "string",
7086
- 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.'
7089
+ 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.'
7087
7090
  },
7088
7091
  before: {
7089
7092
  type: "string",
7090
- 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.'
7093
+ 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.'
7091
7094
  },
7092
7095
  limit: {
7093
7096
  type: "integer",
@@ -7127,7 +7130,7 @@ function registerSmartmoneyTools() {
7127
7130
  {
7128
7131
  name: "smartmoney_get_performance_by_trader",
7129
7132
  module: "smartmoney",
7130
- 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).",
7133
+ 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).",
7131
7134
  isWrite: false,
7132
7135
  outputSchema: envelope(
7133
7136
  { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
@@ -7197,7 +7200,7 @@ function registerSmartmoneyTools() {
7197
7200
  {
7198
7201
  name: "smartmoney_get_trader_positions",
7199
7202
  module: "smartmoney",
7200
- 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).",
7203
+ 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).",
7201
7204
  isWrite: false,
7202
7205
  outputSchema: envelope({
7203
7206
  type: "array",
@@ -7217,9 +7220,9 @@ function registerSmartmoneyTools() {
7217
7220
  direction: {
7218
7221
  type: "string",
7219
7222
  enum: ["long", "short"],
7220
- 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.'
7223
+ 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.'
7221
7224
  },
7222
- posCcy: { type: "string", description: 'Position currency \u2014 the asset being held, e.g. "BTC".' },
7225
+ posCcy: { type: "string", description: 'Position currency - the asset being held, e.g. "BTC".' },
7223
7226
  quoteCcy: { type: "string", description: 'Quote currency the position is priced/settled in, e.g. "USDT".' },
7224
7227
  pos: {
7225
7228
  type: "string",
@@ -7248,7 +7251,7 @@ function registerSmartmoneyTools() {
7248
7251
  },
7249
7252
  instId: {
7250
7253
  type: "string",
7251
- 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.'
7254
+ 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.'
7252
7255
  }
7253
7256
  },
7254
7257
  required: ["authorId"]
@@ -7278,7 +7281,7 @@ function registerSmartmoneyTools() {
7278
7281
  {
7279
7282
  name: "smartmoney_get_trader_positions_history",
7280
7283
  module: "smartmoney",
7281
- 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).",
7284
+ 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).",
7282
7285
  isWrite: false,
7283
7286
  outputSchema: envelope(
7284
7287
  {
@@ -7294,7 +7297,7 @@ function registerSmartmoneyTools() {
7294
7297
  },
7295
7298
  ctVal: {
7296
7299
  type: "string",
7297
- description: "Contract face value \u2014 USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
7300
+ description: "Contract face value - USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
7298
7301
  },
7299
7302
  posSide: {
7300
7303
  type: "string",
@@ -7361,15 +7364,15 @@ function registerSmartmoneyTools() {
7361
7364
  },
7362
7365
  instId: {
7363
7366
  type: "string",
7364
- 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.'
7367
+ 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.'
7365
7368
  },
7366
7369
  after: {
7367
7370
  type: "string",
7368
- description: 'Pagination cursor (older) \u2014 returns positions with `posId` smaller than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7371
+ description: 'Pagination cursor (older) - returns positions with `posId` smaller than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7369
7372
  },
7370
7373
  before: {
7371
7374
  type: "string",
7372
- description: 'Pagination cursor (newer) \u2014 returns positions with `posId` greater than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7375
+ description: 'Pagination cursor (newer) - returns positions with `posId` greater than this value. Pass the `posId` as a string, e.g. `"872913470357110787"`.'
7373
7376
  },
7374
7377
  limit: {
7375
7378
  type: "integer",
@@ -7415,7 +7418,7 @@ function registerSmartmoneyTools() {
7415
7418
  {
7416
7419
  name: "smartmoney_get_trader_orders_history",
7417
7420
  module: "smartmoney",
7418
- 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).",
7421
+ 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).",
7419
7422
  isWrite: false,
7420
7423
  outputSchema: envelope(
7421
7424
  {
@@ -7479,15 +7482,15 @@ function registerSmartmoneyTools() {
7479
7482
  },
7480
7483
  instId: {
7481
7484
  type: "string",
7482
- 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.'
7485
+ 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.'
7483
7486
  },
7484
7487
  after: {
7485
7488
  type: "string",
7486
- description: 'Pagination cursor (older) \u2014 returns trades with `ordId` smaller than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7489
+ description: 'Pagination cursor (older) - returns trades with `ordId` smaller than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7487
7490
  },
7488
7491
  before: {
7489
7492
  type: "string",
7490
- description: 'Pagination cursor (newer) \u2014 returns trades with `ordId` greater than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7493
+ description: 'Pagination cursor (newer) - returns trades with `ordId` greater than this value. Pass the `ordId` as a string, e.g. `"872913470357110787"`.'
7491
7494
  },
7492
7495
  limit: {
7493
7496
  type: "integer",
@@ -7541,7 +7544,7 @@ function registerSmartmoneyTools() {
7541
7544
  items: {
7542
7545
  type: "object",
7543
7546
  properties: {
7544
- authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other `smartmoney_get_trader_*` tools." },
7547
+ authorId: { type: "string", description: "Trader's unique ID - pass to other `smartmoney_get_trader_*` tools." },
7545
7548
  nickName: { type: "string", description: "Display nickname matched against the keyword." },
7546
7549
  followerCount: { type: "string", description: "OKX-platform follower count (numeric string; Twitter followers excluded). Sort key." }
7547
7550
  }
@@ -7583,7 +7586,7 @@ function registerSmartmoneyTools() {
7583
7586
  {
7584
7587
  name: "smartmoney_get_signal_overview_by_filter",
7585
7588
  module: "smartmoney",
7586
- 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).",
7589
+ 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).",
7587
7590
  isWrite: false,
7588
7591
  outputSchema: envelope({
7589
7592
  type: "array",
@@ -7604,7 +7607,7 @@ function registerSmartmoneyTools() {
7604
7607
  type: "array",
7605
7608
  items: { type: "string" },
7606
7609
  minItems: 1,
7607
- 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.'
7610
+ 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.'
7608
7611
  },
7609
7612
  ...SIGNAL_POOL_FILTER_PROPS,
7610
7613
  lmtNum: {
@@ -7612,7 +7615,7 @@ function registerSmartmoneyTools() {
7612
7615
  minimum: 1,
7613
7616
  maximum: 2e3,
7614
7617
  default: 100,
7615
- 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."
7618
+ 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)."
7616
7619
  }
7617
7620
  },
7618
7621
  required: ["sortBy", "period"]
@@ -7624,7 +7627,7 @@ function registerSmartmoneyTools() {
7624
7627
  if (instCcyList && topInstrumentsRaw !== void 0) {
7625
7628
  throw actionableError(
7626
7629
  '"topInstruments" and "instCcyList" are mutually exclusive.',
7627
- "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7630
+ "Pass exactly one - `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7628
7631
  );
7629
7632
  }
7630
7633
  const response = await context.client.privateGet(
@@ -7643,7 +7646,7 @@ function registerSmartmoneyTools() {
7643
7646
  {
7644
7647
  name: "smartmoney_get_signal_overview_by_trader",
7645
7648
  module: "smartmoney",
7646
- 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).",
7649
+ 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).",
7647
7650
  isWrite: false,
7648
7651
  outputSchema: envelope({
7649
7652
  type: "array",
@@ -7670,7 +7673,7 @@ function registerSmartmoneyTools() {
7670
7673
  type: "array",
7671
7674
  items: { type: "string" },
7672
7675
  minItems: 1,
7673
- 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.'
7676
+ 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.'
7674
7677
  },
7675
7678
  sortBy: {
7676
7679
  type: "string",
@@ -7703,7 +7706,7 @@ function registerSmartmoneyTools() {
7703
7706
  if (instCcyList && topInstrumentsRaw !== void 0) {
7704
7707
  throw actionableError(
7705
7708
  '"topInstruments" and "instCcyList" are mutually exclusive.',
7706
- "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7709
+ "Pass exactly one - `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7707
7710
  );
7708
7711
  }
7709
7712
  const response = await context.client.privateGet(
@@ -7725,7 +7728,7 @@ function registerSmartmoneyTools() {
7725
7728
  {
7726
7729
  name: "smartmoney_get_signal_trend_by_filter",
7727
7730
  module: "smartmoney",
7728
- 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.",
7731
+ 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.",
7729
7732
  isWrite: false,
7730
7733
  outputSchema: envelope({
7731
7734
  type: "array",
@@ -7737,11 +7740,11 @@ function registerSmartmoneyTools() {
7737
7740
  properties: {
7738
7741
  instCcy: {
7739
7742
  type: "string",
7740
- 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.'
7743
+ 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.'
7741
7744
  },
7742
7745
  asOfTime: {
7743
7746
  type: "string",
7744
- 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.'
7747
+ 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.'
7745
7748
  },
7746
7749
  granularity: {
7747
7750
  type: "string",
@@ -7762,7 +7765,7 @@ function registerSmartmoneyTools() {
7762
7765
  minimum: 1,
7763
7766
  maximum: 2e3,
7764
7767
  default: 100,
7765
- description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Default 100, max 2000."
7768
+ 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)."
7766
7769
  }
7767
7770
  },
7768
7771
  required: ["instCcy", "granularity", "sortBy", "period"]
@@ -7797,7 +7800,7 @@ function registerSmartmoneyTools() {
7797
7800
  {
7798
7801
  name: "smartmoney_get_signal_trend_by_trader",
7799
7802
  module: "smartmoney",
7800
- 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.",
7803
+ 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.",
7801
7804
  isWrite: false,
7802
7805
  outputSchema: envelope({
7803
7806
  type: "array",
@@ -7815,11 +7818,11 @@ function registerSmartmoneyTools() {
7815
7818
  },
7816
7819
  instCcy: {
7817
7820
  type: "string",
7818
- 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.'
7821
+ 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.'
7819
7822
  },
7820
7823
  asOfTime: {
7821
7824
  type: "string",
7822
- 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.'
7825
+ 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.'
7823
7826
  },
7824
7827
  granularity: {
7825
7828
  type: "string",
@@ -7899,7 +7902,7 @@ function buildContractTradeTools(cfg) {
7899
7902
  {
7900
7903
  name: n("place_order"),
7901
7904
  module,
7902
- 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.`,
7905
+ 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.`,
7903
7906
  isWrite: true,
7904
7907
  inputSchema: {
7905
7908
  type: "object",
@@ -8239,10 +8242,10 @@ function buildContractTradeTools(cfg) {
8239
8242
  module,
8240
8243
  description: `Set leverage for a ${label} instrument or position. [CAUTION] Changes risk parameters.
8241
8244
  Scenarios (SWAP/FUTURES only):
8242
- \u2022 cross + any instId under the index \u2192 sets leverage at the index level
8243
- \u2022 isolated + buy-sell (net) posMode \u2192 instId only
8244
- \u2022 isolated + long-short (hedge) posMode \u2192 instId + posSide=long|short (BOTH directions must be set separately)
8245
- 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.`,
8245
+ \u2022 cross + any instId under the index -> sets leverage at the index level
8246
+ \u2022 isolated + buy-sell (net) posMode -> instId only
8247
+ \u2022 isolated + long-short (hedge) posMode -> instId + posSide=long|short (BOTH directions must be set separately)
8248
+ 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.`,
8246
8249
  isWrite: true,
8247
8250
  inputSchema: {
8248
8251
  type: "object",
@@ -8250,13 +8253,13 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
8250
8253
  instId: { type: "string", description: instIdExample },
8251
8254
  lever: {
8252
8255
  type: "string",
8253
- description: "Leverage multiplier as a positive number string, e.g. '10'. Max value depends on the instrument (query market_get_instruments \u2192 lever)."
8256
+ description: "Leverage multiplier as a positive number string, e.g. '10'. Max value depends on the instrument (query market_get_instruments -> lever)."
8254
8257
  },
8255
8258
  mgnMode: { type: "string", enum: ["cross", "isolated"] },
8256
8259
  posSide: {
8257
8260
  type: "string",
8258
8261
  enum: ["long", "short"],
8259
- 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."
8262
+ 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."
8260
8263
  }
8261
8264
  },
8262
8265
  required: ["instId", "lever", "mgnMode"]
@@ -9072,12 +9075,12 @@ var OI_BARS = ["5m", "15m", "1H", "4H", "1D"];
9072
9075
  function registerMarketFilterTools() {
9073
9076
  return [
9074
9077
  // ─────────────────────────────────────────────────────────────────────────
9075
- // market_filter /api/v5/aigc/mcp/market-filter
9078
+ // market_filter - /api/v5/aigc/mcp/market-filter
9076
9079
  // ─────────────────────────────────────────────────────────────────────────
9077
9080
  {
9078
9081
  name: "market_filter",
9079
9082
  module: "market",
9080
- 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.",
9083
+ 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.",
9081
9084
  isWrite: false,
9082
9085
  inputSchema: {
9083
9086
  type: "object",
@@ -9161,7 +9164,7 @@ function registerMarketFilterTools() {
9161
9164
  sortBy: {
9162
9165
  type: "string",
9163
9166
  enum: ["last", "chg24hPct", "marketCapUsd", "volUsd24h", "fundingRate", "oiUsd", "listTime"],
9164
- 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."
9167
+ 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."
9165
9168
  },
9166
9169
  sortOrder: {
9167
9170
  type: "string",
@@ -9209,12 +9212,12 @@ function registerMarketFilterTools() {
9209
9212
  }
9210
9213
  },
9211
9214
  // ─────────────────────────────────────────────────────────────────────────
9212
- // market_get_oi_history /api/v5/aigc/mcp/oi-history
9215
+ // market_get_oi_history - /api/v5/aigc/mcp/oi-history
9213
9216
  // ─────────────────────────────────────────────────────────────────────────
9214
9217
  {
9215
9218
  name: "market_get_oi_history",
9216
9219
  module: "market",
9217
- 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.",
9220
+ 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.",
9218
9221
  isWrite: false,
9219
9222
  inputSchema: {
9220
9223
  type: "object",
@@ -9256,12 +9259,12 @@ function registerMarketFilterTools() {
9256
9259
  }
9257
9260
  },
9258
9261
  // ─────────────────────────────────────────────────────────────────────────
9259
- // market_filter_oi_change /api/v5/aigc/mcp/oi-change-filter
9262
+ // market_filter_oi_change - /api/v5/aigc/mcp/oi-change-filter
9260
9263
  // ─────────────────────────────────────────────────────────────────────────
9261
9264
  {
9262
9265
  name: "market_filter_oi_change",
9263
9266
  module: "market",
9264
- 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.",
9267
+ 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.",
9265
9268
  isWrite: false,
9266
9269
  inputSchema: {
9267
9270
  type: "object",
@@ -9293,7 +9296,7 @@ function registerMarketFilterTools() {
9293
9296
  sortBy: {
9294
9297
  type: "string",
9295
9298
  enum: ["oiUsd", "oiDeltaUsd", "oiDeltaPct", "absOiDeltaPct", "volUsd24h", "fundingRate", "last"],
9296
- 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."
9299
+ 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."
9297
9300
  },
9298
9301
  sortOrder: {
9299
9302
  type: "string",
@@ -9328,7 +9331,7 @@ function registerMarketFilterTools() {
9328
9331
  }
9329
9332
  },
9330
9333
  // ─────────────────────────────────────────────────────────────────────────
9331
- // market_get_pair_spread /api/v5/aigc/mcp/pair-spread
9334
+ // market_get_pair_spread - /api/v5/aigc/mcp/pair-spread
9332
9335
  // ─────────────────────────────────────────────────────────────────────────
9333
9336
  {
9334
9337
  name: "market_get_pair_spread",
@@ -9614,7 +9617,7 @@ var D_COINS_SENTIMENT = 'Comma-separated uppercase ticker symbols, max 20 (e.g.
9614
9617
  var D_LANGUAGE = "Content language: zh-CN or en-US. Infer from user's message. No server default.";
9615
9618
  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.";
9616
9619
  var D_END = "End time, Unix epoch milliseconds. Parse relative time if given. Omit for no upper bound.";
9617
- 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.";
9620
+ 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.";
9618
9621
  var D_PLATFORM = "Filter by news source. Use values from news_get_domains (e.g. blockbeats, odaily_flash). Omit for all sources.";
9619
9622
  var D_LIMIT = "Number of results (default 10, max 50).";
9620
9623
  function registerNewsTools() {
@@ -9713,7 +9716,7 @@ function registerNewsTools() {
9713
9716
  {
9714
9717
  name: "news_search",
9715
9718
  module: "news",
9716
- 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.",
9719
+ 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.",
9717
9720
  isWrite: false,
9718
9721
  inputSchema: {
9719
9722
  type: "object",
@@ -9823,7 +9826,7 @@ function registerNewsTools() {
9823
9826
  {
9824
9827
  name: "news_get_coin_sentiment",
9825
9828
  module: "news",
9826
- 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.",
9829
+ 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.",
9827
9830
  isWrite: false,
9828
9831
  inputSchema: {
9829
9832
  type: "object",
@@ -9836,7 +9839,7 @@ function registerNewsTools() {
9836
9839
  },
9837
9840
  trendPoints: {
9838
9841
  type: "number",
9839
- description: "Trend data points. Pass for time-series trend; omit for snapshot. Guide: 1h\u219224, 4h\u21926, 24h\u21927."
9842
+ description: "Trend data points. Pass for time-series trend; omit for snapshot. Guide: 1h->24, 4h->6, 24h->7."
9840
9843
  }
9841
9844
  },
9842
9845
  required: ["coins"]
@@ -9903,7 +9906,7 @@ function registerNewsTools() {
9903
9906
  {
9904
9907
  name: "news_list_calendar_regions",
9905
9908
  module: "news",
9906
- 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.",
9909
+ 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.",
9907
9910
  isWrite: false,
9908
9911
  inputSchema: { type: "object", properties: {}, required: [] },
9909
9912
  handler: async () => ({ data: CALENDAR_REGIONS })
@@ -9911,15 +9914,15 @@ function registerNewsTools() {
9911
9914
  {
9912
9915
  name: "news_get_economic_calendar",
9913
9916
  module: "news",
9914
- 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.",
9917
+ 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.",
9915
9918
  isWrite: false,
9916
9919
  inputSchema: {
9917
9920
  type: "object",
9918
9921
  properties: {
9919
9922
  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." },
9920
9923
  importance: { type: "string", enum: ["1", "2", "3"], description: "Importance level: 1=low, 2=medium, 3=high. Omit for all levels." },
9921
- 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." },
9922
- 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." },
9924
+ before: { type: "string", description: "Lower time bound - returns events NEWER than this timestamp (reversed semantics). Pair with 'after' for future-event windows. Unix ms." },
9925
+ 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." },
9923
9926
  limit: { type: "number", minimum: 1, maximum: 100, description: "Number of results (default 100, max 100)." }
9924
9927
  },
9925
9928
  required: []
@@ -10077,7 +10080,7 @@ function registerOptionAlgoTools() {
10077
10080
  {
10078
10081
  name: "option_amend_algo_order",
10079
10082
  module: "option",
10080
- 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.",
10083
+ 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.",
10081
10084
  isWrite: true,
10082
10085
  inputSchema: {
10083
10086
  type: "object",
@@ -10240,7 +10243,7 @@ function registerOptionTools() {
10240
10243
  {
10241
10244
  name: "option_place_order",
10242
10245
  module: "option",
10243
- 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.",
10246
+ 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.",
10244
10247
  isWrite: true,
10245
10248
  inputSchema: {
10246
10249
  type: "object",
@@ -11014,7 +11017,7 @@ function registerSpotTradeTools() {
11014
11017
  {
11015
11018
  name: "spot_amend_algo_order",
11016
11019
  module: "spot",
11017
- 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.",
11020
+ 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.",
11018
11021
  isWrite: true,
11019
11022
  inputSchema: {
11020
11023
  type: "object",
@@ -11083,7 +11086,7 @@ function registerSpotTradeTools() {
11083
11086
  {
11084
11087
  name: "spot_get_algo_orders",
11085
11088
  module: "spot",
11086
- description: "Query spot algo orders (TP/SL) \u2014 pending or history.",
11089
+ description: "Query spot algo orders (TP/SL) - pending or history.",
11087
11090
  isWrite: false,
11088
11091
  inputSchema: {
11089
11092
  type: "object",
@@ -11376,16 +11379,16 @@ function registerSpotTradeTools() {
11376
11379
  }
11377
11380
  },
11378
11381
  // ── set_leverage (SPOT margin: instId-level isolated OR ccy-level cross) ──
11379
- // Covers OKX scenarios 15 (everything except SWAP/FUTURES, which are in
11382
+ // Covers OKX scenarios 1-5 (everything except SWAP/FUTURES, which are in
11380
11383
  // contract-trade.ts). Callers supply exactly one of {instId, ccy}:
11381
- // • instId + isolated scenario 1 (pair-level margin)
11382
- // • instId + cross scenario 3 (contract-mode pair-level cross margin)
11383
- // • ccy + cross scenarios 2 / 4 / 5 (spot/multi-ccy/PM currency-level cross)
11384
+ // • instId + isolated -> scenario 1 (pair-level margin)
11385
+ // • instId + cross -> scenario 3 (contract-mode pair-level cross margin)
11386
+ // • ccy + cross -> scenarios 2 / 4 / 5 (spot/multi-ccy/PM currency-level cross)
11384
11387
  // Not applicable: posSide (spot has no long/short hedge).
11385
11388
  {
11386
11389
  name: "spot_set_leverage",
11387
11390
  module: "spot",
11388
- 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.",
11391
+ 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.",
11389
11392
  isWrite: true,
11390
11393
  inputSchema: {
11391
11394
  type: "object",
@@ -11400,7 +11403,7 @@ function registerSpotTradeTools() {
11400
11403
  },
11401
11404
  lever: {
11402
11405
  type: "string",
11403
- 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."
11406
+ 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."
11404
11407
  },
11405
11408
  mgnMode: {
11406
11409
  type: "string",
@@ -11421,7 +11424,7 @@ function registerSpotTradeTools() {
11421
11424
  }
11422
11425
  if (instId && ccy) {
11423
11426
  throw new ValidationError(
11424
- `Parameters "instId" and "ccy" are mutually exclusive \u2014 provide only one. instId sets pair-level leverage; ccy sets currency-level cross margin leverage.`
11427
+ `Parameters "instId" and "ccy" are mutually exclusive - provide only one. instId sets pair-level leverage; ccy sets currency-level cross margin leverage.`
11425
11428
  );
11426
11429
  }
11427
11430
  const leverRaw = requireString(args, "lever");
@@ -11468,7 +11471,7 @@ function registerSwapTradeTools() {
11468
11471
  {
11469
11472
  name: "swap_amend_algo_order",
11470
11473
  module: "swap",
11471
- 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.",
11474
+ 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.",
11472
11475
  isWrite: true,
11473
11476
  inputSchema: {
11474
11477
  type: "object",
@@ -11621,9 +11624,9 @@ function readFullConfig() {
11621
11624
  throw new ConfigError(
11622
11625
  `Failed to parse ${path4}: ${err instanceof Error ? err.message : String(err)}`,
11623
11626
  `If your passphrase or keys contain special characters:
11624
- - Contains # \\ " \u2192 use single quotes: passphrase = 'your#pass'
11625
- - Contains ' \u2192 use double quotes: passphrase = "your'pass"
11626
- - Contains both \u2192 use triple quotes: passphrase = '''your'#pass'''
11627
+ - Contains # \\ " -> use single quotes: passphrase = 'your#pass'
11628
+ - Contains ' -> use double quotes: passphrase = "your'pass"
11629
+ - Contains both -> use triple quotes: passphrase = '''your'#pass'''
11627
11630
  Or re-run: okx config init`
11628
11631
  );
11629
11632
  }
@@ -11751,6 +11754,9 @@ async function loadConfig(cli) {
11751
11754
  }
11752
11755
  var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
11753
11756
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11757
+ var NEGATIVE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
11758
+ var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
11759
+ var FETCH_TIMEOUT_MS = 3e3;
11754
11760
  function readCache2() {
11755
11761
  try {
11756
11762
  if (existsSync4(CACHE_FILE)) {
@@ -11767,7 +11773,73 @@ function writeCache2(cache) {
11767
11773
  } catch {
11768
11774
  }
11769
11775
  }
11776
+ function resolveNpmRegistry() {
11777
+ const envRegistry = process.env.npm_config_registry;
11778
+ if (envRegistry) {
11779
+ return envRegistry.endsWith("/") ? envRegistry : `${envRegistry}/`;
11780
+ }
11781
+ for (const filePath of buildNpmrcCandidates()) {
11782
+ const registry = readNpmrcRegistry(filePath);
11783
+ if (registry) {
11784
+ return registry.endsWith("/") ? registry : `${registry}/`;
11785
+ }
11786
+ }
11787
+ return DEFAULT_REGISTRY;
11788
+ }
11789
+ function buildNpmrcCandidates() {
11790
+ const paths = [];
11791
+ const seen = /* @__PURE__ */ new Set();
11792
+ const add = (p) => {
11793
+ if (!seen.has(p)) {
11794
+ seen.add(p);
11795
+ paths.push(p);
11796
+ }
11797
+ };
11798
+ let dir = process.cwd();
11799
+ const root = dir.startsWith("/") ? "/" : dir.slice(0, 3);
11800
+ while (true) {
11801
+ add(join8(dir, ".npmrc"));
11802
+ if (dir === root) break;
11803
+ const parent = join8(dir, "..");
11804
+ if (parent === dir) break;
11805
+ dir = parent;
11806
+ }
11807
+ add(join8(homedir6(), ".npmrc"));
11808
+ if (process.platform !== "win32") {
11809
+ add("/etc/npmrc");
11810
+ }
11811
+ return paths;
11812
+ }
11813
+ function readNpmrcRegistry(filePath) {
11814
+ try {
11815
+ if (!existsSync4(filePath)) return null;
11816
+ const lines = readFileSync5(filePath, "utf-8").split(/\r?\n/);
11817
+ for (const line of lines) {
11818
+ const trimmed = line.trim();
11819
+ if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
11820
+ const eqIdx = trimmed.indexOf("=");
11821
+ const key = trimmed.slice(0, eqIdx).trim();
11822
+ const value = trimmed.slice(eqIdx + 1).trim();
11823
+ if (key === "registry" && value) return value;
11824
+ }
11825
+ } catch {
11826
+ }
11827
+ return null;
11828
+ }
11829
+ async function fetchFromRegistry(packageName, suffix = "") {
11830
+ const registry = resolveNpmRegistry();
11831
+ const url = `${registry}${encodeURIComponent(packageName)}${suffix}`;
11832
+ try {
11833
+ return await fetch(url, {
11834
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
11835
+ headers: { accept: "application/json" }
11836
+ });
11837
+ } catch {
11838
+ return null;
11839
+ }
11840
+ }
11770
11841
  function isNewerVersion(current, latest) {
11842
+ if (!latest) return false;
11771
11843
  const parse2 = (v) => v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10));
11772
11844
  const [cMaj, cMin, cPat] = parse2(current);
11773
11845
  const [lMaj, lMin, lPat] = parse2(latest);
@@ -11777,14 +11849,8 @@ function isNewerVersion(current, latest) {
11777
11849
  }
11778
11850
  async function fetchLatestVersion(packageName) {
11779
11851
  try {
11780
- const controller = new AbortController();
11781
- const timeout = setTimeout(() => controller.abort(), 3e3);
11782
- const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
11783
- signal: controller.signal,
11784
- headers: { accept: "application/json" }
11785
- });
11786
- clearTimeout(timeout);
11787
- if (!res.ok) return null;
11852
+ const res = await fetchFromRegistry(packageName, "/latest");
11853
+ if (!res || !res.ok) return null;
11788
11854
  const data = await res.json();
11789
11855
  return data.version ?? null;
11790
11856
  } catch {
@@ -11793,26 +11859,31 @@ async function fetchLatestVersion(packageName) {
11793
11859
  }
11794
11860
  function refreshCacheInBackground(packageName) {
11795
11861
  fetchLatestVersion(packageName).then((latest) => {
11796
- if (!latest) return;
11797
11862
  const cache = readCache2();
11798
- cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
11863
+ if (latest) {
11864
+ cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
11865
+ } else {
11866
+ cache[packageName] = { latestVersion: null, checkedAt: Date.now(), failed: true };
11867
+ }
11799
11868
  writeCache2(cache);
11800
11869
  }).catch(() => {
11801
11870
  });
11802
11871
  }
11803
11872
  function checkForUpdates(packageName, currentVersion) {
11873
+ if (process.env.OKX_UPDATE_CHECK === "false") return;
11804
11874
  const cache = readCache2();
11805
11875
  const entry = cache[packageName];
11806
- if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
11876
+ if (entry && entry.latestVersion && isNewerVersion(currentVersion, entry.latestVersion)) {
11807
11877
  process.stderr.write(
11808
11878
  `
11809
- Update available for ${packageName}: ${currentVersion} \u2192 ${entry.latestVersion}
11879
+ Update available for ${packageName}: ${currentVersion} -> ${entry.latestVersion}
11810
11880
  Run: npm install -g ${packageName}
11811
11881
 
11812
11882
  `
11813
11883
  );
11814
11884
  }
11815
- if (!entry || Date.now() - entry.checkedAt > CHECK_INTERVAL_MS) {
11885
+ const ttl = entry?.failed ? NEGATIVE_CHECK_INTERVAL_MS : CHECK_INTERVAL_MS;
11886
+ if (!entry || Date.now() - entry.checkedAt > ttl) {
11816
11887
  refreshCacheInBackground(packageName);
11817
11888
  }
11818
11889
  }
@@ -11997,7 +12068,7 @@ function mergeJsonConfig(configPath, serverName, entry) {
11997
12068
  }
11998
12069
  const backupPath = configPath + ".bak";
11999
12070
  fs3.copyFileSync(configPath, backupPath);
12000
- process.stdout.write(` Backup \u2192 ${backupPath}
12071
+ process.stdout.write(` Backup -> ${backupPath}
12001
12072
  `);
12002
12073
  }
12003
12074
  if (typeof data.mcpServers !== "object" || data.mcpServers === null) {
@@ -12068,7 +12139,7 @@ var _require = createRequire(import.meta.url);
12068
12139
  var pkg = _require("../package.json");
12069
12140
  var SERVER_NAME = "okx-trade-mcp";
12070
12141
  var SERVER_VERSION = pkg.version;
12071
- var GIT_HASH = true ? "0566db8f" : "dev";
12142
+ var GIT_HASH = true ? "c34b282c" : "dev";
12072
12143
 
12073
12144
  // src/server.ts
12074
12145
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -12250,7 +12321,7 @@ Options:
12250
12321
  --profile <name> Profile to load from ${configFilePath()}
12251
12322
  Falls back to default_profile in config, then "default"
12252
12323
  --site <site> OKX site to connect to: global, eea, us (default: global)
12253
- global \u2192 www.okx.com, eea \u2192 eea.okx.com, us \u2192 app.okx.com
12324
+ global -> www.okx.com, eea -> eea.okx.com, us -> app.okx.com
12254
12325
  --read-only Expose only read/query tools and disable write operations
12255
12326
  --demo Enable simulated trading (injects x-simulated-trading: 1)
12256
12327
  --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)