hvip-mcp-server 0.2.44 → 0.2.46

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.
Files changed (2) hide show
  1. package/dist/index.js +1971 -174
  2. package/package.json +64 -61
package/dist/index.js CHANGED
@@ -25298,7 +25298,41 @@ var INST_TYPE_CONTRACTS = ["SWAP", "FUTURES", "OPTION"];
25298
25298
  var INST_TYPE_SWAP_FUT = ["SWAP", "FUTURES"];
25299
25299
  var INST_TYPE_MARGIN_PUB = ["MARGIN", "SWAP", "FUTURES", "OPTION"];
25300
25300
  var INST_TYPE_RUBIK = ["SPOT", "CONTRACTS"];
25301
+ function autoSummary(data) {
25302
+ if (Array.isArray(data)) {
25303
+ const n = data.length;
25304
+ if (n === 0) return "\u8FD4\u56DE\u7A7A\u5217\u8868";
25305
+ const first = data[0];
25306
+ if (first?.instId && first?.last) return `\u8FD4\u56DE ${n} \u4E2A\u4EA7\u54C1\u884C\u60C5`;
25307
+ if (first?.instId && first?.pos) return `\u8FD4\u56DE ${n} \u4E2A\u6301\u4ED3`;
25308
+ if (first?.ccy) return `\u8FD4\u56DE ${n} \u4E2A\u5E01\u79CD\u4FE1\u606F`;
25309
+ if (first?.ordId) return `\u8FD4\u56DE ${n} \u7B14\u8BA2\u5355`;
25310
+ if (first?.ts) return `\u8FD4\u56DE ${n} \u6761\u65F6\u95F4\u5E8F\u5217\u6570\u636E`;
25311
+ if (first?.name) return `\u8FD4\u56DE ${n} \u6761\u8BB0\u5F55`;
25312
+ return `\u8FD4\u56DE ${n} \u6761\u8BB0\u5F55`;
25313
+ }
25314
+ if (typeof data === "object" && data !== null) {
25315
+ const d = data;
25316
+ if (d.ok === true && d.message) return d.message;
25317
+ if (d._summary) return void 0;
25318
+ if (d.data && Array.isArray(d.data)) return `\u8FD4\u56DE ${d.data.length} \u6761\u8BB0\u5F55`;
25319
+ }
25320
+ return void 0;
25321
+ }
25301
25322
  function toResult(data) {
25323
+ if (Array.isArray(data)) {
25324
+ const summary = autoSummary(data);
25325
+ const wrapper = { list: data };
25326
+ if (summary) wrapper._summary = summary;
25327
+ return { content: [{ type: "text", text: JSON.stringify(wrapper, null, 2) }] };
25328
+ }
25329
+ if (typeof data === "object" && data !== null && !data._summary) {
25330
+ const summary = autoSummary(data);
25331
+ if (summary) {
25332
+ const enriched = { _summary: summary, ...data };
25333
+ return { content: [{ type: "text", text: JSON.stringify(enriched, null, 2) }] };
25334
+ }
25335
+ }
25302
25336
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
25303
25337
  }
25304
25338
  function classifyError(msg) {
@@ -25320,6 +25354,57 @@ function classifyError(msg) {
25320
25354
  if (msg.includes("API Key")) return { errorCode: "AUTH_REQUIRED", errorCategory: "AUTH" };
25321
25355
  return { errorCode: "UNKNOWN_ERROR", errorCategory: "BUSINESS" };
25322
25356
  }
25357
+ function classifyRisk(toolName) {
25358
+ const admin = ["okx_set_account_mode", "okx_set_position_mode", "okx_set_settle_currency"];
25359
+ if (admin.includes(toolName)) return "ADMIN";
25360
+ const fund = ["okx_withdrawal"];
25361
+ if (fund.some((p) => toolName.startsWith(p))) return "FUND_TRANSFER";
25362
+ const writePrefixes = [
25363
+ "okx_place_",
25364
+ "okx_cancel_",
25365
+ "okx_amend_",
25366
+ "okx_create_",
25367
+ "okx_stop_",
25368
+ "okx_close_",
25369
+ "okx_batch_",
25370
+ "okx_set_",
25371
+ "okx_transfer",
25372
+ "okx_borrow",
25373
+ "okx_repay",
25374
+ "okx_convert_trade",
25375
+ "okx_preset_",
25376
+ "okx_activate_",
25377
+ "okx_move_",
25378
+ "okx_copy_",
25379
+ "okx_first_",
25380
+ "okx_one_click_",
25381
+ "okx_easy_convert",
25382
+ "agent_quick_trade"
25383
+ ];
25384
+ if (writePrefixes.some((p) => toolName.startsWith(p))) return "WRITE";
25385
+ const readSpecials = [
25386
+ "agent_simulate_order",
25387
+ "okx_preflight_check",
25388
+ "okx_agent_feedback",
25389
+ "agent_catalog",
25390
+ "agent_catalog_detail",
25391
+ "agent_hub_status",
25392
+ "agent_hub_dispatch",
25393
+ "agent_hub_review",
25394
+ "agent_room_send",
25395
+ "agent_room_view",
25396
+ "okx_ws_subscribe",
25397
+ "okx_ws_events",
25398
+ "okx_ws_status",
25399
+ "okx_ws_close",
25400
+ "xlayer_subscribe",
25401
+ "xlayer_get_events",
25402
+ "xlayer_unsubscribe"
25403
+ ];
25404
+ if (readSpecials.includes(toolName)) return "READ";
25405
+ if (toolName.startsWith("xlayer_call")) return "WRITE";
25406
+ return "READ";
25407
+ }
25323
25408
  function toError(e) {
25324
25409
  const msg = e instanceof Error ? e.message : String(e);
25325
25410
  const { errorCode, errorCategory } = classifyError(msg);
@@ -31343,6 +31428,59 @@ function registerFiatTools(server, auth) {
31343
31428
  var fs = __toESM(require("node:fs"));
31344
31429
  var path = __toESM(require("node:path"));
31345
31430
  var os = __toESM(require("node:os"));
31431
+ async function fetchAllSettled(fetchers) {
31432
+ const entries = Object.entries(fetchers);
31433
+ const keys = entries.map((e) => e[0]);
31434
+ const results = await Promise.allSettled(entries.map((e) => e[1]));
31435
+ const errors = [];
31436
+ for (let i = 0; i < results.length; i++) {
31437
+ if (results[i].status === "rejected") {
31438
+ const r = results[i];
31439
+ if (r.status === "rejected") {
31440
+ errors.push(`${keys[i]}: ${r.reason?.message ?? String(r.reason)}`);
31441
+ }
31442
+ }
31443
+ }
31444
+ const get = (name) => {
31445
+ const idx = keys.indexOf(name);
31446
+ if (idx < 0) return null;
31447
+ const r = results[idx];
31448
+ if (r.status === "rejected") return null;
31449
+ return r.value;
31450
+ };
31451
+ return { get, errors };
31452
+ }
31453
+ function inferInstType(instId) {
31454
+ const upper = instId.toUpperCase();
31455
+ if (upper.includes("-SWAP")) return "SWAP";
31456
+ if (upper.includes("-OPTION") || /-[\d]+-[CP]$/.test(upper)) return "OPTION";
31457
+ if (upper.includes("-FUTURES")) return "FUTURES";
31458
+ if (upper.includes("MARGIN")) return "MARGIN";
31459
+ if (/-[\d]{6,}/.test(upper)) return "FUTURES";
31460
+ return "SPOT";
31461
+ }
31462
+ function inferFeeCcy(instId, tdMode) {
31463
+ if (tdMode === "cash") {
31464
+ const parts = instId.split("-");
31465
+ return parts[1] || "USDT";
31466
+ }
31467
+ return "USDT";
31468
+ }
31469
+ function parseNumeric(val, label) {
31470
+ const n = parseFloat(val);
31471
+ if (isNaN(n) || !isFinite(n)) {
31472
+ return { ok: false, error: `\u53C2\u6570 ${label}="${val}" \u4E0D\u662F\u6709\u6548\u6570\u5B57\uFF0CAgent \u8BF7\u68C0\u67E5\u7528\u6237\u8F93\u5165\u540E\u91CD\u8BD5` };
31473
+ }
31474
+ return { ok: true, value: n };
31475
+ }
31476
+ function extractOrderbook(raw) {
31477
+ const item = Array.isArray(raw) ? raw[0] : raw;
31478
+ if (!item) return { asks: [], bids: [] };
31479
+ return {
31480
+ asks: item.asks ?? [],
31481
+ bids: item.bids ?? []
31482
+ };
31483
+ }
31346
31484
  function registerAgentUtils(server, auth) {
31347
31485
  server.tool(
31348
31486
  "okx_account_overview",
@@ -31351,13 +31489,13 @@ function registerAgentUtils(server, auth) {
31351
31489
  async () => {
31352
31490
  if (!auth) return toError(AUTH_REQUIRED);
31353
31491
  try {
31354
- const [balance, positions, config2, valuation] = await Promise.allSettled([
31355
- privateApi.getBalance(auth),
31356
- privateApi.getPositions(auth),
31357
- privateApi.getAccountConfig(auth),
31358
- privateApi.getAssetValuation(auth)
31359
- ]);
31360
- const balanceData = balance.status === "fulfilled" ? balance.value : [];
31492
+ const { get, errors } = await fetchAllSettled({
31493
+ balance: privateApi.getBalance(auth),
31494
+ positions: privateApi.getPositions(auth),
31495
+ config: privateApi.getAccountConfig(auth),
31496
+ valuation: privateApi.getAssetValuation(auth)
31497
+ });
31498
+ const balanceData = get("balance") ?? [];
31361
31499
  const totalEq = balanceData.length > 0 ? balanceData[0]?.totalEq : "N/A";
31362
31500
  const details = balanceData.length > 0 ? balanceData[0]?.details ?? [] : [];
31363
31501
  const nonZero = details.filter((d) => parseFloat(d.availBal || d.cashBal || "0") > 0);
@@ -31368,7 +31506,7 @@ function registerAgentUtils(server, auth) {
31368
31506
  frozen: d.frozenBal ?? "0",
31369
31507
  upl: d.upl
31370
31508
  }));
31371
- const posData = positions.status === "fulfilled" ? positions.value : [];
31509
+ const posData = get("positions") ?? [];
31372
31510
  const activePositions = posData.filter(
31373
31511
  (p) => parseFloat(p.avgPx || "0") > 0 || parseFloat(p.pos || "0") !== 0
31374
31512
  );
@@ -31384,9 +31522,12 @@ function registerAgentUtils(server, auth) {
31384
31522
  margin: p.margin,
31385
31523
  liqPx: p.liqPx
31386
31524
  }));
31387
- const cfg = config2.status === "fulfilled" ? config2.value?.[0] ?? {} : {};
31388
- const val = valuation.status === "fulfilled" ? valuation.value?.[0] ?? {} : {};
31525
+ const cfgArr = get("config") ?? [];
31526
+ const cfg = cfgArr[0] ?? {};
31527
+ const valArr = get("valuation") ?? [];
31528
+ const val = valArr[0] ?? {};
31389
31529
  const overview = {
31530
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31390
31531
  totalEquity: totalEq,
31391
31532
  totalValue: val.totalBal ?? "N/A",
31392
31533
  valuationCurrency: val.ccy ?? "USD",
@@ -31405,12 +31546,7 @@ function registerAgentUtils(server, auth) {
31405
31546
  posMode: cfg.posMode,
31406
31547
  autoLoan: cfg.autoLoan
31407
31548
  },
31408
- errors: [
31409
- balance.status === "rejected" ? `\u4F59\u989D\u67E5\u8BE2\u5931\u8D25: ${balance.reason?.message}` : null,
31410
- positions.status === "rejected" ? `\u6301\u4ED3\u67E5\u8BE2\u5931\u8D25: ${positions.reason?.message}` : null,
31411
- config2.status === "rejected" ? `\u914D\u7F6E\u67E5\u8BE2\u5931\u8D25: ${config2.reason?.message}` : null,
31412
- valuation.status === "rejected" ? `\u4F30\u503C\u67E5\u8BE2\u5931\u8D25: ${valuation.reason?.message}` : null
31413
- ].filter(Boolean)
31549
+ errors
31414
31550
  };
31415
31551
  return toResult(overview);
31416
31552
  } catch (e) {
@@ -31427,36 +31563,31 @@ function registerAgentUtils(server, auth) {
31427
31563
  async ({ instId }) => {
31428
31564
  try {
31429
31565
  const isSwap = instId.toUpperCase().includes("-SWAP");
31430
- const isMargin = instId.toUpperCase().includes("MARGIN");
31431
- const fetchers = [
31432
- ["ticker", publicApi.getTicker(instId)],
31433
- ["orderbook", publicApi.getOrderbook(instId, 5)]
31434
- ];
31566
+ const fetchers = {
31567
+ ticker: publicApi.getTicker(instId),
31568
+ orderbook: publicApi.getOrderbook(instId, 5)
31569
+ };
31435
31570
  if (isSwap) {
31436
- fetchers.push(["fundingRate", publicApi.getFundingRate(instId)]);
31437
- }
31438
- const keys = fetchers.map((f) => f[0]);
31439
- const results = await Promise.allSettled(fetchers.map((f) => f[1]));
31440
- const tickerIdx = keys.indexOf("ticker");
31441
- const tickerRaw = results[tickerIdx]?.status === "fulfilled" ? results[tickerIdx].value : [];
31442
- const tk = tickerRaw.length > 0 ? tickerRaw[0] : {};
31443
- const obIdx = keys.indexOf("orderbook");
31444
- const obRaw = results[obIdx]?.status === "fulfilled" ? results[obIdx].value : null;
31445
- const obData = obRaw?.data?.[0] ?? obRaw;
31446
- const asks = (obData?.asks ?? []).slice(0, 5);
31447
- const bids = (obData?.bids ?? []).slice(0, 5);
31571
+ fetchers.fundingRate = publicApi.getFundingRate(instId);
31572
+ }
31573
+ const { get, errors } = await fetchAllSettled(fetchers);
31574
+ const tickerArr = get("ticker") ?? [];
31575
+ const tk = tickerArr[0] ?? {};
31576
+ const obRaw = get("orderbook");
31577
+ const { asks, bids } = extractOrderbook(obRaw);
31448
31578
  const spread = asks.length > 0 && bids.length > 0 ? (parseFloat(asks[0]?.[0] ?? "0") - parseFloat(bids[0]?.[0] ?? "0")).toFixed(2) : "N/A";
31449
31579
  let fundingRate = null;
31450
31580
  if (isSwap) {
31451
- const frIdx = keys.indexOf("fundingRate");
31452
- const frRaw = results[frIdx]?.status === "fulfilled" ? results[frIdx].value : [];
31453
- const fr = frRaw.length > 0 ? frRaw[0] : {};
31581
+ const frArr = get("fundingRate") ?? [];
31582
+ const fr = frArr[0] ?? {};
31454
31583
  fundingRate = {
31455
31584
  fundingRate: fr.fundingRate,
31456
31585
  nextFundingTime: fr.nextFundingTime ? new Date(parseInt(fr.nextFundingTime)).toISOString() : void 0
31457
31586
  };
31458
31587
  }
31588
+ const isMargin = instId.toUpperCase().includes("MARGIN");
31459
31589
  const summary = {
31590
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31460
31591
  instId,
31461
31592
  productType: isSwap ? "\u6C38\u7EED\u5408\u7EA6" : isMargin ? "\u6760\u6746" : "\u73B0\u8D27/\u5176\u4ED6",
31462
31593
  ticker: {
@@ -31470,11 +31601,11 @@ function registerAgentUtils(server, auth) {
31470
31601
  },
31471
31602
  depth: {
31472
31603
  spread,
31473
- top5Asks: asks.map((a) => ({ px: a[0], sz: a[1], orders: a[3] })),
31474
- top5Bids: bids.map((b) => ({ px: b[0], sz: b[1], orders: b[3] }))
31604
+ top5Asks: asks.slice(0, 5).map((a) => ({ px: a[0], sz: a[1], orders: a[3] })),
31605
+ top5Bids: bids.slice(0, 5).map((b) => ({ px: b[0], sz: b[1], orders: b[3] }))
31475
31606
  },
31476
31607
  fundingRate,
31477
- errors: results.map((r, i) => r.status === "rejected" ? `${keys[i]}\u5931\u8D25: ${r.reason?.message}` : null).filter(Boolean)
31608
+ errors
31478
31609
  };
31479
31610
  return toResult(summary);
31480
31611
  } catch (e) {
@@ -31496,28 +31627,30 @@ function registerAgentUtils(server, auth) {
31496
31627
  async ({ instId, tdMode, sz, px, side, ordType }) => {
31497
31628
  if (!auth) return toError(AUTH_REQUIRED);
31498
31629
  try {
31499
- const calls = [
31500
- ["maxSize", privateApi.getMaxSize(auth, instId, tdMode)],
31501
- ["priceLimit", publicApi.getPriceLimitBatch("", void 0, instId)],
31502
- ["convertCoin", publicApi.convertContractCoin(instId, sz, "coin", "open")],
31503
- ["ticker", publicApi.getTicker(instId)]
31504
- ];
31505
- const keys = calls.map((c) => c[0]);
31506
- const results = await Promise.allSettled(calls.map((c) => c[1]));
31507
- const get = (name) => {
31508
- const idx = keys.indexOf(name);
31509
- if (idx < 0) return null;
31510
- return results[idx].status === "fulfilled" ? results[idx].value : null;
31511
- };
31512
- const maxSizeData = get("maxSize");
31513
- const maxSz = maxSizeData?.[0];
31514
- const limitData = get("priceLimit");
31515
- const limit = limitData?.[0];
31516
- const convertData = get("convertCoin");
31517
- const converted = convertData?.[0];
31518
- const tickerData = get("ticker");
31519
- const tk = tickerData?.[0] ?? {};
31630
+ const szParsed = parseNumeric(sz, "sz(\u6570\u91CF)");
31631
+ if (!szParsed.ok) return toError(szParsed.error);
31632
+ if (szParsed.value <= 0) return toError(`\u6570\u91CF sz=${sz} \u5FC5\u987B\u5927\u4E8E 0\uFF0CAgent \u8BF7\u8C03\u6574\u540E\u91CD\u8BD5`);
31633
+ if (px !== void 0) {
31634
+ const pxParsed = parseNumeric(px, "px(\u4EF7\u683C)");
31635
+ if (!pxParsed.ok) return toError(pxParsed.error);
31636
+ if (pxParsed.value <= 0) return toError(`\u4EF7\u683C px=${px} \u5FC5\u987B\u5927\u4E8E 0\uFF0CAgent \u8BF7\u8C03\u6574\u540E\u91CD\u8BD5`);
31637
+ }
31638
+ const { get, errors } = await fetchAllSettled({
31639
+ maxSize: privateApi.getMaxSize(auth, instId, tdMode),
31640
+ priceLimit: publicApi.getPriceLimitBatch("", void 0, instId),
31641
+ convertCoin: publicApi.convertContractCoin(instId, sz, "coin", "open"),
31642
+ ticker: publicApi.getTicker(instId)
31643
+ });
31644
+ const maxSizeArr = get("maxSize") ?? [];
31645
+ const maxSz = maxSizeArr[0];
31646
+ const limitArr = get("priceLimit") ?? [];
31647
+ const limit = limitArr[0];
31648
+ const convertArr = get("convertCoin") ?? [];
31649
+ const converted = convertArr[0];
31650
+ const tickerArr = get("ticker") ?? [];
31651
+ const tk = tickerArr[0] ?? {};
31520
31652
  const checks = {
31653
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31521
31654
  instId,
31522
31655
  tdMode,
31523
31656
  userInput: { sz, px, side, ordType },
@@ -31534,7 +31667,7 @@ function registerAgentUtils(server, auth) {
31534
31667
  priceLimit: limit ? {
31535
31668
  highest: limit.highest,
31536
31669
  lowest: limit.lowest,
31537
- pxWithinLimit: px ? parseFloat(px) >= parseFloat(limit.lowest ?? "0") && parseFloat(px) <= parseFloat(limit.highest ?? "Infinity") : null
31670
+ pxWithinLimit: px ? szParsed.ok && parseFloat(px) >= parseFloat(limit.lowest ?? "0") && parseFloat(px) <= parseFloat(limit.highest ?? "Infinity") : null
31538
31671
  } : null,
31539
31672
  contractConversion: converted ? {
31540
31673
  fromCoin: sz,
@@ -31542,25 +31675,30 @@ function registerAgentUtils(server, auth) {
31542
31675
  unit: "contracts"
31543
31676
  } : null,
31544
31677
  warnings: [],
31545
- errors: results.map((r, i) => r.status === "rejected" ? `${keys[i]}\u67E5\u8BE2\u5931\u8D25: ${r.reason?.message}` : null).filter(Boolean)
31678
+ errors
31546
31679
  };
31547
- if (limit && px) {
31680
+ if (limit && px !== void 0) {
31548
31681
  const lowest = parseFloat(limit.lowest ?? "0");
31549
31682
  const highest = parseFloat(limit.highest ?? "Infinity");
31550
31683
  const price = parseFloat(px);
31551
- if (price < lowest) checks.warnings.push(`\u4EF7\u683C ${px} \u4F4E\u4E8E\u4E0B\u9650 ${limit.lowest}`);
31552
- if (price > highest) checks.warnings.push(`\u4EF7\u683C ${px} \u9AD8\u4E8E\u4E0A\u9650 ${limit.highest}`);
31684
+ if (!isNaN(price) && price < lowest)
31685
+ checks.warnings.push(`\u4EF7\u683C ${px} \u4F4E\u4E8E\u4E0B\u9650 ${limit.lowest}\uFF0CAgent \u8BF7\u964D\u4F4E\u4EF7\u683C\u81F3 \u2265${lowest}`);
31686
+ if (!isNaN(price) && price > highest)
31687
+ checks.warnings.push(`\u4EF7\u683C ${px} \u9AD8\u4E8E\u4E0A\u9650 ${limit.highest}\uFF0CAgent \u8BF7\u63D0\u9AD8\u4EF7\u683C\u81F3 \u2264${highest}`);
31553
31688
  }
31554
31689
  if (maxSz && side) {
31555
31690
  const maxBuy = parseFloat(maxSz.maxBuy ?? "0");
31556
31691
  const maxSell = parseFloat(maxSz.maxSell ?? "0");
31557
- const qty = parseFloat(sz);
31558
- if (side === "buy" && qty > maxBuy) checks.warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF\u4E70 ${maxSz.maxBuy}`);
31559
- if (side === "sell" && qty > maxSell) checks.warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF\u5356 ${maxSz.maxSell}`);
31692
+ const qty = szParsed.value;
31693
+ if (side === "buy" && qty > maxBuy)
31694
+ checks.warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF\u4E70 ${maxSz.maxBuy}\uFF0CAgent \u8BF7\u51CF\u81F3 \u2264${maxBuy}`);
31695
+ if (side === "sell" && qty > maxSell)
31696
+ checks.warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF\u5356 ${maxSz.maxSell}\uFF0CAgent \u8BF7\u51CF\u81F3 \u2264${maxSell}`);
31560
31697
  }
31561
31698
  if (converted) {
31562
31699
  const contractSz = converted.sz;
31563
- if (parseFloat(contractSz ?? "0") < 1) checks.warnings.push(`\u6362\u7B97\u540E\u5F20\u6570 ${contractSz} < 1\uFF0C\u53EF\u80FD\u65E0\u6CD5\u4E0B\u5355`);
31700
+ if (parseFloat(contractSz ?? "0") < 1)
31701
+ checks.warnings.push(`\u6362\u7B97\u540E\u5F20\u6570 ${contractSz} < 1\uFF0C\u53EF\u80FD\u65E0\u6CD5\u4E0B\u5355\uFF0CAgent \u8BF7\u589E\u52A0\u6570\u91CF`);
31564
31702
  }
31565
31703
  checks.passed = checks.warnings.length === 0 && checks.errors.length === 0;
31566
31704
  return toResult(checks);
@@ -31606,14 +31744,15 @@ function registerAgentUtils(server, auth) {
31606
31744
  async () => {
31607
31745
  if (!auth) return toError(AUTH_REQUIRED);
31608
31746
  try {
31609
- const [balance, positions, config2] = await Promise.allSettled([
31610
- privateApi.getBalance(auth),
31611
- privateApi.getPositions(auth),
31612
- privateApi.getAccountConfig(auth)
31613
- ]);
31614
- const balOk = balance.status === "fulfilled" ? balance.value : [];
31615
- const posOk = positions.status === "fulfilled" ? positions.value : [];
31616
- const cfgOk = config2.status === "fulfilled" ? config2.value?.[0] ?? {} : {};
31747
+ const { get, errors } = await fetchAllSettled({
31748
+ balance: privateApi.getBalance(auth),
31749
+ positions: privateApi.getPositions(auth),
31750
+ config: privateApi.getAccountConfig(auth)
31751
+ });
31752
+ const balOk = get("balance") ?? [];
31753
+ const posOk = get("positions") ?? [];
31754
+ const cfgArr = get("config") ?? [];
31755
+ const cfgOk = cfgArr[0] ?? {};
31617
31756
  const totalEq = balOk.length > 0 ? parseFloat(balOk[0]?.totalEq ?? "0") : 0;
31618
31757
  const details = balOk.length > 0 ? balOk[0]?.details ?? [] : [];
31619
31758
  const activePos = posOk.filter((p) => parseFloat(p.pos || "0") !== 0);
@@ -31628,6 +31767,7 @@ function registerAgentUtils(server, auth) {
31628
31767
  if (liqPx > 0 && markPx > 0) {
31629
31768
  liqDistance = p.posSide === "long" ? (markPx - liqPx) / markPx * 100 : (liqPx - markPx) / markPx * 100;
31630
31769
  }
31770
+ const mgnMode = p.mgnMode || "unknown";
31631
31771
  return {
31632
31772
  instId: p.instId,
31633
31773
  posSide: p.posSide,
@@ -31644,12 +31784,13 @@ function registerAgentUtils(server, auth) {
31644
31784
  // 附操作上下文,让 Agent 不再二次查
31645
31785
  instFamily: p.instFamily,
31646
31786
  instType: p.instType,
31647
- mgnMode: p.mgnMode || (cfgOk.posMode === "long_short_mode" ? "cross" : "isolated")
31787
+ mgnMode
31648
31788
  };
31649
31789
  }).sort((a, b) => parseFloat(a.mgnRatio) - parseFloat(b.mgnRatio));
31650
31790
  const totalMargin = posRisks.reduce((s, p) => s + parseFloat(p.margin), 0);
31651
31791
  const dangerCount = posRisks.filter((p) => p.riskLevel === "\u{1F534} \u5371\u9669").length;
31652
31792
  const warnCount = posRisks.filter((p) => p.riskLevel === "\u{1F7E1} \u8B66\u544A").length;
31793
+ const unknownMgnCount = posRisks.filter((p) => p.mgnMode === "unknown").length;
31653
31794
  const totalMgnRatio = totalEq > 0 ? (totalMargin / totalEq * 100).toFixed(1) : "N/A";
31654
31795
  const usdtDetail = details.find((d) => d.ccy === "USDT");
31655
31796
  const availBalance = usdtDetail ? parseFloat(usdtDetail.availBal ?? "0") : 0;
@@ -31672,11 +31813,9 @@ function registerAgentUtils(server, auth) {
31672
31813
  acctLv: cfgOk.acctLv,
31673
31814
  autoLoan: cfgOk.autoLoan
31674
31815
  },
31675
- errors: [
31676
- balance.status === "rejected" ? `\u4F59\u989D: ${balance.reason?.message}` : null,
31677
- positions.status === "rejected" ? `\u6301\u4ED3: ${positions.reason?.message}` : null,
31678
- config2.status === "rejected" ? `\u914D\u7F6E: ${config2.reason?.message}` : null
31679
- ].filter(Boolean)
31816
+ errors,
31817
+ _notes: unknownMgnCount > 0 ? `\u26A0\uFE0F ${unknownMgnCount} \u4E2A\u4ED3\u4F4D\u672A\u8FD4\u56DE mgnMode\uFF0C\u5E73\u4ED3\u65F6 Agent \u9700\u5355\u72EC\u786E\u8BA4\u4FDD\u8BC1\u91D1\u6A21\u5F0F` : void 0,
31818
+ _summary: `\u5F53\u524D${posRisks.length}\u4E2A\u6301\u4ED3\uFF0C\u4FDD\u8BC1\u91D1${totalMargin.toFixed(2)} USD\uFF0C\u603B\u6743\u76CA${totalEq.toFixed(2)} USD\u3002${dangerCount > 0 ? `\u{1F534} ${dangerCount}\u4E2A\u4ED3\u4F4D\u63A5\u8FD1\u5F3A\u5E73\uFF01` : warnCount > 0 ? `\u26A1 ${warnCount}\u4E2A\u9700\u5173\u6CE8` : posRisks.length > 0 ? `\u2705 \u6240\u6709\u4ED3\u4F4D\u5B89\u5168` : `\u65E0\u6301\u4ED3`}`
31680
31819
  };
31681
31820
  return toResult(overview);
31682
31821
  } catch (e) {
@@ -31698,47 +31837,57 @@ function registerAgentUtils(server, auth) {
31698
31837
  async ({ instId, side, sz, tdMode, px, ordType }) => {
31699
31838
  if (!auth) return toError(AUTH_REQUIRED);
31700
31839
  try {
31701
- const feeCcy = tdMode === "cash" ? instId.split("-")[1] || "USDT" : "USDT";
31702
- const calls = [
31703
- ["balance", privateApi.getBalance(auth)],
31704
- ["maxSize", privateApi.getMaxSize(auth, instId, tdMode)],
31705
- ["feeRates", privateApi.getFeeRates(auth, instId.split("-")[1] === "USDT" ? "SPOT" : "SWAP", instId)],
31706
- ["ticker", publicApi.getTicker(instId)],
31707
- ["priceLimit", publicApi.getPriceLimitBatch("", void 0, instId)]
31708
- ];
31709
- const keys = calls.map((c) => c[0]);
31710
- const results = await Promise.allSettled(calls.map((c) => c[1]));
31711
- const get = (name) => {
31712
- const idx = keys.indexOf(name);
31713
- if (idx < 0) return null;
31714
- return results[idx].status === "fulfilled" ? results[idx].value : null;
31715
- };
31716
- const balData = get("balance");
31717
- const details = balData?.[0]?.details ?? [];
31718
- const availBal = parseFloat(details.find((d) => d.ccy === feeCcy)?.availBal ?? "0");
31719
- const maxData = get("maxSize")?.[0];
31840
+ const szParsed = parseNumeric(sz, "sz(\u6570\u91CF)");
31841
+ if (!szParsed.ok) return toError(szParsed.error);
31842
+ if (szParsed.value <= 0) return toError(`\u6570\u91CF sz=${sz} \u5FC5\u987B\u5927\u4E8E 0\uFF0CAgent \u8BF7\u8C03\u6574\u540E\u91CD\u8BD5`);
31843
+ if (px !== void 0) {
31844
+ const pxParsed = parseNumeric(px, "px(\u4EF7\u683C)");
31845
+ if (!pxParsed.ok) return toError(pxParsed.error);
31846
+ if (pxParsed.value <= 0) return toError(`\u4EF7\u683C px=${px} \u5FC5\u987B\u5927\u4E8E 0\uFF0CAgent \u8BF7\u8C03\u6574\u540E\u91CD\u8BD5`);
31847
+ }
31848
+ const instType = inferInstType(instId);
31849
+ const feeCcy = inferFeeCcy(instId, tdMode);
31850
+ const { get, errors: precheckErrors } = await fetchAllSettled({
31851
+ balance: privateApi.getBalance(auth),
31852
+ maxSize: privateApi.getMaxSize(auth, instId, tdMode),
31853
+ feeRates: privateApi.getFeeRates(auth, instType, instId),
31854
+ ticker: publicApi.getTicker(instId),
31855
+ priceLimit: publicApi.getPriceLimitBatch("", void 0, instId)
31856
+ });
31857
+ const balArr = get("balance") ?? [];
31858
+ const balDetails = balArr[0]?.details ?? [];
31859
+ const availBal = parseFloat(balDetails.find((d) => d.ccy === feeCcy)?.availBal ?? "0");
31860
+ const maxArr = get("maxSize") ?? [];
31861
+ const maxData = maxArr[0];
31720
31862
  const maxBuy = parseFloat(maxData?.maxBuy ?? "0");
31721
31863
  const maxSell = parseFloat(maxData?.maxSell ?? "0");
31722
31864
  const maxSz = side === "buy" ? maxBuy : maxSell;
31723
- const feeData = get("feeRates")?.[0];
31865
+ const feeArr = get("feeRates") ?? [];
31866
+ const feeData = feeArr[0] ?? {};
31724
31867
  const makerFee = parseFloat(feeData?.maker ?? "0");
31725
31868
  const takerFee = parseFloat(feeData?.taker ?? "0");
31726
31869
  const feeRate = ordType === "market" ? takerFee : makerFee;
31727
- const tkData = get("ticker")?.[0] ?? {};
31870
+ const tkArr = get("ticker") ?? [];
31871
+ const tkData = tkArr[0] ?? {};
31728
31872
  const lastPx = parseFloat(tkData?.last ?? "0");
31729
31873
  const orderPx = px ? parseFloat(px) : lastPx;
31730
- const estCost = parseFloat(sz) * orderPx;
31874
+ const estCost = szParsed.value * orderPx;
31731
31875
  const estFee = estCost * feeRate;
31732
- const limitData = get("priceLimit")?.[0];
31876
+ const limitArr = get("priceLimit") ?? [];
31877
+ const limitData = limitArr[0];
31733
31878
  const lowest = parseFloat(limitData?.lowest ?? "0");
31734
31879
  const highest = parseFloat(limitData?.highest ?? "999999");
31735
31880
  const warnings = [];
31736
- const qty = parseFloat(sz);
31737
- if (qty > maxSz) warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF${side === "buy" ? "\u4E70" : "\u5356"} ${maxSz}`);
31738
- if (availBal < estCost + estFee) warnings.push(`\u4F59\u989D\u4E0D\u8DB3\uFF1A\u9700\u8981 $${(estCost + estFee).toFixed(2)}\uFF0C\u53EF\u7528 $${availBal.toFixed(2)}`);
31739
- if (orderPx < lowest || orderPx > highest) warnings.push(`\u4EF7\u683C ${orderPx} \u8D85\u51FA\u9650\u4EF7\u8303\u56F4 [${lowest}, ${highest}]`);
31740
- if (warnings.length > 0) {
31881
+ const qty = szParsed.value;
31882
+ if (maxSz > 0 && qty > maxSz)
31883
+ warnings.push(`\u6570\u91CF ${sz} \u8D85\u8FC7\u6700\u5927\u53EF${side === "buy" ? "\u4E70" : "\u5356"} ${maxSz}\uFF0CAgent \u8BF7\u51CF\u81F3 \u2264${maxSz}`);
31884
+ if (availBal > 0 && availBal < estCost + estFee)
31885
+ warnings.push(`\u4F59\u989D\u4E0D\u8DB3\uFF1A\u9700\u8981 $${(estCost + estFee).toFixed(2)}\uFF0C\u53EF\u7528 $${availBal.toFixed(2)}\uFF0CAgent \u8BF7\u51CF\u5C11\u6570\u91CF\u6216\u5145\u503C`);
31886
+ if (orderPx > 0 && (orderPx < lowest || orderPx > highest))
31887
+ warnings.push(`\u4EF7\u683C ${orderPx} \u8D85\u51FA\u9650\u4EF7\u8303\u56F4 [${lowest}, ${highest}]\uFF0CAgent \u8BF7\u8C03\u6574\u4EF7\u683C`);
31888
+ if (warnings.length > 0 || precheckErrors.length > 0) {
31741
31889
  return toResult({
31890
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31742
31891
  executed: false,
31743
31892
  precheck: {
31744
31893
  balance: availBal.toFixed(2),
@@ -31748,9 +31897,8 @@ function registerAgentUtils(server, auth) {
31748
31897
  estFee: estFee.toFixed(4),
31749
31898
  priceCheck: { orderPx, lowest, highest, within: orderPx >= lowest && orderPx <= highest }
31750
31899
  },
31751
- warnings,
31752
- tip: "\u9884\u68C0\u672A\u901A\u8FC7\uFF0C\u8BF7\u8C03\u6574\u53C2\u6570\u540E\u91CD\u8BD5",
31753
- tsIso: (/* @__PURE__ */ new Date()).toISOString()
31900
+ warnings: [...warnings, ...precheckErrors],
31901
+ tip: "\u9884\u68C0\u672A\u901A\u8FC7\uFF0C\u8BF7 Agent \u6839\u636E\u4EE5\u4E0A warnings \u8C03\u6574\u53C2\u6570\u540E\u91CD\u8BD5"
31754
31902
  });
31755
31903
  }
31756
31904
  const body = { instId, side, sz, tdMode };
@@ -31758,6 +31906,7 @@ function registerAgentUtils(server, auth) {
31758
31906
  if (px) body.px = px;
31759
31907
  const orderResult = await privateApi.placeOrder(auth, body);
31760
31908
  return toResult({
31909
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31761
31910
  executed: true,
31762
31911
  order: orderResult?.[0] ?? orderResult,
31763
31912
  precheck: {
@@ -31768,10 +31917,10 @@ function registerAgentUtils(server, auth) {
31768
31917
  estFee: estFee.toFixed(4)
31769
31918
  },
31770
31919
  risk: {
31771
- marginUsed: (estCost / availBal * 100).toFixed(1) + "%",
31772
- warning: estCost / availBal > 0.3 ? "\u6B64\u5355\u4F7F\u7528\u8D85\u8FC730%\u53EF\u7528\u4F59\u989D" : null
31920
+ marginUsed: availBal > 0 ? (estCost / availBal * 100).toFixed(1) + "%" : "N/A",
31921
+ warning: availBal > 0 && estCost / availBal > 0.3 ? "\u6B64\u5355\u4F7F\u7528\u8D85\u8FC730%\u53EF\u7528\u4F59\u989D" : null
31773
31922
  },
31774
- tsIso: (/* @__PURE__ */ new Date()).toISOString()
31923
+ _summary: `${side === "buy" ? "\u4E70\u5165" : "\u5356\u51FA"} ${sz} ${instId} \u5DF2\u6210\u4EA4\uFF0C\u9884\u4F30\u6210\u672C $${(estCost + estFee).toFixed(2)}\uFF0C\u624B\u7EED\u8D39 $${estFee.toFixed(4)}\u3002\u4FDD\u8BC1\u91D1\u5360\u7528 ${availBal > 0 ? (estCost / availBal * 100).toFixed(1) + "%" : "N/A"}`
31775
31924
  });
31776
31925
  } catch (e) {
31777
31926
  return toError(e);
@@ -31800,40 +31949,61 @@ function registerAgentUtils(server, auth) {
31800
31949
  high24h: parseFloat(t.high24h ?? "0"),
31801
31950
  low24h: parseFloat(t.low24h ?? "0"),
31802
31951
  bid: parseFloat(t.bidPx ?? "0"),
31803
- ask: parseFloat(t.askPx ?? "0")
31952
+ ask: parseFloat(t.askPx ?? "0"),
31953
+ fundingRate: 0
31954
+ // 稍后填充
31804
31955
  })).filter((t) => t.last > 0 && t.vol24h > 0);
31805
- if (sb === "vol") arr.sort((a, b) => b.vol24h - a.vol24h);
31806
- else if (sb === "change") arr.sort((a, b) => Math.abs(b.change24h) - Math.abs(a.change24h));
31807
- else arr.sort((a, b) => Math.abs(b.change24h) - Math.abs(a.change24h));
31956
+ if (sb === "vol") {
31957
+ arr.sort((a, b) => b.vol24h - a.vol24h);
31958
+ } else if (sb === "change") {
31959
+ arr.sort((a, b) => Math.abs(b.change24h) - Math.abs(a.change24h));
31960
+ } else if (sb === "fundingRate") {
31961
+ const candidates = [...arr].sort((a, b) => Math.abs(b.change24h) - Math.abs(a.change24h)).slice(0, 20);
31962
+ const frFetchers = {};
31963
+ for (const c of candidates) {
31964
+ frFetchers[c.instId] = publicApi.getFundingRate(c.instId);
31965
+ }
31966
+ const { get: frGet } = await fetchAllSettled(frFetchers);
31967
+ for (const c of candidates) {
31968
+ const frArr = frGet(c.instId) ?? [];
31969
+ c.fundingRate = parseFloat(frArr[0]?.fundingRate ?? "0");
31970
+ }
31971
+ const frMap = new Map(candidates.map((c) => [c.instId, c.fundingRate]));
31972
+ arr.forEach((t) => {
31973
+ t.fundingRate = frMap.get(t.instId) ?? 0;
31974
+ });
31975
+ arr.sort((a, b) => Math.abs(b.fundingRate) - Math.abs(a.fundingRate));
31976
+ }
31808
31977
  const top = arr.slice(0, n);
31809
31978
  const gainers = top.filter((t) => t.change24h > 0).sort((a, b) => b.change24h - a.change24h);
31810
31979
  const losers = top.filter((t) => t.change24h < 0).sort((a, b) => a.change24h - b.change24h);
31811
- const volumeLeader = arr.slice(0, n).sort((a, b) => b.vol24h - a.vol24h).slice(0, 5);
31980
+ const volumeLeader = [...arr].sort((a, b) => b.vol24h - a.vol24h).slice(0, 5);
31812
31981
  let fundingAlerts = [];
31813
31982
  if (it === "SWAP") {
31814
- try {
31815
- const fr = {};
31816
- for (const t of top.slice(0, 10)) {
31817
- try {
31818
- const frData = await publicApi.getFundingRate(t.instId);
31819
- fr[t.instId] = parseFloat(frData?.[0]?.fundingRate ?? "0");
31820
- } catch {
31821
- fr[t.instId] = 0;
31822
- }
31983
+ const toFetch = top.filter((t) => t.fundingRate === 0);
31984
+ if (toFetch.length > 0) {
31985
+ const frFetchers = {};
31986
+ for (const t of toFetch) {
31987
+ frFetchers[t.instId] = publicApi.getFundingRate(t.instId);
31988
+ }
31989
+ const { get: frGet } = await fetchAllSettled(frFetchers);
31990
+ for (const t of toFetch) {
31991
+ const frArr = frGet(t.instId) ?? [];
31992
+ t.fundingRate = parseFloat(frArr[0]?.fundingRate ?? "0");
31823
31993
  }
31824
- fundingAlerts = Object.entries(fr).filter(([, r]) => Math.abs(r) > 1e-3).map(([instId, rate]) => ({ instId, fundingRate: (rate * 100).toFixed(4) + "%" }));
31825
- } catch {
31826
31994
  }
31995
+ fundingAlerts = top.filter((t) => Math.abs(t.fundingRate) > 1e-3).map((t) => ({ instId: t.instId, fundingRate: (t.fundingRate * 100).toFixed(4) + "%" }));
31827
31996
  }
31828
31997
  return toResult({
31829
- scanType: sb === "change" ? "\u6DA8\u8DCC\u5E45\u5F02\u52A8" : sb === "vol" ? "\u6210\u4EA4\u91CF\u6392\u884C" : "\u7EFC\u5408\u626B\u63CF",
31998
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31999
+ scanType: sb === "fundingRate" ? "\u8D44\u91D1\u8D39\u7387\u6392\u884C" : sb === "vol" ? "\u6210\u4EA4\u91CF\u6392\u884C" : "\u6DA8\u8DCC\u5E45\u5F02\u52A8",
31830
32000
  instType: it,
31831
32001
  top: top.slice(0, n),
31832
32002
  gainers: gainers.slice(0, 5),
31833
32003
  losers: losers.slice(0, 5),
31834
32004
  volumeLeader,
31835
32005
  fundingAlerts,
31836
- updateTime: (/* @__PURE__ */ new Date()).toISOString(),
32006
+ _summary: `\u5171\u626B\u63CF${arr.length}\u4E2A${it}\u54C1\u79CD\u3002${sb === "fundingRate" ? `\u8D44\u91D1\u8D39\u7387\u524D${n}: ${top.slice(0, 3).map((t) => `${t.instId}(${(t.fundingRate * 100).toFixed(3)}%)`).join("\u3001")}` : `\u6DA8\u5E45\u524D5: ${gainers.slice(0, 3).map((g) => g.instId).join("\u3001") || "\u65E0"}\u3002\u8DCC\u5E45\u524D5: ${losers.slice(0, 3).map((l) => l.instId).join("\u3001") || "\u65E0"}`}\u3002${fundingAlerts.length > 0 ? `\u26A0\uFE0F ${fundingAlerts.length}\u4E2A\u54C1\u79CD\u8D44\u91D1\u8D39\u7387\u5F02\u5E38\u3002` : ""}`,
31837
32007
  tip: "\u626B\u63CF\u7ED3\u679C\u4E3A\u5FEB\u7167\u3002\u5177\u4F53\u54C1\u79CD\u7528 okx_quick_market \u6DF1\u5165\u5206\u6790\u3002"
31838
32008
  });
31839
32009
  } catch (e) {
@@ -31851,14 +32021,15 @@ function registerAgentUtils(server, auth) {
31851
32021
  if (!auth) return toError(AUTH_REQUIRED);
31852
32022
  try {
31853
32023
  const d = days || 7;
31854
- const [balance, positions, fills] = await Promise.allSettled([
31855
- privateApi.getBalance(auth),
31856
- privateApi.getPositions(auth),
31857
- privateApi.getFillsHistory(auth, void 0, void 0, Math.min(d * 10, 100))
31858
- ]);
31859
- const balOk = balance.status === "fulfilled" ? balance.value?.[0] ?? {} : {};
31860
- const posOk = positions.status === "fulfilled" ? positions.value : [];
31861
- const fillsOk = fills.status === "fulfilled" ? fills.value : [];
32024
+ const { get, errors } = await fetchAllSettled({
32025
+ balance: privateApi.getBalance(auth),
32026
+ positions: privateApi.getPositions(auth),
32027
+ fills: privateApi.getFillsHistory(auth, void 0, void 0, Math.min(d * 10, 100))
32028
+ });
32029
+ const balOk = get("balance") ?? [];
32030
+ const balFirst = balOk[0] ?? {};
32031
+ const posOk = get("positions") ?? [];
32032
+ const fillsOk = get("fills") ?? [];
31862
32033
  const totalUpl = posOk.reduce((s, p) => s + parseFloat(p.upl ?? "0"), 0).toFixed(2);
31863
32034
  const activePos = posOk.filter((p) => parseFloat(p.pos || "0") !== 0);
31864
32035
  const posPnL = activePos.map((p) => ({
@@ -31885,7 +32056,7 @@ function registerAgentUtils(server, auth) {
31885
32056
  dailyPnL[day] = (dailyPnL[day] || 0) + pnl;
31886
32057
  totalRealized += pnl;
31887
32058
  }
31888
- const totalEq = parseFloat(balOk.totalEq ?? "0");
32059
+ const totalEq = parseFloat(balFirst.totalEq ?? "0");
31889
32060
  return toResult({
31890
32061
  tsIso: (/* @__PURE__ */ new Date()).toISOString(),
31891
32062
  period: `\u8FD1${d}\u65E5`,
@@ -31901,11 +32072,1588 @@ function registerAgentUtils(server, auth) {
31901
32072
  positions: posPnL,
31902
32073
  positionCount: activePos.length,
31903
32074
  tradesAnalyzed: recentFills.length,
31904
- errors: [
31905
- balance.status === "rejected" ? `\u4F59\u989D: ${balance.reason?.message}` : null,
31906
- positions.status === "rejected" ? `\u6301\u4ED3: ${positions.reason?.message}` : null,
31907
- fills.status === "rejected" ? `\u6210\u4EA4: ${fills.reason?.message}` : null
31908
- ].filter(Boolean)
32075
+ errors,
32076
+ _summary: `\u8FD1${d}\u65E5\u6D6E\u52A8\u76C8\u4E8F $${totalUpl}\uFF08${totalEq > 0 ? (parseFloat(totalUpl) / totalEq * 100).toFixed(2) : "N/A"}%\uFF09\uFF0C\u5DF2\u5B9E\u73B0\u76C8\u4E8F $${totalRealized.toFixed(2)}\u3002${activePos.length}\u4E2A\u6301\u4ED3\uFF0C\u5206\u6790${recentFills.length}\u7B14\u6210\u4EA4\u3002`
32077
+ });
32078
+ } catch (e) {
32079
+ return toError(e);
32080
+ }
32081
+ }
32082
+ );
32083
+ server.tool(
32084
+ "agent_simulate_order",
32085
+ 'CAT:[\u7CFB\u7EDF] | ## \u529F\u80FD\uFF1A\u6A21\u62DF\u4E0B\u5355\u2014\u2014\u4E0D\u4EA7\u751F\u771F\u5B9E\u8BA2\u5355\uFF0C\u8FD4\u56DE\u9884\u4F30\u6210\u4EA4\u4EF7\u3001\u6ED1\u70B9\u3001\u624B\u7EED\u8D39\u3001\u8D44\u91D1\u5360\u7528\n## \u573A\u666F\uFF1AAgent \u56DE\u7B54"\u5982\u679C\u6211\u73B0\u5728\u4E70\u51650.1 BTC\u4F1A\u600E\u6837"\u65F6\u4F7F\u7528\uFF0C\u8BA9\u7528\u6237\u5728\u4E0D\u5192\u98CE\u9669\u7684\u60C5\u51B5\u4E0B\u4E86\u89E3\u4EA4\u6613\u6210\u672C\n## \u5173\u952E\u8BCD\uFF1A\u6A21\u62DF\u4EA4\u6613, \u6C99\u76D2, simulate, \u9884\u4F30, \u6ED1\u70B9, \u624B\u7EED\u8D39, \u8D44\u91D1\u9884\u4F30\n## \u53C2\u6570\uFF1A\n## - instId: \u4EA7\u54C1ID\n## - side: buy=\u4E70\u5165, sell=\u5356\u51FA\n## - sz: \u4E0B\u5355\u6570\u91CF\n## - tdMode: \u4EA4\u6613\u6A21\u5F0F\n## - px: \u9650\u4EF7\uFF08\u9009\u586B\uFF0C\u7528\u4E8E\u8BA1\u7B97\u9650\u4EF7\u5355\u9884\u4F30\uFF09\n## \u9274\u6743\uFF1A\u26A0\uFE0F \u9700\u8981 API Key\uFF08\u53EA\u8BFB\uFF0C\u4E0D\u4EA7\u751F\u8BA2\u5355\uFF09\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u67E5\u8BE2+\u8BA1\u7B97\uFF0C\u4E0D\u4EA7\u751F\u771F\u5B9E\u8BA2\u5355\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~1KB\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u6A21\u62DF \u2192 \u7528\u6237\u786E\u8BA4 \u2192 agent_quick_trade \u771F\u5B9E\u4E0B\u5355',
32086
+ {
32087
+ instId: external_exports.string().describe("\u4EA7\u54C1ID\uFF0C\u5982 BTC-USDT-SWAP"),
32088
+ side: external_exports.enum(["buy", "sell"]).describe("\u4E70\u5356\u65B9\u5411"),
32089
+ sz: external_exports.string().describe("\u4E0B\u5355\u6570\u91CF"),
32090
+ tdMode: external_exports.enum(["cash", "cross", "isolated"]).describe("\u4EA4\u6613\u6A21\u5F0F"),
32091
+ px: external_exports.string().optional().describe("\u9650\u4EF7\uFF08\u9009\u586B\uFF0C\u7528\u4E8E\u8BA1\u7B97\u9650\u4EF7\u5355\u9884\u4F30\uFF09")
32092
+ },
32093
+ async ({ instId, side, sz, tdMode, px }) => {
32094
+ if (!auth) return toError(AUTH_REQUIRED);
32095
+ try {
32096
+ const szParsed = parseNumeric(sz, "sz(\u6570\u91CF)");
32097
+ if (!szParsed.ok) return toError(szParsed.error);
32098
+ if (szParsed.value <= 0) return toError(`\u6570\u91CF sz=${sz} \u5FC5\u987B\u5927\u4E8E 0\uFF0CAgent \u8BF7\u8C03\u6574\u540E\u91CD\u8BD5`);
32099
+ if (px !== void 0) {
32100
+ const pxParsed = parseNumeric(px, "px(\u4EF7\u683C)");
32101
+ if (!pxParsed.ok) return toError(pxParsed.error);
32102
+ }
32103
+ const qty = szParsed.value;
32104
+ const instType = inferInstType(instId);
32105
+ const { get, errors } = await fetchAllSettled({
32106
+ ticker: publicApi.getTicker(instId),
32107
+ orderbook: publicApi.getOrderbook(instId, 10),
32108
+ feeRates: privateApi.getFeeRates(auth, instType, instId),
32109
+ priceLimit: publicApi.getPriceLimitBatch("", void 0, instId)
32110
+ });
32111
+ const tkArr = get("ticker") ?? [];
32112
+ const tk = tkArr[0] ?? {};
32113
+ const lastPx = parseFloat(tk?.last ?? "0");
32114
+ const bidPx = parseFloat(tk?.bidPx ?? "0");
32115
+ const askPx = parseFloat(tk?.askPx ?? "0");
32116
+ const markPx = lastPx || bidPx || askPx;
32117
+ const obRaw = get("orderbook");
32118
+ const { asks, bids } = extractOrderbook(obRaw);
32119
+ let slippage = 0;
32120
+ if (side === "buy" && asks.length > 0) {
32121
+ let remaining = qty;
32122
+ let cost = 0;
32123
+ for (const a of asks) {
32124
+ const px_a = parseFloat(a[0] || "0");
32125
+ const sz_a = parseFloat(a[1] || "0");
32126
+ const fill = Math.min(remaining, sz_a);
32127
+ cost += fill * px_a;
32128
+ remaining -= fill;
32129
+ if (remaining <= 0) break;
32130
+ }
32131
+ const avgPx = cost / qty;
32132
+ slippage = markPx > 0 ? (avgPx - markPx) / markPx * 100 : 0;
32133
+ } else if (side === "sell" && bids.length > 0) {
32134
+ let remaining = qty;
32135
+ let revenue = 0;
32136
+ for (const b of bids) {
32137
+ const px_b = parseFloat(b[0] || "0");
32138
+ const sz_b = parseFloat(b[1] || "0");
32139
+ const fill = Math.min(remaining, sz_b);
32140
+ revenue += fill * px_b;
32141
+ remaining -= fill;
32142
+ if (remaining <= 0) break;
32143
+ }
32144
+ const avgPx = revenue / qty;
32145
+ slippage = markPx > 0 ? (markPx - avgPx) / markPx * 100 : 0;
32146
+ }
32147
+ const feeArr = get("feeRates") ?? [];
32148
+ const feeData = feeArr[0] ?? {};
32149
+ const takerFee = parseFloat(feeData?.taker ?? "0");
32150
+ const feeRate = takerFee;
32151
+ const limitArr = get("priceLimit") ?? [];
32152
+ const limitData = limitArr[0];
32153
+ const lowest = parseFloat(limitData?.lowest ?? "0");
32154
+ const highest = parseFloat(limitData?.highest ?? "999999");
32155
+ const orderPx = px ? parseFloat(px) : side === "buy" ? askPx : bidPx;
32156
+ const estCost = qty * orderPx;
32157
+ const estFee = estCost * feeRate;
32158
+ const pxWithinLimit = orderPx >= lowest && orderPx <= highest;
32159
+ return toResult({
32160
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
32161
+ simulated: true,
32162
+ instId,
32163
+ side,
32164
+ sz,
32165
+ tdMode,
32166
+ orderPx: orderPx.toFixed(4),
32167
+ currentPrice: { last: lastPx, bid: bidPx, ask: askPx },
32168
+ estimate: {
32169
+ estCost: estCost.toFixed(2) + " USD",
32170
+ estFee: estFee.toFixed(4) + " USD",
32171
+ estTotal: (estCost + estFee).toFixed(2) + " USD",
32172
+ feeRate: (feeRate * 100).toFixed(4) + "%",
32173
+ slippage: slippage.toFixed(4) + "%"
32174
+ },
32175
+ priceCheck: {
32176
+ withinLimit: pxWithinLimit,
32177
+ range: `${lowest} ~ ${highest}`
32178
+ },
32179
+ errors,
32180
+ _summary: `\u6A21\u62DF${side === "buy" ? "\u4E70\u5165" : "\u5356\u51FA"} ${sz} ${instId}\uFF1A\u9884\u4F30\u6210\u4EA4\u4EF7 $${orderPx.toFixed(2)}\uFF0C\u624B\u7EED\u8D39 $${estFee.toFixed(4)}\uFF0C\u5408\u8BA1 $${(estCost + estFee).toFixed(2)}\uFF0C\u6ED1\u70B9 ${slippage.toFixed(2)}%\u3002${pxWithinLimit ? "\u4EF7\u683C\u5728\u9650\u4EF7\u8303\u56F4\u5185\u3002" : "\u26A0\uFE0F \u4EF7\u683C\u8D85\u51FA\u9650\u4EF7\u8303\u56F4\uFF0CAgent \u8BF7\u8C03\u6574\u3002"}`,
32181
+ tip: "\u4EE5\u4E0A\u4E3A\u6A21\u62DF\u7ED3\u679C\uFF0C\u4E0D\u4EA7\u751F\u771F\u5B9E\u8BA2\u5355\u3002\u786E\u8BA4\u540E\u53EF\u7528 agent_quick_trade \u771F\u5B9E\u4E0B\u5355\u3002"
32182
+ });
32183
+ } catch (e) {
32184
+ return toError(e);
32185
+ }
32186
+ }
32187
+ );
32188
+ server.tool(
32189
+ "agent_get_preference",
32190
+ 'CAT:[\u7CFB\u7EDF] | ## \u529F\u80FD\uFF1A\u8BFB\u53D6 Agent \u6301\u4E45\u5316\u504F\u597D\uFF0C\u8DE8\u4F1A\u8BDD\u4FDD\u7559\n## \u573A\u666F\uFF1AAgent \u5728\u65B0\u4F1A\u8BDD\u4E2D\u6062\u590D\u7528\u6237\u504F\u597D\uFF08\u5982"\u53EA\u505A\u73B0\u8D27""\u9ED8\u8BA4\u4EA4\u6613\u5BF9BTC-USDT""\u98CE\u9669\u504F\u597D\u4F4E"\uFF09\n## \u5173\u952E\u8BCD\uFF1A\u504F\u597D, preference, \u8BB0\u5FC6, \u6301\u4E45\u5316, \u7528\u6237\u753B\u50CF\n## \u53C2\u6570\uFF1A\n## - key: \u504F\u597D\u952E\u540D\u3002\u4E0D\u586B\u8FD4\u56DE\u5168\u90E8\u504F\u597D\n## \u9274\u6743\uFF1APUBLIC \u2014 \u672C\u5730\u8BFB\u53D6\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~300B\n## \u5173\u8054\uFF1Aagent_set_preference \u8BBE\u7F6E\u504F\u597D \u2192 \u672C\u5DE5\u5177 \u2192 Agent \u6839\u636E\u504F\u597D\u8C03\u6574\u7B56\u7565',
32191
+ {
32192
+ key: external_exports.string().optional().describe("\u504F\u597D\u952E\u540D\uFF0C\u4E0D\u586B\u8FD4\u56DE\u5168\u90E8")
32193
+ },
32194
+ async ({ key }) => {
32195
+ try {
32196
+ const prefPath = path.join(os.homedir(), ".hvip", "preferences.json");
32197
+ let prefs = {};
32198
+ try {
32199
+ if (fs.existsSync(prefPath)) {
32200
+ prefs = JSON.parse(fs.readFileSync(prefPath, "utf-8"));
32201
+ }
32202
+ } catch {
32203
+ }
32204
+ const result = key ? { key, value: prefs[key] ?? null, found: key in prefs } : { all: prefs, count: Object.keys(prefs).length };
32205
+ return toResult({
32206
+ ...result,
32207
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
32208
+ _summary: key ? `\u504F\u597D "${key}" = ${prefs[key] ? `"${prefs[key]}"` : "\u672A\u8BBE\u7F6E"}` : `\u5DF2\u5B58\u50A8 ${Object.keys(prefs).length} \u6761\u504F\u597D`
32209
+ });
32210
+ } catch (e) {
32211
+ return toError(e);
32212
+ }
32213
+ }
32214
+ );
32215
+ server.tool(
32216
+ "agent_set_preference",
32217
+ 'CAT:[\u7CFB\u7EDF] | ## \u529F\u80FD\uFF1A\u8BBE\u7F6E Agent \u6301\u4E45\u5316\u504F\u597D\uFF0C\u8DE8\u4F1A\u8BDD\u4FDD\u7559\n## \u573A\u666F\uFF1A\u7528\u6237\u8BF4"\u4EE5\u540E\u9ED8\u8BA4\u4EA4\u6613\u5BF9\u7528BTC-USDT"\u65F6\uFF0CAgent \u4FDD\u5B58\u504F\u597D\uFF0C\u4E0B\u6B21\u4F1A\u8BDD\u81EA\u52A8\u6062\u590D\n## \u5173\u952E\u8BCD\uFF1A\u504F\u597D, preference, \u8BBE\u7F6E, \u8BB0\u5FC6, \u6301\u4E45\u5316, \u7528\u6237\u753B\u50CF\n## \u53C2\u6570\uFF1A\n## - key: \u504F\u597D\u952E\u540D\n## - value: \u504F\u597D\u503C\n## \u9274\u6743\uFF1APUBLIC \u2014 \u672C\u5730\u5199\u5165\n## \u98CE\u9669\uFF1AREAD \u2014 \u672C\u5730\u6587\u4EF6\u5199\u5165\uFF0C\u65E0\u8D44\u91D1\u98CE\u9669\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~200B\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u8BBE\u7F6E\u504F\u597D \u2192 agent_get_preference \u8BFB\u53D6 \u2192 Agent \u6309\u504F\u597D\u51B3\u7B56\n## \u5E38\u7528\u952E\u540D\u53C2\u8003:\n## - default_instId: \u9ED8\u8BA4\u4EA4\u6613\u5BF9\uFF0C\u5982 BTC-USDT-SWAP\n## - default_tdMode: \u9ED8\u8BA4\u4EA4\u6613\u6A21\u5F0F (cross/isolated/cash)\n## - risk_level: \u98CE\u9669\u504F\u597D (low/medium/high)\n## - trade_mode: \u4EA4\u6613\u6A21\u5F0F (spot_only/swap_permitted/margin_permitted)\n## - position_size_pct: \u5355\u7B14\u4ED3\u4F4D\u5360\u6BD4\uFF0C\u5982 0.1 (10%)',
32218
+ {
32219
+ key: external_exports.string().describe("\u504F\u597D\u952E\u540D\u3002\u5E38\u7528: default_instId, default_tdMode, risk_level, trade_mode, position_size_pct"),
32220
+ value: external_exports.string().describe("\u504F\u597D\u503C")
32221
+ },
32222
+ async ({ key, value }) => {
32223
+ try {
32224
+ const dir = path.join(os.homedir(), ".hvip");
32225
+ const prefPath = path.join(dir, "preferences.json");
32226
+ let prefs = {};
32227
+ try {
32228
+ fs.mkdirSync(dir, { recursive: true });
32229
+ if (fs.existsSync(prefPath)) {
32230
+ prefs = JSON.parse(fs.readFileSync(prefPath, "utf-8"));
32231
+ }
32232
+ } catch {
32233
+ }
32234
+ prefs[key] = value;
32235
+ fs.writeFileSync(prefPath, JSON.stringify(prefs, null, 2), "utf-8");
32236
+ return toResult({
32237
+ ok: true,
32238
+ key,
32239
+ value,
32240
+ stored: Object.keys(prefs).length,
32241
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
32242
+ _summary: `\u5DF2\u4FDD\u5B58\u504F\u597D: "${key}" = "${value}"\uFF08\u5171 ${Object.keys(prefs).length} \u6761\u504F\u597D\uFF09`
32243
+ });
32244
+ } catch (e) {
32245
+ return toError(e);
32246
+ }
32247
+ }
32248
+ );
32249
+ const CATALOG = {
32250
+ _instruction: [
32251
+ "\u4F60\u662F hvip MCP Server \u7684 AI Agent\u3002\u6536\u5230\u672C\u76EE\u5F55\u540E\uFF0C\u4F60\u5DF2\u7ECF\u638C\u63E1\u5168\u90E8\u5FC5\u8981\u4FE1\u606F\uFF0C\u65E0\u9700\u518D\u626B\u63CF tools/list\u3002",
32252
+ "\u5DE5\u4F5C\u6D41: 1) \u770B _setup \u4E86\u89E3\u8FDE\u63A5\u72B6\u6001 -> 2) _onboarding \u51B3\u5B9A\u7B2C\u4E00\u6B65 -> 3) \u5339\u914D\u7528\u6237\u610F\u56FE\u5230\u57DF -> 4) \u7528 go_to_params \u76F4\u63A5\u8C03\u76EE\u6807\u5DE5\u5177\u3002",
32253
+ "\u4E0D\u786E\u5B9A\u57DF\u65F6\u8C03\u7528 agent_catalog_detail { domain } \u67E5\u770B\u8BE5\u57DF\u7684\u8BE6\u7EC6\u5DE5\u5177\u6E05\u5355\u3002",
32254
+ "\u6240\u6709 WRITE/FUND_TRANSFER/ADMIN \u5DE5\u5177\u9700\u7528\u6237\u786E\u8BA4\u540E\u8C03\u7528\uFF0C\u4E0D\u53EF\u81EA\u52A8\u53D1\u8D77\u3002"
32255
+ ].join(" "),
32256
+ domains: [
32257
+ {
32258
+ domain: "\u8D26\u6237\u8D44\u4EA7",
32259
+ what: "\u67E5\u770B\u8D26\u6237\u4F59\u989D\u3001\u6301\u4ED3\u3001\u914D\u7F6E\u3001\u624B\u7EED\u8D39\u3001\u4F30\u503C",
32260
+ when: ["\u6211\u7684\u8D26\u6237", "\u6301\u4ED3", "\u4F59\u989D", "\u4FDD\u8BC1\u91D1", "\u6760\u6746\u500D\u6570", "\u8D44\u4EA7\u4F30\u503C", "\u624B\u7EED\u8D39\u7387", "\u8D26\u6237\u914D\u7F6E"],
32261
+ go_to: "okx_account_overview",
32262
+ also: [
32263
+ "okx_get_balance \u2014 \u5404\u5E01\u79CD\u4F59\u989D\u660E\u7EC6",
32264
+ "okx_get_positions \u2014 \u6301\u4ED3\u8BE6\u60C5\uFF08\u542B\u5E73\u4ED3\u4E0A\u4E0B\u6587 _actionContext\uFF09",
32265
+ "okx_get_account_config \u2014 \u8D26\u6237\u6A21\u5F0F/\u6301\u4ED3\u6A21\u5F0F",
32266
+ "okx_get_asset_valuation \u2014 \u603B\u8D44\u4EA7\u4F30\u503C",
32267
+ "okx_get_fee_rates \u2014 \u624B\u7EED\u8D39\u7387",
32268
+ "okx_get_leverage_info \u2014 \u5F53\u524D\u6760\u6746\u500D\u6570",
32269
+ "okx_get_max_size \u2014 \u6700\u5927\u53EF\u5F00\u6570\u91CF",
32270
+ "okx_set_leverage \u2014 \u8C03\u6574\u6760\u6746\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09"
32271
+ ],
32272
+ risk: "READ"
32273
+ },
32274
+ {
32275
+ domain: "\u884C\u60C5\u770B\u76D8",
32276
+ what: "\u67E5\u770B\u4EA7\u54C1\u4EF7\u683C\u3001K\u7EBF\u3001\u6DF1\u5EA6\u3001\u6210\u4EA4\u8BB0\u5F55",
32277
+ when: ["BTC\u4EF7\u683C", "ETH\u884C\u60C5", "K\u7EBF", "\u6DA8\u8DCC", "\u6DF1\u5EA6", "\u76D8\u53E3", "\u6210\u4EA4\u8BB0\u5F55", "\u8D70\u52BF"],
32278
+ go_to: "okx_quick_market",
32279
+ also: [
32280
+ "okx_get_ticker \u2014 \u5355\u4E2A\u4EA7\u54C1\u5B9E\u65F6\u884C\u60C5",
32281
+ "okx_get_tickers \u2014 \u67D0\u7C7B\u578B\u5168\u90E8\u4EA7\u54C1\u884C\u60C5",
32282
+ "okx_get_orderbook \u2014 \u5B8C\u6574\u6DF1\u5EA6/\u76D8\u53E3",
32283
+ "okx_get_candles \u2014 K\u7EBF\u6570\u636E",
32284
+ "okx_get_trades \u2014 \u6700\u65B0\u6210\u4EA4\u8BB0\u5F55",
32285
+ "okx_get_history_candles \u2014 \u5386\u53F2K\u7EBF",
32286
+ "okx_get_index_candles \u2014 \u6307\u6570K\u7EBF",
32287
+ "okx_get_mark_price_candles \u2014 \u6807\u8BB0\u4EF7K\u7EBF"
32288
+ ],
32289
+ risk: "READ"
32290
+ },
32291
+ {
32292
+ domain: "\u4E0B\u5355\u4EA4\u6613",
32293
+ what: "\u4E0B\u5355\u3001\u64A4\u5355\u3001\u6539\u5355\u3001\u5E73\u4ED3",
32294
+ when: ["\u4E70\u5165", "\u5356\u51FA", "\u4E0B\u5355", "\u5F00\u4ED3", "\u5E73\u4ED3", "\u64A4\u5355", "\u6539\u5355", "\u6302\u5355"],
32295
+ go_to: "agent_quick_trade",
32296
+ also: [
32297
+ "okx_preflight_check \u2014 \u4E0B\u5355\u524D\u9884\u68C0\uFF08\u6570\u91CF/\u9650\u4EF7/\u5408\u7EA6\u6362\u7B97\uFF09",
32298
+ "agent_simulate_order \u2014 \u6A21\u62DF\u4E0B\u5355\uFF08\u4E0D\u4EA7\u751F\u771F\u5B9E\u8BA2\u5355\uFF09",
32299
+ "okx_place_order \u2014 \u6807\u51C6\u4E0B\u5355\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32300
+ "okx_cancel_order \u2014 \u64A4\u5355\uFF08WRITE\uFF09",
32301
+ "okx_amend_order \u2014 \u6539\u5355\uFF08WRITE\uFF09",
32302
+ "okx_close_position \u2014 \u5E73\u4ED3\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32303
+ "okx_get_order \u2014 \u67E5\u8BE2\u8BA2\u5355\u72B6\u6001",
32304
+ "okx_get_orders_pending \u2014 \u5F53\u524D\u6302\u5355",
32305
+ "okx_get_orders_history \u2014 \u5386\u53F2\u8BA2\u5355",
32306
+ "okx_batch_orders \u2014 \u6279\u91CF\u4E0B\u5355\uFF08WRITE\uFF09",
32307
+ "okx_batch_cancel_orders \u2014 \u6279\u91CF\u64A4\u5355\uFF08WRITE\uFF09",
32308
+ "okx_amend_batch_orders \u2014 \u6279\u91CF\u6539\u5355\uFF08WRITE\uFF09"
32309
+ ],
32310
+ risk: "WRITE"
32311
+ },
32312
+ {
32313
+ domain: "\u98CE\u9669\u98CE\u63A7",
32314
+ what: "\u6301\u4ED3\u98CE\u9669\u8BC4\u4F30\u3001\u5F3A\u5E73\u9884\u8B66\u3001\u4FDD\u8BC1\u91D1\u7387",
32315
+ when: ["\u98CE\u9669", "\u7206\u4ED3", "\u5F3A\u5E73", "\u4FDD\u8BC1\u91D1\u7387", "\u5065\u5EB7\u5EA6", "\u98CE\u63A7", "\u4F1A\u4E0D\u4F1A\u7206"],
32316
+ go_to: "agent_risk_overview",
32317
+ also: [
32318
+ "okx_get_account_position_risk \u2014 \u8D26\u6237\u6301\u4ED3\u98CE\u9669\u8BE6\u60C5",
32319
+ "okx_get_risk_state \u2014 \u8D26\u6237\u98CE\u63A7\u72B6\u6001",
32320
+ "okx_get_positions \u2014 \u6301\u4ED3\u4E2D\u7684 mgnRatio/liqPx",
32321
+ "okx_get_margin_balance \u2014 \u4FDD\u8BC1\u91D1\u4F59\u989D",
32322
+ "agent_quick_trade \u2014 \u98CE\u63A7\u544A\u8B66\u540E\u5E73\u4ED3\uFF08WRITE\uFF09"
32323
+ ],
32324
+ risk: "READ"
32325
+ },
32326
+ {
32327
+ domain: "\u5E02\u573A\u626B\u63CF",
32328
+ what: "\u53D1\u73B0\u5E02\u573A\u673A\u4F1A\u3001\u6DA8\u5E45\u699C\u3001\u8DCC\u5E45\u699C\u3001\u8D44\u91D1\u8D39\u7387\u5F02\u5E38",
32329
+ when: ["\u4ECA\u5929\u6709\u4EC0\u4E48\u673A\u4F1A", "\u6DA8\u5E45\u699C", "\u8DCC\u5E45\u699C", "\u4EC0\u4E48\u5728\u6DA8", "\u4EC0\u4E48\u5728\u8DCC", "\u5F02\u52A8", "\u626B\u63CF"],
32330
+ go_to: "agent_market_scan",
32331
+ also: [
32332
+ "okx_quick_market \u2014 \u6DF1\u5165\u770B\u67D0\u4E2A\u54C1\u79CD",
32333
+ "okx_get_funding_rate \u2014 \u6C38\u7EED\u5408\u7EA6\u8D44\u91D1\u8D39\u7387",
32334
+ "okx_get_tickers \u2014 \u5168\u5E02\u573A\u884C\u60C5"
32335
+ ],
32336
+ risk: "READ"
32337
+ },
32338
+ {
32339
+ domain: "\u76C8\u4E8F\u590D\u76D8",
32340
+ what: "\u8BA1\u7B97\u5DF2\u5B9E\u73B0/\u672A\u5B9E\u73B0\u76C8\u4E8F\u3001\u4EA4\u6613\u7EE9\u6548",
32341
+ when: ["\u8D5A\u4E86\u591A\u5C11", "\u4E8F\u4E86\u591A\u5C11", "\u76C8\u4E8F", "\u7EE9\u6548", "\u590D\u76D8", "PnL", "\u6536\u76CA"],
32342
+ go_to: "agent_pnl_report",
32343
+ also: [
32344
+ "okx_get_positions \u2014 \u5F53\u524D\u6D6E\u52A8\u76C8\u4E8F",
32345
+ "okx_get_orders_history \u2014 \u5386\u53F2\u8BA2\u5355\u76C8\u4E8F",
32346
+ "okx_get_positions_history \u2014 \u5386\u53F2\u5E73\u4ED3\u76C8\u4E8F",
32347
+ "okx_get_fills \u2014 \u6210\u4EA4\u660E\u7EC6"
32348
+ ],
32349
+ risk: "READ"
32350
+ },
32351
+ {
32352
+ domain: "\u8D44\u91D1\u7BA1\u7406",
32353
+ what: "\u5212\u8F6C\u3001\u63D0\u73B0\u3001\u5145\u503C\u3001\u5151\u6362",
32354
+ when: ["\u5212\u8F6C", "\u63D0\u73B0", "\u5145\u503C", "\u8F6C\u8D26", "\u5151\u6362", "\u95EA\u5151", "\u51FA\u5165\u91D1", "deposit", "withdraw"],
32355
+ go_to: "okx_get_balance",
32356
+ also: [
32357
+ "okx_transfer \u2014 \u8D44\u91D1\u5212\u8F6C\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32358
+ "okx_withdrawal \u2014 \u63D0\u73B0\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32359
+ "okx_get_deposit_address \u2014 \u83B7\u53D6\u5145\u503C\u5730\u5740",
32360
+ "okx_get_deposit_history \u2014 \u5145\u503C\u8BB0\u5F55",
32361
+ "okx_get_withdrawal_history \u2014 \u63D0\u73B0\u8BB0\u5F55",
32362
+ "okx_convert_trade \u2014 \u4E00\u952E\u5151\u6362\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32363
+ "okx_get_convert_currencies \u2014 \u53EF\u5151\u6362\u5E01\u79CD",
32364
+ "okx_get_currencies \u2014 \u94FE\u4E0A\u5E01\u79CD\u4FE1\u606F",
32365
+ "okx_get_funding_balance \u2014 \u8D44\u91D1\u8D26\u6237\u4F59\u989D"
32366
+ ],
32367
+ risk: "FUND_TRANSFER"
32368
+ },
32369
+ {
32370
+ domain: "\u7B56\u7565\u4EA4\u6613",
32371
+ what: "\u7F51\u683C\u3001\u8DDF\u5355\u3001\u4FE1\u53F7\u3001\u7B56\u7565\u59D4\u6258\u3001\u4EF7\u5DEE\u3001RFQ",
32372
+ when: ["\u7F51\u683C", "\u8DDF\u5355", "\u4FE1\u53F7", "\u7B56\u7565", "\u5B9A\u6295", "\u4EF7\u5DEE", "\u5927\u5B97", "RFQ"],
32373
+ go_to: "okx_get_grid_ai_param",
32374
+ also: [
32375
+ "okx_create_grid_order \u2014 \u521B\u5EFA\u7F51\u683C\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32376
+ "okx_stop_grid_order \u2014 \u505C\u6B62\u7F51\u683C",
32377
+ "okx_get_grid_positions \u2014 \u7F51\u683C\u6301\u4ED3",
32378
+ "okx_create_recurring_plan \u2014 \u521B\u5EFA\u5B9A\u6295\u8BA1\u5212\uFF08WRITE\uFF09",
32379
+ "okx_copy_trader \u2014 \u5F00\u59CB\u8DDF\u5355\uFF08WRITE\uFF0C\u9700\u786E\u8BA4\uFF09",
32380
+ "okx_get_lead_trader_positions \u2014 \u4EA4\u6613\u5458\u6301\u4ED3",
32381
+ "okx_get_lead_trader_stats \u2014 \u4EA4\u6613\u5458\u7EDF\u8BA1",
32382
+ "okx_place_algo_order \u2014 \u7B56\u7565\u59D4\u6258\u4E0B\u5355\uFF08WRITE\uFF09",
32383
+ "okx_get_algo_orders \u2014 \u7B56\u7565\u59D4\u6258\u5217\u8868",
32384
+ "okx_create_rfq \u2014 \u521B\u5EFA\u5927\u5B97\u8BE2\u4EF7\uFF08WRITE\uFF09"
32385
+ ],
32386
+ risk: "WRITE"
32387
+ },
32388
+ {
32389
+ domain: "\u9884\u6D4B\u5E02\u573A",
32390
+ what: "\u4E8B\u4EF6\u5408\u7EA6\u3001\u9884\u6D4B\u5E02\u573A\u4EA4\u6613",
32391
+ when: ["\u9884\u6D4B\u5E02\u573A", "\u4E8B\u4EF6\u5408\u7EA6", "outcomes", "predictions", "\u5BF9\u8D4C"],
32392
+ go_to: "okx_event_instruments",
32393
+ also: [
32394
+ "okx_event_place_order \u2014 \u4E8B\u4EF6\u5408\u7EA6\u4E0B\u5355\uFF08WRITE\uFF09",
32395
+ "okx_event_fills \u2014 \u4E8B\u4EF6\u5408\u7EA6\u6210\u4EA4",
32396
+ "okx_predictions_positions \u2014 \u9884\u6D4B\u5E02\u573A\u6301\u4ED3",
32397
+ "okx_predictions_order_list \u2014 \u9884\u6D4B\u5E02\u573A\u8BA2\u5355",
32398
+ "okx_predictions_redeem \u2014 \u9884\u6D4B\u5E02\u573A\u8D4E\u56DE\uFF08WRITE\uFF09"
32399
+ ],
32400
+ risk: "READ"
32401
+ },
32402
+ {
32403
+ domain: "\u6280\u672F\u6307\u6807",
32404
+ what: "\u6280\u672F\u5206\u6790\u6307\u6807\u8BA1\u7B97\uFF1ARSI\u3001MACD\u3001\u5E03\u6797\u5E26\u3001ADX\u3001Supertrend \u7B49 17 \u79CD\u6307\u6807",
32405
+ when: ["RSI", "MACD", "\u5E03\u6797\u5E26", "KDJ", "ADX", "\u8D85\u4E70", "\u8D85\u5356", "\u91D1\u53C9", "\u6B7B\u53C9", "\u6280\u672F\u6307\u6807", "\u6307\u6807\u5206\u6790"],
32406
+ go_to: "okx_indicator",
32407
+ also: [
32408
+ "okx_indicator \u2014 \u5355\u6307\u6807\u8BA1\u7B97+\u4FE1\u53F7\u89E3\u8BFB",
32409
+ "okx_indicator_batch \u2014 \u591A\u6307\u6807\u6279\u91CF+\u7EFC\u5408\u4FE1\u53F7",
32410
+ "okx_get_candles \u2014 \u539F\u59CBK\u7EBF\u6570\u636E"
32411
+ ],
32412
+ risk: "READ"
32413
+ },
32414
+ {
32415
+ domain: "\u806A\u660E\u94B1",
32416
+ what: "\u4EA4\u6613\u5458\u6392\u884C\u699C\u3001\u806A\u660E\u94B1\u6D41\u5411\u3001\u5E02\u573A\u60C5\u7EEA\u5206\u6790",
32417
+ when: ["\u4EA4\u6613\u5458", "\u6392\u884C\u699C", "\u806A\u660E\u94B1", "\u8DDF\u8C01\u8D5A\u94B1", "\u60C5\u7EEA", "\u591A\u7A7A\u6BD4", "\u6050\u614C", "smart money"],
32418
+ go_to: "okx_smart_leaderboard",
32419
+ also: [
32420
+ "okx_smart_leaderboard \u2014 \u4EA4\u6613\u5458\u6392\u884C\u699C",
32421
+ "okx_smart_trader_detail \u2014 \u5355\u4EA4\u6613\u5458\u6DF1\u5EA6\u5206\u6790",
32422
+ "okx_smart_sentiment \u2014 \u5E02\u573A\u60C5\u7EEA\u4EEA\u8868\u76D8",
32423
+ "okx_get_lead_trader_positions \u2014 \u4EA4\u6613\u5458\u5F53\u524D\u6301\u4ED3",
32424
+ "okx_get_lead_trader_stats \u2014 \u4EA4\u6613\u5458\u5386\u53F2\u7EDF\u8BA1"
32425
+ ],
32426
+ risk: "READ"
32427
+ },
32428
+ {
32429
+ domain: "WebSocket \u5B9E\u65F6",
32430
+ what: "\u5B9E\u65F6\u884C\u60C5\u63A8\u9001\u3001\u6210\u4EA4\u63A8\u9001",
32431
+ when: ["\u5B9E\u65F6", "\u8BA2\u9605", "\u63A8\u9001", "websocket", "ws", "\u76D1\u542C"],
32432
+ go_to: "okx_ws_subscribe",
32433
+ also: [
32434
+ "okx_ws_events \u2014 \u83B7\u53D6\u63A8\u9001\u4E8B\u4EF6",
32435
+ "okx_ws_status \u2014 \u8BA2\u9605\u72B6\u6001",
32436
+ "okx_ws_close \u2014 \u5173\u95ED\u8BA2\u9605"
32437
+ ],
32438
+ risk: "READ"
32439
+ },
32440
+ {
32441
+ domain: "\u6A21\u62DF\u4F30\u7B97",
32442
+ what: "\u4E0D\u4EA7\u751F\u771F\u5B9E\u8BA2\u5355\uFF0C\u9884\u4F30\u6210\u4EA4\u4EF7\u3001\u6ED1\u70B9\u3001\u624B\u7EED\u8D39",
32443
+ when: ["\u5982\u679C\u4E70\u5165", "\u5982\u679C\u5356\u51FA", "\u6A21\u62DF", "\u9884\u4F30", "\u6C99\u76D2", "\u8BD5\u7B97", "\u4F1A\u600E\u6837"],
32444
+ go_to: "agent_simulate_order",
32445
+ also: [
32446
+ "agent_quick_trade \u2014 \u786E\u8BA4\u540E\u771F\u5B9E\u4E0B\u5355\uFF08WRITE\uFF09",
32447
+ "okx_preflight_check \u2014 \u4E0B\u5355\u524D\u53C2\u6570\u6821\u9A8C",
32448
+ "okx_position_builder \u2014 \u7EC4\u5408\u4FDD\u8BC1\u91D1\u8BD5\u7B97"
32449
+ ],
32450
+ risk: "READ"
32451
+ },
32452
+ {
32453
+ domain: "\u7CFB\u7EDF\u5DE5\u5177",
32454
+ what: "\u504F\u597D\u8BBE\u7F6E\u3001\u53CD\u9988\u7559\u8A00\u3001Agent\u96C6\u7FA4\u7BA1\u7406",
32455
+ when: ["\u504F\u597D", "\u8BBE\u7F6E\u9ED8\u8BA4", "\u8BB0\u4F4F", "\u53CD\u9988", "\u5EFA\u8BAE", "Agent Hub", "\u96C6\u7FA4"],
32456
+ go_to: "agent_get_preference",
32457
+ also: [
32458
+ "agent_set_preference \u2014 \u4FDD\u5B58\u504F\u597D",
32459
+ "okx_agent_feedback \u2014 \u63D0\u4EA4\u4F7F\u7528\u53CD\u9988",
32460
+ "agent_hub_status \u2014 Agent \u96C6\u7FA4\u72B6\u6001",
32461
+ "agent_hub_dispatch \u2014 \u6D3E\u53D1\u4EFB\u52A1",
32462
+ "agent_room_send \u2014 \u623F\u95F4\u6D88\u606F",
32463
+ "xlayer_subscribe \u2014 X Layer \u94FE\u4E0A\u4E8B\u4EF6",
32464
+ "xlayer_call \u2014 X Layer \u5408\u7EA6\u8C03\u7528\uFF08WRITE\uFF09"
32465
+ ],
32466
+ risk: "READ"
32467
+ }
32468
+ ],
32469
+ _tips: [
32470
+ "\u6BCF\u4E2A\u65B0\u4F1A\u8BDD\u5148\u8C03 agent_get_preference \u6062\u590D\u7528\u6237\u504F\u597D(\u9ED8\u8BA4\u4EA4\u6613\u5BF9/\u98CE\u9669\u504F\u597D/\u4ED3\u4F4D\u5360\u6BD4)",
32471
+ "\u9700\u8981 API Key \u65F6\u544A\u8BC9\u7528\u6237\u53BB OKX \u5B98\u7F51\u521B\u5EFA\uFF0C\u5F00\u901A\u8BFB\u53D6+\u4EA4\u6613\u6743\u9650",
32472
+ "\u5148\u6A21\u62DF\u540E\u4EA4\u6613: agent_simulate_order \u9884\u4F30 -> \u786E\u8BA4 -> agent_quick_trade \u4E0B\u5355",
32473
+ "WRITE \u5DE5\u5177\u4F1A\u4FEE\u6539\u8D26\u6237\uFF0C\u8C03\u7528\u524D\u5FC5\u987B\u5411\u7528\u6237\u786E\u8BA4\uFF0C\u4E0D\u53EF\u81EA\u52A8\u53D1\u8D77",
32474
+ "\u6280\u672F\u6307\u6807\u7528 okx_indicator \u5355\u4E2A\u8BA1\u7B97\u6216 okx_indicator_batch \u6279\u91CF\uFF0C\u652F\u6301 RSI/MACD/\u5E03\u6797\u5E26\u7B49 17 \u79CD",
32475
+ "\u806A\u660E\u94B1\u5206\u6790\u7528 okx_smart_leaderboard \u627E\u9876\u5C16\u4EA4\u6613\u5458 \u2192 okx_smart_sentiment \u770B\u5E02\u573A\u60C5\u7EEA"
32476
+ ]
32477
+ };
32478
+ server.tool(
32479
+ "agent_catalog",
32480
+ "CAT:[\u7CFB\u7EDF] | ## \u529F\u80FD\uFF1A\u5168\u5C40\u5DE5\u5177\u5BFC\u822A\u2014\u2014Agent \u9996\u6B21\u8FDE\u63A5 hvip MCP \u540E\u7B2C\u4E00\u4E2A\u5E94\u8C03\u7528\u7684\u5DE5\u5177\u3002\u8FD4\u56DE\u6309\u7528\u6237\u610F\u56FE\u5206\u7EC4\u7684\u5DE5\u5177\u5730\u56FE\n## \u573A\u666F\uFF1AAgent \u9996\u6B21\u8FDE\u63A5\u3001\u4E0D\u786E\u5B9A\u8BE5\u7528\u4EC0\u4E48\u5DE5\u5177\u3001\u60F3\u4E86\u89E3 hvip \u80FD\u505A\u4EC0\u4E48\u3002\u770B\u5B8C\u76EE\u5F55\u540E Agent \u6309\u57DF\u76F4\u8FBE\u76EE\u6807\u5DE5\u5177\uFF0C\u65E0\u9700\u9605\u8BFB\u5168\u90E8 350+ \u5DE5\u5177\u63CF\u8FF0\n## \u5173\u952E\u8BCD\uFF1A\u5BFC\u822A, catalog, \u76EE\u5F55, \u5730\u56FE, \u5165\u53E3, \u9996\u6B21\u8FDE\u63A5, \u5DE5\u5177\u53D1\u73B0, \u7D22\u5F15, \u8DEF\u7531\n## \u53C2\u6570\uFF1A\u65E0\n## \u9274\u6743\uFF1APUBLIC \u2014 \u7EAF\u7D22\u5F15\uFF0C\u4E0D\u67E5\u4EFB\u4F55 API\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~5KB \u2014 12 \u4E2A\u57DF\u7684\u7ED3\u6784\u5316\u7D22\u5F15\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u770B\u5168\u5C40 \u2192 agent_catalog_detail { domain } \u770B\u57DF\u8BE6\u60C5 \u2192 \u76F4\u63A5\u8C03\u7528\u76EE\u6807\u5DE5\u5177",
32481
+ {},
32482
+ async () => {
32483
+ try {
32484
+ const hasAuth = auth !== null;
32485
+ const setup = {
32486
+ hasApiKey: hasAuth,
32487
+ readOnly: process.env.OKX_READ_ONLY === "true",
32488
+ mode: hasAuth ? process.env.OKX_READ_ONLY === "true" ? "\u53EA\u8BFB\uFF08WRITE \u5DE5\u5177\u5DF2\u9690\u85CF\uFF09" : "\u5B8C\u6574\u4EA4\u6613\uFF08\u6240\u6709\u5DE5\u5177\u53EF\u7528\uFF09" : "\u672A\u914D\u7F6E API Key\uFF08\u4EC5\u516C\u5F00\u5DE5\u5177\u53EF\u7528\uFF09"
32489
+ };
32490
+ if (!hasAuth) {
32491
+ setup.howToConfigure = {
32492
+ required: ["OKX_API_KEY", "OKX_SECRET_KEY", "OKX_PASSPHRASE"],
32493
+ where: "OKX \u5B98\u7F51 -> \u4E2A\u4EBA\u4E2D\u5FC3 -> API -> \u521B\u5EFA API Key\uFF08\u5F00\u901A \u8BFB\u53D6+\u4EA4\u6613 \u6743\u9650\uFF09",
32494
+ quickStart: "npx hvip-mcp setup --client claude-code"
32495
+ };
32496
+ setup.whatYouCanDoNow = "\u5F53\u524D\u65E0 API Key\uFF0C\u4F46\u4EE5\u4E0B\u57DF\u7684\u5DE5\u5177\u53EF\u7ACB\u5373\u4F7F\u7528: \u884C\u60C5\u770B\u76D8 / \u6280\u672F\u6307\u6807 / \u5E02\u573A\u626B\u63CF / \u806A\u660E\u94B1 / \u9884\u6D4B\u5E02\u573A\uFF08\u516C\u5F00\u67E5\u8BE2\uFF09/ WebSocket \u5B9E\u65F6 / \u7CFB\u7EDF\u5DE5\u5177";
32497
+ }
32498
+ const onboarding = hasAuth ? "API Key \u5DF2\u914D\u7F6E\u3002\u5EFA\u8BAE\u7B2C\u4E00\u6B65: agent_get_preference \u6062\u590D\u504F\u597D -> okx_account_overview \u4E86\u89E3\u8D26\u6237\u5168\u666F -> \u6839\u636E\u7528\u6237\u610F\u56FE\u5339\u914D\u57DF go_to \u5DE5\u5177" : "\u6B22\u8FCE\uFF01hvip MCP \u5DF2\u8FDE\u63A5\u4F46\u5C1A\u672A\u914D\u7F6E API Key\u3002\u544A\u8BC9\u7528\u6237\uFF1A\u60F3\u770B\u884C\u60C5\u548C\u6307\u6807\u73B0\u5728\u5C31\u80FD\u770B\uFF0C\u60F3\u770B\u8D26\u6237\u548C\u4EA4\u6613\u8BF7\u5148\u914D Key\u3002\u914D\u597D\u540E\u91CD\u8FDE MCP \u5373\u53EF\u3002";
32499
+ const paramsHints = {
32500
+ "okx_account_overview": "\u65E0\u9700\u53C2\u6570",
32501
+ "okx_quick_market": "instId (\u5982 BTC-USDT\u3001ETH-USDT-SWAP)",
32502
+ "okx_preflight_check": "instId, tdMode (cash|cross|isolated), sz, px?, side?, ordType?",
32503
+ "agent_quick_trade": "instId, side (buy|sell), sz, tdMode (cash|cross|isolated), px?, ordType?",
32504
+ "agent_risk_overview": "\u65E0\u9700\u53C2\u6570",
32505
+ "agent_market_scan": "instType? (\u9ED8\u8BA4SWAP), topN?, sortBy? (change|vol|fundingRate)",
32506
+ "agent_pnl_report": "days? (\u9ED8\u8BA47)",
32507
+ "okx_get_balance": "ccy? (\u5982 BTC\uFF0C\u4E0D\u586B\u5168\u90E8)",
32508
+ "agent_simulate_order": "instId, side, sz, tdMode, px?",
32509
+ "okx_indicator": "instId, indicator (rsi|macd|bb|atr|stoch \u7B4917\u79CD), period?, bar? (\u9ED8\u8BA41H)",
32510
+ "okx_indicator_batch": "instId, indicators (\u9017\u53F7\u5206\u9694\u5982 rsi,macd,bb), bar?",
32511
+ "okx_smart_leaderboard": "instType?, sortBy?, topN?",
32512
+ "okx_smart_trader_detail": "uniqueCode (\u4EA4\u6613\u5458\u7801), instType?",
32513
+ "okx_smart_sentiment": "instFamily? (\u9ED8\u8BA4BTC-USD)",
32514
+ "agent_get_preference": "key? (\u4E0D\u586B\u8FD4\u56DE\u5168\u90E8)",
32515
+ "agent_set_preference": "key, value",
32516
+ "okx_event_instruments": "eventType? (\u4E0D\u586B\u8FD4\u56DE\u5168\u90E8)",
32517
+ "okx_ws_subscribe": "channels (JSON\u6570\u7EC4)",
32518
+ "okx_get_grid_ai_param": "instType, algoOrdType"
32519
+ };
32520
+ const enrichedDomains = CATALOG.domains.map((d) => ({
32521
+ ...d,
32522
+ go_to_params: paramsHints[d.go_to] || "\u89C1 agent_catalog_detail"
32523
+ }));
32524
+ return toResult({
32525
+ tsIso: (/* @__PURE__ */ new Date()).toISOString(),
32526
+ _setup: setup,
32527
+ _onboarding: onboarding,
32528
+ _instruction: CATALOG._instruction,
32529
+ domains: enrichedDomains,
32530
+ _tips: CATALOG._tips
32531
+ });
32532
+ } catch (e) {
32533
+ return toError(e);
32534
+ }
32535
+ }
32536
+ );
32537
+ const DOMAIN_DETAILS = {
32538
+ "\u8D26\u6237\u8D44\u4EA7": {
32539
+ workflow: "okx_account_overview\uFF08\u5168\u666F\uFF09\u2192 \u67E5\u770B\u5177\u4F53\u9879 \u2192 okx_get_balance / okx_get_positions \u7EC6\u5316",
32540
+ tools: [
32541
+ { name: "okx_account_overview", auth: "API Key", params: "\u65E0", what: "\u8D26\u6237\u5168\u666F\uFF1A\u4F59\u989D+\u6301\u4ED3+\u914D\u7F6E+\u4F30\u503C \u56DB\u5408\u4E00\u4E00\u952E\u67E5\u8BE2" },
32542
+ { name: "okx_get_balance", auth: "API Key", params: "ccy? (\u5E01\u79CD\uFF0C\u4E0D\u586B\u5168\u90E8)", what: "\u4EA4\u6613\u8D26\u6237\u5404\u5E01\u79CD\u4F59\u989D" },
32543
+ { name: "okx_get_positions", auth: "API Key", params: "instType? (\u4E0D\u586B\u5168\u90E8)", what: "\u6301\u4ED3\u8BE6\u60C5\uFF0C\u542B _actionContext \u53EF\u76F4\u63A5\u7528\u4E8E\u5E73\u4ED3" },
32544
+ { name: "okx_get_account_config", auth: "API Key", params: "\u65E0", what: "\u8D26\u6237\u6A21\u5F0F/\u6301\u4ED3\u6A21\u5F0F/UID" },
32545
+ { name: "okx_get_asset_valuation", auth: "API Key", params: "ccy? (\u8BA1\u4EF7\u5E01\u79CD)", what: "\u603B\u8D44\u4EA7\u4F30\u503C" },
32546
+ { name: "okx_get_fee_rates", auth: "API Key", params: "instType?, instId?", what: "\u624B\u7EED\u8D39\u7387" },
32547
+ { name: "okx_get_leverage_info", auth: "API Key", params: "instId, mgnMode", what: "\u5F53\u524D\u6760\u6746\u500D\u6570" },
32548
+ { name: "okx_get_max_size", auth: "API Key", params: "instId, tdMode", what: "\u6700\u5927\u53EF\u5F00\u4ED3\u6570\u91CF" },
32549
+ { name: "okx_set_leverage", auth: "API Key (\u4EA4\u6613)", params: "instId, lever, mgnMode", what: "\u8C03\u6574\u6760\u6746 \u2014 WRITE\uFF0C\u9700\u786E\u8BA4" },
32550
+ { name: "okx_get_max_loan", auth: "API Key", params: "instId, mgnMode", what: "\u6700\u5927\u53EF\u501F\u6570\u91CF" },
32551
+ { name: "okx_get_margin_balance", auth: "API Key", params: "instId, mgnMode", what: "\u4FDD\u8BC1\u91D1\u4F59\u989D\u8BE6\u60C5" },
32552
+ { name: "okx_set_position_mode", auth: "API Key (\u4EA4\u6613)", params: "posMode", what: "\u5207\u6362\u6301\u4ED3\u6A21\u5F0F \u2014 ADMIN\uFF0C\u9700\u786E\u8BA4" },
32553
+ { name: "okx_get_account_bills", auth: "API Key", params: "instType?, ccy?, limit?", what: "\u8D26\u6237\u8D44\u91D1\u6D41\u6C34" },
32554
+ { name: "okx_get_interest_accrued", auth: "API Key", params: "instId?, ccy?", what: "\u501F\u5E01\u5229\u606F\u7D2F\u8BA1" },
32555
+ { name: "okx_get_interest_rates", auth: "API Key", params: "ccy?", what: "\u5404\u5E01\u79CD\u501F\u5E01\u5229\u7387" },
32556
+ { name: "okx_get_interest_limits", auth: "API Key", params: "\u65E0", what: "\u5229\u606F\u9650\u989D" },
32557
+ { name: "okx_get_max_withdrawal", auth: "API Key", params: "ccy?", what: "\u6700\u5927\u53EF\u63D0\u73B0\u6570\u91CF" },
32558
+ { name: "okx_get_account_position_risk", auth: "API Key", params: "\u65E0", what: "\u6301\u4ED3\u98CE\u9669\u8BC4\u4F30" },
32559
+ { name: "okx_get_risk_state", auth: "API Key", params: "\u65E0", what: "\u8D26\u6237\u98CE\u63A7\u72B6\u6001" },
32560
+ { name: "okx_get_account_greeks", auth: "API Key", params: "instType?, instFamily?", what: "\u671F\u6743 Greeks \u98CE\u9669\u53C2\u6570" },
32561
+ { name: "okx_get_trade_fee", auth: "API Key", params: "instType?, instId?", what: "\u6309\u4EA7\u54C1\u7EF4\u5EA6\u67E5\u8BE2\u624B\u7EED\u8D39" },
32562
+ { name: "okx_get_convert_currencies", auth: "API Key", params: "\u65E0", what: "\u652F\u6301\u4E00\u952E\u5151\u6362\u7684\u5E01\u79CD\u5217\u8868" },
32563
+ { name: "okx_convert_trade", auth: "API Key (\u4EA4\u6613)", params: "fromCcy, toCcy, sz", what: "\u6267\u884C\u8D44\u4EA7\u5151\u6362 \u2014 FUND_TRANSFER\uFF0C\u9700\u786E\u8BA4" },
32564
+ { name: "okx_get_bills", auth: "API Key", params: "instType?, ccy?", what: "\u8D26\u6237\u8D26\u5355\u6D41\u6C34" },
32565
+ { name: "okx_get_positions_history", auth: "API Key", params: "instType?, instId?", what: "\u5386\u53F2\u6301\u4ED3\uFF08\u5DF2\u5E73\u4ED3\uFF09" },
32566
+ { name: "okx_get_account_bills_archive", auth: "API Key", params: "\u65E0", what: "\u5F52\u6863\u8D26\u5355\uFF083\u4E2A\u6708\u524D\uFF09" },
32567
+ { name: "okx_borrow_repay", auth: "API Key (\u4EA4\u6613)", params: "ccy, side(borrow|repay), amt", what: "\u501F\u5E01/\u8FD8\u6B3E \u2014 FUND_TRANSFER\uFF0C\u9700\u786E\u8BA4" },
32568
+ { name: "okx_get_borrow_repay_history", auth: "API Key", params: "\u65E0", what: "\u501F\u8D37\u5386\u53F2\u8BB0\u5F55" },
32569
+ { name: "okx_activate_option", auth: "API Key (\u4EA4\u6613)", params: "\u65E0", what: "\u6FC0\u6D3B\u671F\u6743\u4EA4\u6613 \u2014 ADMIN\uFF0C\u9700\u786E\u8BA4" },
32570
+ { name: "okx_set_account_mode", auth: "API Key (\u4EA4\u6613)", params: "acctLv(1|2|3|4)", what: "\u8BBE\u7F6E\u8D26\u6237\u5C42\u7EA7 \u2014 ADMIN\uFF0C\u9700\u786E\u8BA4" }
32571
+ ]
32572
+ },
32573
+ "\u884C\u60C5\u770B\u76D8": {
32574
+ workflow: "okx_quick_market\uFF08\u901F\u89C8\uFF09\u2192 \u9700\u8981K\u7EBF\u65F6\u8C03 okx_get_candles \u2192 \u9700\u8981\u5168\u6DF1\u5EA6\u65F6\u8C03 okx_get_orderbook",
32575
+ tools: [
32576
+ { name: "okx_quick_market", auth: "\u516C\u5F00", params: "instId", what: "\u884C\u60C5+5\u6863\u6DF1\u5EA6+\u8D44\u91D1\u8D39\u7387 \u4E09\u5408\u4E00\u901F\u89C8" },
32577
+ { name: "okx_get_ticker", auth: "\u516C\u5F00", params: "instId", what: "\u5355\u4E2A\u4EA7\u54C1\u6700\u65B0\u884C\u60C5" },
32578
+ { name: "okx_get_tickers", auth: "\u516C\u5F00", params: "instType", what: "\u67D0\u7C7B\u578B\u5168\u90E8\u4EA7\u54C1\u884C\u60C5" },
32579
+ { name: "okx_get_orderbook", auth: "\u516C\u5F00", params: "instId, sz?", what: "\u8BA2\u5355\u7C3F\u6DF1\u5EA6\uFF08\u9ED8\u8BA41\u6863\uFF0C\u6700\u5927400\u6863\uFF09" },
32580
+ { name: "okx_get_books_full", auth: "\u516C\u5F00", params: "instId", what: "\u5168\u91CF\u8BA2\u5355\u7C3F" },
32581
+ { name: "okx_get_candles", auth: "\u516C\u5F00", params: "instId, bar, limit?", what: "K\u7EBF\u6570\u636E" },
32582
+ { name: "okx_get_history_candles", auth: "\u516C\u5F00", params: "instId, bar, after?", what: "\u5386\u53F2K\u7EBF\uFF08\u66F4\u65E9\u7684\uFF09" },
32583
+ { name: "okx_get_index_candles", auth: "\u516C\u5F00", params: "instId, bar", what: "\u6307\u6570K\u7EBF" },
32584
+ { name: "okx_get_mark_price_candles", auth: "\u516C\u5F00", params: "instId, bar", what: "\u6807\u8BB0\u4EF7K\u7EBF" },
32585
+ { name: "okx_get_trades", auth: "\u516C\u5F00", params: "instId, limit?", what: "\u6700\u65B0\u6210\u4EA4\u8BB0\u5F55" },
32586
+ { name: "okx_get_history_trades", auth: "\u516C\u5F00", params: "instId, after?", what: "\u5386\u53F2\u6210\u4EA4\u8BB0\u5F55" },
32587
+ { name: "okx_get_index_tickers", auth: "\u516C\u5F00", params: "quoteCcy?", what: "\u6307\u6570\u884C\u60C5" },
32588
+ { name: "okx_get_block_tickers", auth: "\u516C\u5F00", params: "instType", what: "\u6279\u91CF\u4EA7\u54C1\u884C\u60C5\uFF08Tickers\uFF09" },
32589
+ { name: "okx_get_mark_price", auth: "\u516C\u5F00", params: "instType?, instId?", what: "\u6807\u8BB0\u4EF7\u683C" },
32590
+ { name: "okx_get_funding_rate", auth: "\u516C\u5F00", params: "instId", what: "\u6C38\u7EED\u5408\u7EA6\u5F53\u524D\u8D44\u91D1\u8D39\u7387" },
32591
+ { name: "okx_get_funding_rate_history", auth: "\u516C\u5F00", params: "instId, before?, after?", what: "\u8D44\u91D1\u8D39\u7387\u5386\u53F2" }
32592
+ ]
32593
+ },
32594
+ "\u4E0B\u5355\u4EA4\u6613": {
32595
+ workflow: "\u5148\u8C03 agent_simulate_order \u9884\u4F30 \u2192 \u518D\u8C03 okx_preflight_check \u6821\u9A8C \u2192 \u6700\u540E agent_quick_trade \u4E0B\u5355",
32596
+ tools: [
32597
+ { name: "agent_quick_trade", auth: "API Key (\u4EA4\u6613)", params: "instId, side, sz, tdMode, px?, ordType?", what: "\u4E00\u952E\u4E0B\u5355\u5168\u6D41\u7A0B \u2014 WRITE\uFF0C\u9700\u786E\u8BA4" },
32598
+ { name: "agent_simulate_order", auth: "API Key (\u53EA\u8BFB)", params: "instId, side, sz, tdMode, px?", what: "\u6A21\u62DF\u4E0B\u5355\uFF1A\u6ED1\u70B9/\u624B\u7EED\u8D39/\u6210\u672C\u9884\u4F30\uFF08\u65E0\u771F\u5B9E\u8BA2\u5355\uFF09" },
32599
+ { name: "okx_preflight_check", auth: "API Key (\u53EA\u8BFB)", params: "instId, tdMode, sz, px?, side?, ordType?", what: "\u4E0B\u5355\u524D\u9884\u68C0\uFF1A\u6570\u91CF/\u9650\u4EF7/\u5408\u7EA6\u6362\u7B97" },
32600
+ { name: "okx_place_order", auth: "API Key (\u4EA4\u6613)", params: "instId, tdMode, side, sz, ordType, px?", what: "\u6807\u51C6\u4E0B\u5355 \u2014 WRITE\uFF0C\u9700\u786E\u8BA4" },
32601
+ { name: "okx_batch_orders", auth: "API Key (\u4EA4\u6613)", params: "orders[]", what: "\u6279\u91CF\u4E0B\u5355 \u2014 WRITE" },
32602
+ { name: "okx_cancel_order", auth: "API Key (\u4EA4\u6613)", params: "instId, ordId", what: "\u64A4\u9500\u5355\u7B14\u8BA2\u5355 \u2014 WRITE" },
32603
+ { name: "okx_batch_cancel_orders", auth: "API Key (\u4EA4\u6613)", params: "orders[]", what: "\u6279\u91CF\u64A4\u5355 \u2014 WRITE" },
32604
+ { name: "okx_amend_order", auth: "API Key (\u4EA4\u6613)", params: "instId, ordId, newSz?, newPx?", what: "\u4FEE\u6539\u672A\u6210\u4EA4\u8BA2\u5355 \u2014 WRITE" },
32605
+ { name: "okx_amend_batch_orders", auth: "API Key (\u4EA4\u6613)", params: "orders[]", what: "\u6279\u91CF\u6539\u5355 \u2014 WRITE" },
32606
+ { name: "okx_close_position", auth: "API Key (\u4EA4\u6613)", params: "instId, posSide, mgnMode", what: "\u5E73\u4ED3\uFF08\u7528 _actionContext \u4E2D\u53C2\u6570\uFF09\u2014 WRITE\uFF0C\u9700\u786E\u8BA4" },
32607
+ { name: "okx_get_order", auth: "API Key", params: "instId, ordId", what: "\u67E5\u8BE2\u5355\u7B14\u8BA2\u5355\u72B6\u6001" },
32608
+ { name: "okx_get_orders_pending", auth: "API Key", params: "instType?, instId?", what: "\u5F53\u524D\u6302\u5355\u5217\u8868" },
32609
+ { name: "okx_get_orders_history", auth: "API Key", params: "instType, limit?", what: "\u5386\u53F2\u8BA2\u5355\uFF08\u8FD13\u4E2A\u6708\uFF09" },
32610
+ { name: "okx_get_fills", auth: "API Key", params: "instType?, instId?, ordId?", what: "\u6700\u8FD1\u6210\u4EA4\u660E\u7EC6" },
32611
+ { name: "okx_get_fills_history", auth: "API Key", params: "instType?, instId?, limit?", what: "\u5386\u53F2\u6210\u4EA4\u660E\u7EC6" }
32612
+ ]
32613
+ },
32614
+ "\u98CE\u9669\u98CE\u63A7": {
32615
+ workflow: "agent_risk_overview\uFF08\u5168\u666F\uFF09\u2192 \u53D1\u73B0\u98CE\u9669\u4ED3\u4F4D \u2192 agent_quick_trade \u5E73\u4ED3",
32616
+ tools: [
32617
+ { name: "agent_risk_overview", auth: "API Key", params: "\u65E0", what: "\u5168\u4ED3\u98CE\u9669\u4EEA\u8868\u76D8\uFF1A\u98CE\u9669\u6392\u5E8F+\u5F3A\u5E73\u9884\u8B66+\u4FDD\u8BC1\u91D1\u7387" },
32618
+ { name: "okx_get_account_position_risk", auth: "API Key", params: "\u65E0", what: "\u8D26\u6237\u6301\u4ED3\u98CE\u9669\u8BE6\u60C5" },
32619
+ { name: "okx_get_risk_state", auth: "API Key", params: "\u65E0", what: "\u8D26\u6237\u98CE\u63A7\u72B6\u6001" },
32620
+ { name: "okx_get_margin_balance", auth: "API Key", params: "instId, mgnMode", what: "\u67D0\u4EA7\u54C1\u7684\u4FDD\u8BC1\u91D1\u4F59\u989D" },
32621
+ { name: "okx_get_positions", auth: "API Key", params: "instType?", what: "\u6301\u4ED3\u4E2D\u7684 liqPx/mgnRatio \u5B57\u6BB5\u53EF\u76F4\u63A5\u8BC4\u4F30\u98CE\u9669" },
32622
+ { name: "okx_get_max_withdrawal", auth: "API Key", params: "ccy?", what: "\u6700\u5927\u53EF\u63D0\u73B0\u989D\u5EA6" }
32623
+ ]
32624
+ },
32625
+ "\u5E02\u573A\u626B\u63CF": {
32626
+ workflow: "agent_market_scan\uFF08\u5F02\u52A8\uFF09\u2192 okx_quick_market\uFF08\u6DF1\u5165\uFF09\u2192 \u4E0B\u5355",
32627
+ tools: [
32628
+ { name: "agent_market_scan", auth: "\u516C\u5F00", params: "instType?, topN?, sortBy?", what: "\u6DA8\u5E45\u699C/\u8DCC\u5E45\u699C/\u6210\u4EA4\u91CF/\u8D44\u91D1\u8D39\u7387\u5F02\u5E38\u4E00\u952E\u626B\u63CF" },
32629
+ { name: "okx_get_tickers", auth: "\u516C\u5F00", params: "instType", what: "\u67D0\u7C7B\u578B\u5168\u90E8\u4EA7\u54C1\u884C\u60C5\uFF08\u53EF\u81EA\u884C\u6392\u5E8F\u8FC7\u6EE4\uFF09" },
32630
+ { name: "okx_get_funding_rate", auth: "\u516C\u5F00", params: "instId", what: "\u5355\u4EA7\u54C1\u8D44\u91D1\u8D39\u7387" },
32631
+ { name: "okx_get_funding_rate_history", auth: "\u516C\u5F00", params: "instId", what: "\u8D44\u91D1\u8D39\u7387\u5386\u53F2\u8D8B\u52BF" }
32632
+ ]
32633
+ },
32634
+ "\u76C8\u4E8F\u590D\u76D8": {
32635
+ workflow: "agent_pnl_report\uFF08\u76C8\u4E8F\u62A5\u544A\uFF09\u2192 \u9700\u8981\u65F6\u67E5 okx_get_fills \u660E\u7EC6",
32636
+ tools: [
32637
+ { name: "agent_pnl_report", auth: "API Key", params: "days?", what: "\u6D6E\u52A8\u76C8\u4E8F+\u5DF2\u5B9E\u73B0\u76C8\u4E8F \u53CC\u7EF4\u65E5\u62A5" },
32638
+ { name: "okx_get_positions", auth: "API Key", params: "instType?", what: "\u5F53\u524D\u6301\u4ED3\u6D6E\u52A8\u76C8\u4E8F" },
32639
+ { name: "okx_get_positions_history", auth: "API Key", params: "instType?, instId?", what: "\u5386\u53F2\u5E73\u4ED3\u76C8\u4E8F" },
32640
+ { name: "okx_get_orders_history", auth: "API Key", params: "instType", what: "\u5386\u53F2\u8BA2\u5355\u76C8\u4E8F" },
32641
+ { name: "okx_get_fills", auth: "API Key", params: "instType?, instId?", what: "\u9010\u7B14\u6210\u4EA4\u660E\u7EC6\uFF08\u53EF\u6309 pnl \u5B57\u6BB5\u6C42\u548C\uFF09" }
32642
+ ]
32643
+ },
32644
+ "\u8D44\u91D1\u7BA1\u7406": {
32645
+ workflow: "\u5148\u770B\u4F59\u989D \u2192 \u518D\u51B3\u5B9A\u5212\u8F6C/\u63D0\u73B0/\u5145\u503C",
32646
+ tools: [
32647
+ { name: "okx_get_balance", auth: "API Key", params: "ccy?", what: "\u4EA4\u6613\u8D26\u6237\u4F59\u989D" },
32648
+ { name: "okx_get_funding_balance", auth: "API Key", params: "ccy?", what: "\u8D44\u91D1\u8D26\u6237\u4F59\u989D" },
32649
+ { name: "okx_transfer", auth: "API Key (\u4EA4\u6613)", params: "ccy, amt, from, to", what: "\u8D44\u91D1\u5212\u8F6C \u2014 FUND_TRANSFER\uFF0C\u9700\u786E\u8BA4" },
32650
+ { name: "okx_get_transfer_state", auth: "API Key", params: "transId?", what: "\u5212\u8F6C\u72B6\u6001\u67E5\u8BE2" },
32651
+ { name: "okx_withdrawal", auth: "API Key (\u4EA4\u6613)", params: "ccy, amt, dest, toAddr", what: "\u63D0\u73B0 \u2014 FUND_TRANSFER\uFF0C\u9700\u786E\u8BA4" },
32652
+ { name: "okx_get_deposit_address", auth: "API Key", params: "ccy", what: "\u83B7\u53D6\u5145\u503C\u5730\u5740" },
32653
+ { name: "okx_get_deposit_history", auth: "API Key", params: "ccy?, limit?", what: "\u5145\u503C\u8BB0\u5F55" },
32654
+ { name: "okx_get_withdrawal_history", auth: "API Key", params: "ccy?, limit?", what: "\u63D0\u73B0\u8BB0\u5F55" },
32655
+ { name: "okx_get_currencies", auth: "API Key", params: "ccy?", what: "\u94FE\u4E0A\u5E01\u79CD\u4FE1\u606F" },
32656
+ { name: "okx_convert_trade", auth: "API Key (\u4EA4\u6613)", params: "fromCcy, toCcy, sz", what: "\u4E00\u952E\u5151\u6362 \u2014 FUND_TRANSFER\uFF0C\u9700\u786E\u8BA4" },
32657
+ { name: "okx_get_convert_currencies", auth: "API Key", params: "\u65E0", what: "\u53EF\u5151\u6362\u5E01\u79CD\u5217\u8868" },
32658
+ { name: "okx_get_max_withdrawal", auth: "API Key", params: "ccy?", what: "\u6700\u5927\u53EF\u63D0\u73B0\u6570\u91CF" },
32659
+ { name: "okx_get_non_tradable_assets", auth: "API Key", params: "ccy?", what: "\u4E0D\u53EF\u4EA4\u6613\u8D44\u4EA7" },
32660
+ { name: "okx_get_exchange_list", auth: "API Key", params: "\u65E0", what: "\u4EA4\u6613\u6240\u5217\u8868" },
32661
+ { name: "okx_get_deposit_withdraw_status", auth: "API Key", params: "\u65E0", what: "\u5145\u63D0\u72B6\u6001" }
32662
+ ]
32663
+ },
32664
+ "\u7B56\u7565\u4EA4\u6613": {
32665
+ workflow: "\u7F51\u683C/\u8DDF\u5355/\u4FE1\u53F7/\u7B56\u7565\u59D4\u6258 \u5404\u81EA\u72EC\u7ACB\uFF0C\u6309\u9700\u8C03\u7528",
32666
+ tools: [
32667
+ { name: "okx_get_grid_ai_param", auth: "API Key", params: "instType, algoOrdType", what: "\u7F51\u683CAI\u53C2\u6570\u63A8\u8350" },
32668
+ { name: "okx_create_grid_order", auth: "API Key (\u4EA4\u6613)", params: "instId, algoOrdType, maxPx, minPx, gridNum, sz", what: "\u521B\u5EFA\u7F51\u683C \u2014 WRITE" },
32669
+ { name: "okx_stop_grid_order", auth: "API Key (\u4EA4\u6613)", params: "algoId, instId, algoOrdType, stopType", what: "\u505C\u6B62\u7F51\u683C" },
32670
+ { name: "okx_close_grid_position", auth: "API Key (\u4EA4\u6613)", params: "algoId, mktClose", what: "\u5E73\u4ED3\u7F51\u683C\u6240\u6709\u6301\u4ED3" },
32671
+ { name: "okx_get_grid_positions", auth: "API Key (\u4EA4\u6613)", params: "algoOrdType, algoId?", what: "\u7F51\u683C\u6301\u4ED3" },
32672
+ { name: "okx_get_grid_sub_orders", auth: "API Key", params: "algoId, algoOrdType, type", what: "\u7F51\u683C\u5B50\u8BA2\u5355" },
32673
+ { name: "okx_get_grid_orders_pending", auth: "API Key", params: "algoOrdType, algoId?", what: "\u8FD0\u884C\u4E2D\u7684\u7F51\u683C" },
32674
+ { name: "okx_get_grid_orders_history", auth: "API Key", params: "algoOrdType, algoId?", what: "\u5386\u53F2\u7F51\u683C" },
32675
+ { name: "okx_create_recurring_plan", auth: "API Key (\u4EA4\u6613)", params: "instId, sz, period, recurringList", what: "\u5B9A\u6295\u8BA1\u5212 \u2014 WRITE" },
32676
+ { name: "okx_stop_recurring_plan", auth: "API Key (\u4EA4\u6613)", params: "algoId", what: "\u505C\u6B62\u5B9A\u6295" },
32677
+ { name: "okx_copy_trader", auth: "API Key (\u4EA4\u6613)", params: "uniqueCode, copyTotalAmt, instType", what: "\u5F00\u59CB\u8DDF\u5355 \u2014 WRITE\uFF0C\u9700\u786E\u8BA4" },
32678
+ { name: "okx_get_copy_traders", auth: "API Key", params: "\u65E0", what: "\u6211\u5173\u6CE8\u7684\u4EA4\u6613\u5458" },
32679
+ { name: "okx_get_public_lead_traders", auth: "\u516C\u5F00", params: "instType, sortType?", what: "\u516C\u5F00\u4EA4\u6613\u5458\u5217\u8868" },
32680
+ { name: "okx_get_lead_trader_positions", auth: "API Key", params: "uniqueCode", what: "\u4EA4\u6613\u5458\u5F53\u524D\u6301\u4ED3" },
32681
+ { name: "okx_get_lead_trader_stats", auth: "API Key", params: "uniqueCode", what: "\u4EA4\u6613\u5458\u4E1A\u7EE9\u7EDF\u8BA1" },
32682
+ { name: "okx_place_algo_order", auth: "API Key (\u4EA4\u6613)", params: "instId, tdMode, side, ordType, sz, triggerPx", what: "\u7B56\u7565\u59D4\u6258\u4E0B\u5355 \u2014 WRITE" },
32683
+ { name: "okx_get_algo_orders", auth: "API Key", params: "ordType?, algoId?", what: "\u7B56\u7565\u59D4\u6258\u5217\u8868" },
32684
+ { name: "okx_cancel_algo_order", auth: "API Key (\u4EA4\u6613)", params: "algoId, instId", what: "\u64A4\u9500\u7B56\u7565\u59D4\u6258 \u2014 WRITE" }
32685
+ ]
32686
+ },
32687
+ "\u9884\u6D4B\u5E02\u573A": {
32688
+ workflow: "okx_event_instruments \u770B\u5E02\u573A \u2192 okx_event_place_order \u4E0B\u5355",
32689
+ tools: [
32690
+ { name: "okx_event_instruments", auth: "\u516C\u5F00", params: "eventType?", what: "\u4E8B\u4EF6\u5408\u7EA6\u4EA7\u54C1\u5217\u8868" },
32691
+ { name: "okx_event_place_order", auth: "API Key (\u4EA4\u6613)", params: "instId, side, sz, ordType?", what: "\u4E8B\u4EF6\u5408\u7EA6\u4E0B\u5355 \u2014 WRITE" },
32692
+ { name: "okx_event_cancel_order", auth: "API Key (\u4EA4\u6613)", params: "instId, ordId", what: "\u4E8B\u4EF6\u5408\u7EA6\u64A4\u5355 \u2014 WRITE" },
32693
+ { name: "okx_event_amend_order", auth: "API Key (\u4EA4\u6613)", params: "instId, ordId, newSz?", what: "\u4E8B\u4EF6\u5408\u7EA6\u6539\u5355 \u2014 WRITE" },
32694
+ { name: "okx_event_fills", auth: "API Key", params: "instId?", what: "\u4E8B\u4EF6\u5408\u7EA6\u6210\u4EA4\u8BB0\u5F55" },
32695
+ { name: "okx_predictions_place_order", auth: "API Key (\u4EA4\u6613)", params: "marketId, side, price, size", what: "\u9884\u6D4B\u5E02\u573A\u4E0B\u5355 \u2014 WRITE" },
32696
+ { name: "okx_predictions_positions", auth: "API Key", params: "marketId?", what: "\u9884\u6D4B\u5E02\u573A\u6301\u4ED3" },
32697
+ { name: "okx_predictions_order_list", auth: "API Key", params: "marketId?, status?", what: "\u9884\u6D4B\u5E02\u573A\u8BA2\u5355\u5217\u8868" },
32698
+ { name: "okx_predictions_redeem", auth: "API Key (\u4EA4\u6613)", params: "marketId, outcome", what: "\u9884\u6D4B\u5E02\u573A\u8D4E\u56DE \u2014 WRITE" },
32699
+ { name: "okx_predictions_balance", auth: "API Key", params: "\u65E0", what: "\u9884\u6D4B\u5E02\u573A\u4F59\u989D" }
32700
+ ]
32701
+ },
32702
+ "\u6280\u672F\u6307\u6807": {
32703
+ workflow: "okx_indicator \u5355\u6307\u6807\u8BA1\u7B97 \u2192 \u9700\u8981\u7EFC\u5408\u5224\u65AD\u65F6 okx_indicator_batch \u6279\u91CF\u8BA1\u7B97",
32704
+ tools: [
32705
+ { name: "okx_indicator", auth: "\u516C\u5F00", params: "instId, indicator, period?, bar?", what: "\u5355\u6307\u6807\u8BA1\u7B97+Agent\u4FE1\u53F7\u89E3\u8BFB\uFF0817\u79CD\u6307\u6807\u542BRSI/MACD/BB/ATR/ADX\u7B49\uFF09" },
32706
+ { name: "okx_indicator_batch", auth: "\u516C\u5F00", params: "instId, indicators(\u9017\u53F7\u5206\u9694), bar?", what: "\u591A\u6307\u6807\u6279\u91CF\u8BA1\u7B97+\u7EFC\u5408\u4FE1\u53F7\u5171\u8BC6\uFF08\u6700\u591A10\u4E2A\u6307\u6807\uFF09" }
32707
+ ]
32708
+ },
32709
+ "\u806A\u660E\u94B1": {
32710
+ workflow: "okx_smart_leaderboard \u770B\u6392\u884C \u2192 okx_smart_trader_detail \u6DF1\u6316 \u2192 okx_smart_sentiment \u770B\u60C5\u7EEA",
32711
+ tools: [
32712
+ { name: "okx_smart_leaderboard", auth: "\u516C\u5F00", params: "instType?, sortBy?, topN?", what: "\u4EA4\u6613\u5458\u6392\u884C\u699C\uFF08\u6309\u6536\u76CA\u7387/\u603B\u6536\u76CA/\u8DDF\u5355\u4EBA\u6570\u6392\u5E8F\uFF09" },
32713
+ { name: "okx_smart_trader_detail", auth: "\u516C\u5F00", params: "uniqueCode, instType?", what: "\u5355\u4EA4\u6613\u5458\u5168\u666F\uFF1A\u6536\u76CA\u7387+\u80DC\u7387+\u56DE\u64A4+\u6301\u4ED3+PnL\u66F2\u7EBF" },
32714
+ { name: "okx_smart_sentiment", auth: "\u516C\u5F00", params: "instFamily?", what: "\u5E02\u573A\u60C5\u7EEA\u4EEA\u8868\u76D8\uFF1A\u591A\u7A7A\u6BD4+PCR+\u8D44\u91D1\u8D39\u7387 \u2192 \u91CF\u5316\u8BC4\u5206(0-100)" }
32715
+ ]
32716
+ },
32717
+ "WebSocket \u5B9E\u65F6": {
32718
+ workflow: "okx_ws_subscribe \u8BA2\u9605\u9891\u9053 \u2192 okx_ws_events \u62C9\u53D6\u4E8B\u4EF6",
32719
+ tools: [
32720
+ { name: "okx_ws_subscribe", auth: "\u516C\u5F00", params: "channels[]", what: "\u8BA2\u9605\u5B9E\u65F6\u9891\u9053\uFF08tickers/trades/books/candles/fundingRate \u7B49\uFF09" },
32721
+ { name: "okx_ws_events", auth: "\u516C\u5F00", params: "channel?", what: "\u6536\u53D6\u8BA2\u9605\u7684\u5B9E\u65F6\u63A8\u9001\u4E8B\u4EF6" },
32722
+ { name: "okx_ws_status", auth: "\u516C\u5F00", params: "\u65E0", what: "\u5F53\u524D\u8BA2\u9605\u72B6\u6001" },
32723
+ { name: "okx_ws_close", auth: "\u516C\u5F00", params: "channel?", what: "\u53D6\u6D88\u8BA2\u9605" }
32724
+ ]
32725
+ },
32726
+ "\u6A21\u62DF\u4F30\u7B97": {
32727
+ workflow: "agent_simulate_order\uFF08\u9884\u4F30\uFF09\u2192 \u7528\u6237\u786E\u8BA4 \u2192 agent_quick_trade\uFF08\u771F\u5B9E\u4E0B\u5355\uFF09",
32728
+ tools: [
32729
+ { name: "agent_simulate_order", auth: "API Key (\u53EA\u8BFB)", params: "instId, side, sz, tdMode, px?", what: "\u6A21\u62DF\u4E0B\u5355\uFF1A\u6210\u4EA4\u4EF7/\u6ED1\u70B9/\u624B\u7EED\u8D39/\u8D44\u91D1\u5360\u7528 \u5168\u9884\u4F30" },
32730
+ { name: "okx_preflight_check", auth: "API Key (\u53EA\u8BFB)", params: "instId, tdMode, sz, px?, side?, ordType?", what: "\u4E0B\u5355\u9884\u68C0\uFF1A\u6700\u5927\u53EF\u5F00/\u9650\u4EF7/\u5408\u7EA6\u6362\u7B97" },
32731
+ { name: "okx_position_builder", auth: "API Key (\u4EA4\u6613)", params: "body(JSON)", what: "\u7EC4\u5408\u4FDD\u8BC1\u91D1\u8BD5\u7B97 \u2014 WRITE" }
32732
+ ]
32733
+ },
32734
+ "\u7CFB\u7EDF\u5DE5\u5177": {
32735
+ workflow: "\u6309\u9700\u8C03\u7528\uFF0C\u5404\u5DE5\u5177\u72EC\u7ACB",
32736
+ tools: [
32737
+ { name: "agent_catalog", auth: "\u516C\u5F00", params: "\u65E0", what: "\u4F60\u6B63\u5728\u770B\u7684\u8FD9\u4E2A" },
32738
+ { name: "agent_catalog_detail", auth: "\u516C\u5F00", params: "domain", what: "\u67E5\u770B\u67D0\u4E2A\u57DF\u7684\u8BE6\u7EC6\u5DE5\u5177\u6E05\u5355" },
32739
+ { name: "agent_get_preference", auth: "\u516C\u5F00", params: "key?", what: "\u8BFB\u53D6 Agent \u6301\u4E45\u5316\u504F\u597D" },
32740
+ { name: "agent_set_preference", auth: "\u516C\u5F00", params: "key, value", what: "\u4FDD\u5B58 Agent \u504F\u597D" },
32741
+ { name: "okx_agent_feedback", auth: "\u516C\u5F00", params: "title, what, tools, pain, suggestion", what: "\u63D0\u4EA4\u4F7F\u7528\u53CD\u9988" },
32742
+ { name: "agent_hub_status", auth: "\u516C\u5F00", params: "\u65E0", what: "Agent \u96C6\u7FA4\u72B6\u6001" },
32743
+ { name: "agent_hub_dispatch", auth: "\u516C\u5F00", params: "taskId, agentId?", what: "\u6D3E\u53D1\u4EFB\u52A1\u7ED9 Agent" },
32744
+ { name: "agent_hub_review", auth: "\u516C\u5F00", params: "taskId, verdict, feedback?", what: "\u5BA1\u6838 Agent \u63D0\u4EA4\u7ED3\u679C" },
32745
+ { name: "agent_room_send", auth: "\u516C\u5F00", params: "roomId, text", what: "\u53D1\u9001\u623F\u95F4\u6D88\u606F" },
32746
+ { name: "agent_room_view", auth: "\u516C\u5F00", params: "roomId?, limit?", what: "\u67E5\u770B\u623F\u95F4\u6D88\u606F\u5386\u53F2" },
32747
+ { name: "xlayer_subscribe", auth: "\u516C\u5F00", params: "channel, address?", what: "X Layer \u94FE\u4E0A\u4E8B\u4EF6\u8BA2\u9605" },
32748
+ { name: "xlayer_call", auth: "\u516C\u5F00", params: "to, data, value?", what: "X Layer \u5408\u7EA6\u8C03\u7528 \u2014 WRITE" },
32749
+ { name: "xlayer_get_events", auth: "\u516C\u5F00", params: "channel?", what: "X Layer \u94FE\u4E0A\u4E8B\u4EF6" },
32750
+ { name: "okx_get_system_status", auth: "\u516C\u5F00", params: "\u65E0", what: "OKX \u7CFB\u7EDF\u72B6\u6001" },
32751
+ { name: "okx_get_instruments", auth: "\u516C\u5F00", params: "instType, instId?", what: "\u4EA7\u54C1\u5217\u8868/\u8BE6\u60C5" }
32752
+ ]
32753
+ }
32754
+ };
32755
+ server.tool(
32756
+ "agent_catalog_detail",
32757
+ "CAT:[\u7CFB\u7EDF] | ## \u529F\u80FD\uFF1A\u67E5\u770B\u67D0\u4E2A\u4E1A\u52A1\u57DF\u7684\u8BE6\u7EC6\u5DE5\u5177\u6E05\u5355\u2014\u2014\u542B\u6BCF\u4E2A\u5DE5\u5177\u7684\u53C2\u6570\u63D0\u793A\u3001\u9274\u6743\u8981\u6C42\u3001\u63A8\u8350\u8C03\u7528\u987A\u5E8F\n## \u573A\u666F\uFF1AAgent \u5728 agent_catalog \u786E\u5B9A\u57DF\u540E\uFF0C\u8C03\u7528\u6B64\u5DE5\u5177\u83B7\u53D6\u8BE5\u57DF\u6240\u6709\u5DE5\u5177\u7684\u7CBE\u51C6\u4FE1\u606F\u3001\u53C2\u6570\u63D0\u793A\u548C\u5178\u578B workflow\n## \u5173\u952E\u8BCD\uFF1A\u76EE\u5F55\u8BE6\u60C5, catalog detail, \u5DE5\u5177\u6E05\u5355, \u57DF\u8BE6\u60C5, workflow\n## \u53C2\u6570\uFF1A\n## - domain: \u57DF\u540D\u79F0\u3002\u53EF\u53D6\u503C: \u8D26\u6237\u8D44\u4EA7 | \u884C\u60C5\u770B\u76D8 | \u4E0B\u5355\u4EA4\u6613 | \u98CE\u9669\u98CE\u63A7 | \u5E02\u573A\u626B\u63CF | \u76C8\u4E8F\u590D\u76D8 | \u8D44\u91D1\u7BA1\u7406 | \u7B56\u7565\u4EA4\u6613 | \u9884\u6D4B\u5E02\u573A | WebSocket \u5B9E\u65F6 | \u6A21\u62DF\u4F30\u7B97 | \u7CFB\u7EDF\u5DE5\u5177\n## \u9274\u6743\uFF1APUBLIC \u2014 \u7EAF\u7D22\u5F15\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~2KB \u2014 \u5355\u57DF\u8BE6\u60C5\n## \u5173\u8054\uFF1Aagent_catalog \u9009\u57DF \u2192 \u672C\u5DE5\u5177\u83B7\u53D6\u8BE6\u60C5 \u2192 \u76F4\u63A5\u8C03\u7528\u76EE\u6807\u5DE5\u5177",
32758
+ {
32759
+ domain: external_exports.string().describe("\u57DF\u540D\u79F0\u3002\u53EF\u9009: \u8D26\u6237\u8D44\u4EA7, \u884C\u60C5\u770B\u76D8, \u6280\u672F\u6307\u6807, \u4E0B\u5355\u4EA4\u6613, \u98CE\u9669\u98CE\u63A7, \u5E02\u573A\u626B\u63CF, \u806A\u660E\u94B1, \u76C8\u4E8F\u590D\u76D8, \u8D44\u91D1\u7BA1\u7406, \u7B56\u7565\u4EA4\u6613, \u9884\u6D4B\u5E02\u573A, WebSocket \u5B9E\u65F6, \u6A21\u62DF\u4F30\u7B97, \u7CFB\u7EDF\u5DE5\u5177")
32760
+ },
32761
+ async ({ domain }) => {
32762
+ try {
32763
+ const detail = DOMAIN_DETAILS[domain];
32764
+ if (!detail) {
32765
+ return toResult({
32766
+ found: false,
32767
+ domain,
32768
+ availableDomains: Object.keys(DOMAIN_DETAILS),
32769
+ hint: "\u8BF7\u4ECE availableDomains \u4E2D\u9009\u62E9\u4E00\u4E2A\u57DF\uFF0C\u6216\u8C03 agent_catalog \u67E5\u770B\u5B8C\u6574\u5BFC\u822A",
32770
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
32771
+ });
32772
+ }
32773
+ return toResult({
32774
+ found: true,
32775
+ domain,
32776
+ ...detail,
32777
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
32778
+ });
32779
+ } catch (e) {
32780
+ return toError(e);
32781
+ }
32782
+ }
32783
+ );
32784
+ }
32785
+
32786
+ // src/tools/indicators.ts
32787
+ async function fetchCandles(instId, bar, minBars) {
32788
+ const limit = Math.min(300, Math.max(minBars + 50, 100));
32789
+ const raw = await publicApi.getCandles(instId, bar, limit);
32790
+ if (!raw || raw.length === 0) return [];
32791
+ return raw.map((r) => ({
32792
+ ts: parseInt(r[0]),
32793
+ o: parseFloat(r[1]),
32794
+ h: parseFloat(r[2]),
32795
+ l: parseFloat(r[3]),
32796
+ c: parseFloat(r[4]),
32797
+ vol: parseFloat(r[5])
32798
+ })).reverse();
32799
+ }
32800
+ function calcSMA(data, period) {
32801
+ const result = [];
32802
+ for (let i = period - 1; i < data.length; i++) {
32803
+ let sum = 0;
32804
+ for (let j = 0; j < period; j++) sum += data[i - j];
32805
+ result.push(sum / period);
32806
+ }
32807
+ return result;
32808
+ }
32809
+ function calcEMA(data, period) {
32810
+ const result = [];
32811
+ const k = 2 / (period + 1);
32812
+ let ema = data[0];
32813
+ result.push(ema);
32814
+ for (let i = 1; i < data.length; i++) {
32815
+ ema = data[i] * k + ema * (1 - k);
32816
+ result.push(ema);
32817
+ }
32818
+ return result.slice(period - 1);
32819
+ }
32820
+ function calcRSI(closes, period = 14) {
32821
+ const rsi = [];
32822
+ const gains = [], losses = [];
32823
+ for (let i = 1; i < closes.length; i++) {
32824
+ const diff = closes[i] - closes[i - 1];
32825
+ gains.push(diff > 0 ? diff : 0);
32826
+ losses.push(diff < 0 ? -diff : 0);
32827
+ }
32828
+ for (let i = period; i < gains.length; i++) {
32829
+ const avgGain = gains.slice(i - period, i).reduce((a, b) => a + b, 0) / period;
32830
+ const avgLoss = losses.slice(i - period, i).reduce((a, b) => a + b, 0) / period;
32831
+ rsi.push(avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss));
32832
+ }
32833
+ return rsi;
32834
+ }
32835
+ function calcMACD(closes, fast = 12, slow = 26, signal = 9) {
32836
+ const emaFast = calcEMA(closes, fast);
32837
+ const emaSlow = calcEMA(closes, slow);
32838
+ const offset = emaFast.length - emaSlow.length;
32839
+ const macd = [];
32840
+ for (let i = 0; i < emaSlow.length; i++) macd.push(emaFast[i + offset] - emaSlow[i]);
32841
+ const sigLine = calcEMA(macd, signal);
32842
+ const histOffset = macd.length - sigLine.length;
32843
+ const histogram = [];
32844
+ for (let i = 0; i < sigLine.length; i++) histogram.push(macd[i + histOffset] - sigLine[i]);
32845
+ return { macd: macd.slice(-sigLine.length), signal: sigLine, histogram };
32846
+ }
32847
+ function calcBollinger(closes, period = 20, mult = 2) {
32848
+ const sma = calcSMA(closes, period);
32849
+ const upper = [], lower = [], bandwidth = [], pctB = [];
32850
+ for (let i = 0; i < sma.length; i++) {
32851
+ const idx = i + period - 1;
32852
+ let sumSq = 0;
32853
+ for (let j = 0; j < period; j++) sumSq += (closes[idx - j] - sma[i]) ** 2;
32854
+ const std = Math.sqrt(sumSq / period);
32855
+ const up = sma[i] + mult * std;
32856
+ const lo = sma[i] - mult * std;
32857
+ upper.push(up);
32858
+ lower.push(lo);
32859
+ bandwidth.push((up - lo) / sma[i] * 100);
32860
+ pctB.push((closes[idx] - lo) / (up - lo));
32861
+ }
32862
+ return { upper, middle: sma, lower, bandwidth, pctB };
32863
+ }
32864
+ function calcATR(candles, period = 14) {
32865
+ const tr = [];
32866
+ for (let i = 1; i < candles.length; i++) {
32867
+ const h = candles[i].h, l = candles[i].l, pc = candles[i - 1].c;
32868
+ tr.push(Math.max(h - l, Math.abs(h - pc), Math.abs(l - pc)));
32869
+ }
32870
+ return calcSMA(tr, period);
32871
+ }
32872
+ function calcStoch(candles, kPeriod = 14, dPeriod = 3) {
32873
+ const k = [];
32874
+ for (let i = kPeriod - 1; i < candles.length; i++) {
32875
+ let highest = -Infinity, lowest = Infinity;
32876
+ for (let j = 0; j < kPeriod; j++) {
32877
+ highest = Math.max(highest, candles[i - j].h);
32878
+ lowest = Math.min(lowest, candles[i - j].l);
32879
+ }
32880
+ k.push((candles[i].c - lowest) / (highest - lowest) * 100);
32881
+ }
32882
+ const d = calcSMA(k, dPeriod);
32883
+ return { k: k.slice(dPeriod - 1), d };
32884
+ }
32885
+ function calcMFI(candles, period = 14) {
32886
+ const mfi = [];
32887
+ for (let i = period; i < candles.length; i++) {
32888
+ let posFlow = 0, negFlow = 0;
32889
+ for (let j = 0; j < period; j++) {
32890
+ const idx = i - j;
32891
+ const tp = (candles[idx].h + candles[idx].l + candles[idx].c) / 3;
32892
+ const prevTp = (candles[idx - 1].h + candles[idx - 1].l + candles[idx - 1].c) / 3;
32893
+ const rmf = tp * candles[idx].vol;
32894
+ if (tp > prevTp) posFlow += rmf;
32895
+ else negFlow += rmf;
32896
+ }
32897
+ mfi.push(negFlow === 0 ? 100 : 100 - 100 / (1 + posFlow / negFlow));
32898
+ }
32899
+ return mfi;
32900
+ }
32901
+ function calcWR(candles, period = 14) {
32902
+ const wr = [];
32903
+ for (let i = period - 1; i < candles.length; i++) {
32904
+ let highest = -Infinity, lowest = Infinity;
32905
+ for (let j = 0; j < period; j++) {
32906
+ highest = Math.max(highest, candles[i - j].h);
32907
+ lowest = Math.min(lowest, candles[i - j].l);
32908
+ }
32909
+ wr.push((highest - candles[i].c) / (highest - lowest) * -100);
32910
+ }
32911
+ return wr;
32912
+ }
32913
+ function calcCCI(candles, period = 20) {
32914
+ const tp = candles.map((c) => (c.h + c.l + c.c) / 3);
32915
+ const sma = calcSMA(tp, period);
32916
+ const cci = [];
32917
+ for (let i = 0; i < sma.length; i++) {
32918
+ const idx = i + period - 1;
32919
+ let sumDev = 0;
32920
+ for (let j = 0; j < period; j++) sumDev += Math.abs(tp[idx - j] - sma[i]);
32921
+ const mad = sumDev / period;
32922
+ cci.push(mad === 0 ? 0 : (tp[idx] - sma[i]) / (0.015 * mad));
32923
+ }
32924
+ return cci;
32925
+ }
32926
+ function calcOBV(candles) {
32927
+ const obv = [candles[0].vol];
32928
+ for (let i = 1; i < candles.length; i++) {
32929
+ if (candles[i].c > candles[i - 1].c) obv.push(obv[i - 1] + candles[i].vol);
32930
+ else if (candles[i].c < candles[i - 1].c) obv.push(obv[i - 1] - candles[i].vol);
32931
+ else obv.push(obv[i - 1]);
32932
+ }
32933
+ return obv;
32934
+ }
32935
+ function calcVWAP(candles) {
32936
+ const vwap = [];
32937
+ let cumPV = 0, cumV = 0;
32938
+ for (const c of candles) {
32939
+ const tp = (c.h + c.l + c.c) / 3;
32940
+ cumPV += tp * c.vol;
32941
+ cumV += c.vol;
32942
+ vwap.push(cumV === 0 ? c.c : cumPV / cumV);
32943
+ }
32944
+ return vwap;
32945
+ }
32946
+ function calcADX(candles, period = 14) {
32947
+ const tr = [], plusDM = [], minusDM = [];
32948
+ for (let i = 1; i < candles.length; i++) {
32949
+ const up = candles[i].h - candles[i - 1].h;
32950
+ const down = candles[i - 1].l - candles[i].l;
32951
+ tr.push(Math.max(candles[i].h - candles[i].l, Math.abs(candles[i].h - candles[i - 1].c), Math.abs(candles[i].l - candles[i - 1].c)));
32952
+ plusDM.push(up > down && up > 0 ? up : 0);
32953
+ minusDM.push(down > up && down > 0 ? down : 0);
32954
+ }
32955
+ const atrSm = calcEMASmooth(tr, period);
32956
+ const plusDISM = calcEMASmooth(plusDM, period);
32957
+ const minusDISM = calcEMASmooth(minusDM, period);
32958
+ const adx = [], pDI = [], mDI = [];
32959
+ for (let i = 0; i < atrSm.length; i++) {
32960
+ pDI.push(atrSm[i] === 0 ? 0 : plusDISM[i] / atrSm[i] * 100);
32961
+ mDI.push(atrSm[i] === 0 ? 0 : minusDISM[i] / atrSm[i] * 100);
32962
+ }
32963
+ const dx = [];
32964
+ for (let i = 0; i < pDI.length; i++) {
32965
+ const sum = pDI[i] + mDI[i];
32966
+ dx.push(sum === 0 ? 0 : Math.abs(pDI[i] - mDI[i]) / sum * 100);
32967
+ }
32968
+ const adxRaw = calcEMASmooth(dx, period);
32969
+ const offset2 = pDI.length - adxRaw.length;
32970
+ for (let i = 0; i < adxRaw.length; i++) {
32971
+ adx.push(adxRaw[i]);
32972
+ }
32973
+ return { adx, plusDI: pDI.slice(offset2), minusDI: mDI.slice(offset2) };
32974
+ }
32975
+ function calcEMASmooth(data, period) {
32976
+ if (data.length === 0) return [];
32977
+ const result = [data[0]];
32978
+ const k = 2 / (period + 1);
32979
+ for (let i = 1; i < data.length; i++) result.push(data[i] * k + result[i - 1] * (1 - k));
32980
+ return result.slice(period - 1);
32981
+ }
32982
+ function calcSupertrend(candles, period = 10, mult = 3) {
32983
+ const atr = calcATR(candles, period);
32984
+ const upper = [], lower = [], trend = [];
32985
+ const direction = [];
32986
+ for (let i = 0; i < atr.length; i++) {
32987
+ const idx = i + period - 1;
32988
+ const mid = (candles[idx].h + candles[idx].l) / 2;
32989
+ const up = mid - mult * atr[i];
32990
+ const lo = mid + mult * atr[i];
32991
+ if (i === 0) {
32992
+ upper.push(up);
32993
+ lower.push(lo);
32994
+ direction.push(candles[idx].c > lo ? 1 : -1);
32995
+ trend.push(candles[idx].c > lo ? lo : up);
32996
+ } else {
32997
+ const prevLo = lower[i - 1], prevUp = upper[i - 1];
32998
+ const newUp = mid - mult * atr[i];
32999
+ const newLo = mid + mult * atr[i];
33000
+ upper.push(candles[idx - 1].c > prevLo ? Math.max(newUp, upper[i - 1]) : newUp);
33001
+ lower.push(candles[idx - 1].c < prevUp ? Math.min(newLo, lower[i - 1]) : newLo);
33002
+ if (direction[i - 1] === 1) {
33003
+ direction.push(candles[idx].c > lower[i] ? 1 : -1);
33004
+ } else {
33005
+ direction.push(candles[idx].c < upper[i] ? -1 : 1);
33006
+ }
33007
+ trend.push(direction[i] === 1 ? lower[i] : upper[i]);
33008
+ }
33009
+ }
33010
+ return { trend, direction };
33011
+ }
33012
+ function calcCMF(candles, period = 20) {
33013
+ const cmf = [];
33014
+ for (let i = period - 1; i < candles.length; i++) {
33015
+ let mfVol = 0, totalVol = 0;
33016
+ for (let j = 0; j < period; j++) {
33017
+ const c = candles[i - j];
33018
+ const mf = (c.c - c.l - (c.h - c.c)) / (c.h - c.l || 1);
33019
+ mfVol += mf * c.vol;
33020
+ totalVol += c.vol;
33021
+ }
33022
+ cmf.push(totalVol === 0 ? 0 : mfVol / totalVol);
33023
+ }
33024
+ return cmf;
33025
+ }
33026
+ function calcKeltner(candles, period = 20, mult = 2) {
33027
+ const tp = candles.map((c) => (c.h + c.l + c.c) / 3);
33028
+ const middle = calcEMA(tp, period);
33029
+ const atr = calcATR(candles, period);
33030
+ const minLen = Math.min(middle.length, atr.length);
33031
+ const upper = [], lower = [];
33032
+ for (let i = 0; i < minLen; i++) {
33033
+ upper.push(middle[middle.length - minLen + i] + mult * atr[atr.length - minLen + i]);
33034
+ lower.push(middle[middle.length - minLen + i] - mult * atr[atr.length - minLen + i]);
33035
+ }
33036
+ return { upper, middle: middle.slice(-minLen), lower };
33037
+ }
33038
+ function detectDoji(candle) {
33039
+ const body = Math.abs(candle.c - candle.o);
33040
+ const range = candle.h - candle.l;
33041
+ return range > 0 && body / range < 0.1;
33042
+ }
33043
+ function detectHammer(candle) {
33044
+ const body = Math.abs(candle.c - candle.o);
33045
+ const upperWick = candle.h - Math.max(candle.o, candle.c);
33046
+ const lowerWick = Math.min(candle.o, candle.c) - candle.l;
33047
+ return body > 0 && lowerWick > body * 2 && upperWick < body * 0.5;
33048
+ }
33049
+ function detectEngulfing(prev, curr) {
33050
+ const prevBody = Math.abs(prev.c - prev.o);
33051
+ const currBody = Math.abs(curr.c - curr.o);
33052
+ if (currBody < prevBody * 1.2) return null;
33053
+ if (curr.c > curr.o && prev.c < prev.o) return "bullish";
33054
+ if (curr.c < curr.o && prev.c > prev.o) return "bearish";
33055
+ return null;
33056
+ }
33057
+ function rsiSignal(rsi) {
33058
+ if (rsi > 70) return { signal: "bearish", message: `RSI \u8D85\u4E70 (${rsi.toFixed(1)} > 70)\uFF0C\u8FD1\u671F\u6709\u56DE\u8C03\u98CE\u9669\uFF0C\u4E0D\u5B9C\u8FFD\u591A` };
33059
+ if (rsi < 30) return { signal: "bullish", message: `RSI \u8D85\u5356 (${rsi.toFixed(1)} < 30)\uFF0C\u53EF\u80FD\u51FA\u73B0\u6280\u672F\u6027\u53CD\u5F39\uFF0C\u4E0D\u5B9C\u8FFD\u7A7A` };
33060
+ if (rsi > 60) return { signal: "bullish", message: `RSI \u504F\u5F3A (${rsi.toFixed(1)})\uFF0C\u591A\u5934\u52A8\u80FD\u6301\u7EED` };
33061
+ if (rsi < 40) return { signal: "bearish", message: `RSI \u504F\u5F31 (${rsi.toFixed(1)})\uFF0C\u7A7A\u5934\u52A8\u80FD\u6301\u7EED` };
33062
+ return { signal: "neutral", message: `RSI \u4E2D\u6027 (${rsi.toFixed(1)})\uFF0C\u65E0\u660E\u663E\u8D85\u4E70\u8D85\u5356\u4FE1\u53F7` };
33063
+ }
33064
+ function macdSignal(hist, prevHist) {
33065
+ if (hist > 0 && prevHist <= 0) return { signal: "bullish", message: "MACD \u91D1\u53C9\uFF0C\u591A\u5934\u4FE1\u53F7\u542F\u52A8" };
33066
+ if (hist < 0 && prevHist >= 0) return { signal: "bearish", message: "MACD \u6B7B\u53C9\uFF0C\u7A7A\u5934\u4FE1\u53F7\u542F\u52A8" };
33067
+ if (hist > 0) return { signal: "bullish", message: `MACD \u67F1 ${hist.toFixed(4)}\uFF0C\u591A\u5934\u533A\u57DF\u8FD0\u884C` };
33068
+ return { signal: "bearish", message: `MACD \u67F1 ${hist.toFixed(4)}\uFF0C\u7A7A\u5934\u533A\u57DF\u8FD0\u884C` };
33069
+ }
33070
+ function stochSignal(k, d) {
33071
+ if (k > 80 && d > 80) return { signal: "bearish", message: `KD \u8D85\u4E70 (K:${k.toFixed(1)}, D:${d.toFixed(1)})\uFF0C\u5173\u6CE8\u6B7B\u53C9` };
33072
+ if (k < 20 && d < 20) return { signal: "bullish", message: `KD \u8D85\u5356 (K:${k.toFixed(1)}, D:${d.toFixed(1)})\uFF0C\u5173\u6CE8\u91D1\u53C9` };
33073
+ return { signal: "neutral", message: `KD \u4E2D\u6027\u533A\u57DF (K:${k.toFixed(1)}, D:${d.toFixed(1)})` };
33074
+ }
33075
+ function adxSignal(adx, plusDI, minusDI) {
33076
+ if (adx > 40) {
33077
+ if (plusDI > minusDI) return { signal: "bullish", message: `ADX \u5F3A\u52BF\u8D8B\u52BF (${adx.toFixed(1)})\uFF0C+DI \u4E3B\u5BFC\uFF0C\u591A\u5934\u8D8B\u52BF\u5F3A\u52B2` };
33078
+ return { signal: "bearish", message: `ADX \u5F3A\u52BF\u8D8B\u52BF (${adx.toFixed(1)})\uFF0C-DI \u4E3B\u5BFC\uFF0C\u7A7A\u5934\u8D8B\u52BF\u5F3A\u52B2` };
33079
+ }
33080
+ if (adx > 25) {
33081
+ if (plusDI > minusDI) return { signal: "bullish", message: `ADX \u8D8B\u52BF\u786E\u7ACB (${adx.toFixed(1)})\uFF0C\u65B9\u5411\u504F\u591A` };
33082
+ return { signal: "bearish", message: `ADX \u8D8B\u52BF\u786E\u7ACB (${adx.toFixed(1)})\uFF0C\u65B9\u5411\u504F\u7A7A` };
33083
+ }
33084
+ return { signal: "neutral", message: `ADX \u4F4E\u6CE2\u52A8 (${adx.toFixed(1)} < 25)\uFF0C\u9707\u8361\u884C\u60C5\uFF0C\u8D8B\u52BF\u7B56\u7565\u9700\u8C28\u614E` };
33085
+ }
33086
+ function registerIndicatorTools(server) {
33087
+ server.tool(
33088
+ "okx_indicator",
33089
+ "CAT:[\u884C\u60C5] | ## \u529F\u80FD\uFF1A\u8BA1\u7B97\u6307\u5B9A\u4EA7\u54C1\u7684\u5355\u4E2A\u6280\u672F\u6307\u6807\uFF0C\u8FD4\u56DE\u539F\u59CB\u503C + \u4E2D\u6587Agent\u4FE1\u53F7\u89E3\u8BFB\uFF08\u8D85\u4E70/\u8D85\u5356/\u91D1\u53C9/\u6B7B\u53C9/\u8D8B\u52BF\u65B9\u5411\uFF09\n## \u573A\u666F\uFF1AAgent \u56DE\u7B54\u300CRSI \u591A\u5C11\u300D\u300CMACD \u91D1\u53C9\u4E86\u5417\u300D\u300C\u5E03\u6797\u5E26\u4EC0\u4E48\u4F4D\u7F6E\u300D\u7B49\u6307\u6807\u95EE\u9898\u65F6\u8C03\u7528\uFF0C\u4E00\u6B21\u62FF\u5230\u8BA1\u7B97\u7ED3\u679C\u548C\u64CD\u4F5C\u5EFA\u8BAE\n## \u5173\u952E\u8BCD\uFF1A\u6280\u672F\u6307\u6807, indicator, RSI, MACD, \u5E03\u6797\u5E26, KDJ, ADX, \u8D85\u4E70, \u8D85\u5356, \u91D1\u53C9, \u6B7B\u53C9, \u5747\u7EBF\n## \u53C2\u6570\uFF1A\n## - instId: \u4EA7\u54C1ID\uFF0C\u5982 BTC-USDT\n## - indicator: \u6307\u6807\u540D (sma/ema/rsi/macd/bb/atr/stoch/wr/cci/obv/vwap/adx/mfi/cmf/supertrend/keltner/pattern)\n## - period: \u5468\u671F\u53C2\u6570\uFF0C\u9ED8\u8BA4\u503C\u56E0\u6307\u6807\u800C\u5F02\uFF08RSI=14\uFF0CMACD=12/26/9 \u7B49\uFF09\n## - bar: K\u7EBF\u7C92\u5EA6\uFF0C\u9ED8\u8BA4 1H\u3002\u652F\u6301 1m/5m/15m/30m/1H/2H/4H/6H/12H/1D\n## \u9274\u6743\uFF1APUBLIC \u2014 \u516C\u5F00\u63A5\u53E3\uFF08\u57FA\u4E8E okx_get_candles\uFF09\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BA1\u7B97\u4E0D\u4EA4\u6613\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~1KB\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u8BA1\u7B97\u6307\u6807 \u2192 okx_quick_market \u770B\u76D8\u53E3 \u2192 agent_quick_trade \u4E0B\u5355",
33090
+ {
33091
+ instId: external_exports.string().describe("\u4EA7\u54C1ID\uFF0C\u5982 BTC-USDT\u3001ETH-USDT-SWAP"),
33092
+ indicator: external_exports.enum(["sma", "ema", "rsi", "macd", "bb", "atr", "stoch", "wr", "cci", "obv", "vwap", "adx", "mfi", "cmf", "supertrend", "keltner", "pattern"]).describe("\u6307\u6807\u540D"),
33093
+ period: external_exports.number().int().min(2).max(200).optional().describe("\u5468\u671F\uFF08\u9ED8\u8BA4\u503C\u56E0\u6307\u6807\u800C\u5F02\uFF0C\u5982 RSI \u9ED8\u8BA414\uFF09"),
33094
+ bar: external_exports.enum(["1m", "5m", "15m", "30m", "1H", "2H", "4H", "6H", "12H", "1D"]).optional().describe("K\u7EBF\u7C92\u5EA6\uFF0C\u9ED8\u8BA41H")
33095
+ },
33096
+ async ({ instId, indicator, period, bar }) => {
33097
+ try {
33098
+ const b = bar || "1H";
33099
+ const p = period || ({ sma: 20, ema: 20, rsi: 14, macd: 12, bb: 20, atr: 14, stoch: 14, wr: 14, cci: 20, obv: 0, vwap: 0, adx: 14, mfi: 14, cmf: 20, supertrend: 10, keltner: 20, pattern: 0 }[indicator] || 14);
33100
+ const minBars = indicator === "pattern" ? 5 : p * 3 + 50;
33101
+ const candles = await fetchCandles(instId, b, minBars);
33102
+ if (candles.length < p + 2) {
33103
+ return toResult({
33104
+ instId,
33105
+ indicator,
33106
+ bar: b,
33107
+ error: `\u6570\u636E\u4E0D\u8DB3\uFF1A\u4EC5\u83B7\u53D6 ${candles.length} \u6839K\u7EBF\uFF0C\u9700\u8981\u81F3\u5C11 ${p + 2} \u6839`,
33108
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
33109
+ });
33110
+ }
33111
+ const closes = candles.map((c) => c.c);
33112
+ const latest = candles[candles.length - 1];
33113
+ let result = { instId, indicator, bar: b, period: p, tsIso: (/* @__PURE__ */ new Date()).toISOString() };
33114
+ switch (indicator) {
33115
+ case "sma": {
33116
+ const sma = calcSMA(closes, p);
33117
+ const v = sma[sma.length - 1];
33118
+ const prev = sma[sma.length - 2];
33119
+ result.value = v.toFixed(4);
33120
+ result.signal = latest.c > v ? "bullish" : "bearish";
33121
+ result.message = `SMA(${p}) = ${v.toFixed(2)}\u3002\u4EF7\u683C${latest.c > v ? "\u9AD8\u4E8E" : "\u4F4E\u4E8E"}\u5747\u7EBF\uFF0C${latest.c > v ? "\u591A\u5934\u6392\u5217" : "\u7A7A\u5934\u6392\u5217"}${latest.c > v !== prev > 0 ? "\uFF08\u521A\u521A\u7A7F\u8D8A\uFF0C\u53EF\u80FD\u662F\u53D8\u76D8\u4FE1\u53F7\uFF09" : ""}`;
33122
+ break;
33123
+ }
33124
+ case "ema": {
33125
+ const ema = calcEMA(closes, p);
33126
+ const v = ema[ema.length - 1];
33127
+ const prev = ema[ema.length - 2];
33128
+ result.value = v.toFixed(4);
33129
+ result.signal = latest.c > v ? "bullish" : "bearish";
33130
+ result.message = `EMA(${p}) = ${v.toFixed(2)}\u3002\u4EF7\u683C${latest.c > v ? "\u9AD8\u4E8E" : "\u4F4E\u4E8E"}\u6307\u6570\u5747\u7EBF\uFF0C${latest.c > v ? "\u77ED\u671F\u8D8B\u52BF\u504F\u591A" : "\u77ED\u671F\u8D8B\u52BF\u504F\u7A7A"}`;
33131
+ break;
33132
+ }
33133
+ case "rsi": {
33134
+ const rsi = calcRSI(closes, p);
33135
+ const v = rsi[rsi.length - 1];
33136
+ result.value = v.toFixed(2);
33137
+ const sig = rsiSignal(v);
33138
+ result.signal = sig.signal;
33139
+ result.message = sig.message;
33140
+ break;
33141
+ }
33142
+ case "macd": {
33143
+ const { macd, signal: sigLine, histogram } = calcMACD(closes, 12, 26, 9);
33144
+ const m = macd[macd.length - 1], s = sigLine[sigLine.length - 1], h = histogram[histogram.length - 1];
33145
+ const prevH = histogram[histogram.length - 2] || 0;
33146
+ result.value = { macd: m.toFixed(4), signal: s.toFixed(4), histogram: h.toFixed(4) };
33147
+ const sig = macdSignal(h, prevH);
33148
+ result.signal = sig.signal;
33149
+ result.message = `MACD(12,26,9): DIF=${m.toFixed(4)}, DEA=${s.toFixed(4)}, \u67F1=${h.toFixed(4)}\u3002${sig.message}`;
33150
+ break;
33151
+ }
33152
+ case "bb": {
33153
+ const bb = calcBollinger(closes, p, 2);
33154
+ const up = bb.upper[bb.upper.length - 1], mid = bb.middle[bb.middle.length - 1];
33155
+ const lo = bb.lower[bb.lower.length - 1], pct = bb.pctB[bb.pctB.length - 1];
33156
+ result.value = { upper: up.toFixed(2), middle: mid.toFixed(2), lower: lo.toFixed(2), pctB: pct.toFixed(3), bandwidth: bb.bandwidth[bb.bandwidth.length - 1].toFixed(2) + "%" };
33157
+ if (pct > 1) result = { ...result, signal: "bearish", message: `\u5E03\u6797\u5E26(${p},2): \u4EF7\u683C ${latest.c.toFixed(2)} \u7A81\u7834\u4E0A\u8F68 ${up.toFixed(2)}\uFF0C\u8D85\u4E70\u72B6\u6001\uFF0C\u53EF\u80FD\u56DE\u8C03` };
33158
+ else if (pct < 0) result = { ...result, signal: "bullish", message: `\u5E03\u6797\u5E26(${p},2): \u4EF7\u683C ${latest.c.toFixed(2)} \u8DCC\u7834\u4E0B\u8F68 ${lo.toFixed(2)}\uFF0C\u8D85\u5356\u72B6\u6001\uFF0C\u53EF\u80FD\u53CD\u5F39` };
33159
+ else if (pct > 0.8) result = { ...result, signal: "bullish", message: `\u5E03\u6797\u5E26(${p},2): \u4EF7\u683C ${latest.c.toFixed(2)} \u504F\u4E0A\u8F68 ${up.toFixed(2)}\uFF0C\u5F3A\u52BF\u533A\u57DF` };
33160
+ else if (pct < 0.2) result = { ...result, signal: "bearish", message: `\u5E03\u6797\u5E26(${p},2): \u4EF7\u683C ${latest.c.toFixed(2)} \u504F\u4E0B\u8F68 ${lo.toFixed(2)}\uFF0C\u5F31\u52BF\u533A\u57DF` };
33161
+ else result = { ...result, signal: "neutral", message: `\u5E03\u6797\u5E26(${p},2): \u4EF7\u683C ${latest.c.toFixed(2)} \u5728\u4E2D\u8F68 ${mid.toFixed(2)} \u9644\u8FD1\uFF0C\u9707\u8361\u6574\u7406` };
33162
+ break;
33163
+ }
33164
+ case "atr": {
33165
+ const atr = calcATR(candles, p);
33166
+ const v = atr[atr.length - 1];
33167
+ const pctOfPrice = v / latest.c * 100;
33168
+ result.value = v.toFixed(4);
33169
+ result.message = `ATR(${p}) = ${v.toFixed(4)}\uFF08\u4E3A\u5F53\u524D\u4EF7\u683C\u7684 ${pctOfPrice.toFixed(2)}%\uFF09\u3002\u6CE2\u52A8\u7387${pctOfPrice > 3 ? "\u8F83\u9AD8" : pctOfPrice > 1 ? "\u6B63\u5E38" : "\u8F83\u4F4E"}\uFF0C\u6B62\u635F\u5EFA\u8BAE\u8BBE\u7F6E\u5728 ${(latest.c - v * 1.5).toFixed(2)} ~ ${(latest.c - v * 2).toFixed(2)}`;
33170
+ result.signal = "neutral";
33171
+ break;
33172
+ }
33173
+ case "stoch": {
33174
+ const st = calcStoch(candles, p, 3);
33175
+ const k = st.k[st.k.length - 1], d = st.d[st.d.length - 1];
33176
+ result.value = { k: k.toFixed(2), d: d.toFixed(2) };
33177
+ const sig = stochSignal(k, d);
33178
+ result.signal = sig.signal;
33179
+ result.message = sig.message;
33180
+ break;
33181
+ }
33182
+ case "wr": {
33183
+ const wr = calcWR(candles, p);
33184
+ const v = wr[wr.length - 1];
33185
+ result.value = v.toFixed(2);
33186
+ if (v > -20) result = { ...result, signal: "bearish", message: `W%R(${p}) = ${v.toFixed(1)}\uFF0C\u8D85\u4E70\u533A\uFF08>-20\uFF09\uFF0C\u77ED\u671F\u56DE\u8C03\u98CE\u9669` };
33187
+ else if (v < -80) result = { ...result, signal: "bullish", message: `W%R(${p}) = ${v.toFixed(1)}\uFF0C\u8D85\u5356\u533A\uFF08<-80\uFF09\uFF0C\u77ED\u671F\u53CD\u5F39\u673A\u4F1A` };
33188
+ else result = { ...result, signal: "neutral", message: `W%R(${p}) = ${v.toFixed(1)}\uFF0C\u4E2D\u6027\u533A\u57DF` };
33189
+ break;
33190
+ }
33191
+ case "cci": {
33192
+ const cci = calcCCI(candles, p);
33193
+ const v = cci[cci.length - 1];
33194
+ result.value = v.toFixed(2);
33195
+ if (v > 200) result = { ...result, signal: "bearish", message: `CCI(${p}) = ${v.toFixed(1)}\uFF0C\u6781\u5EA6\u8D85\u4E70\uFF08>200\uFF09\uFF0C\u5F3A\u56DE\u8C03\u4FE1\u53F7` };
33196
+ else if (v > 100) result = { ...result, signal: "bullish", message: `CCI(${p}) = ${v.toFixed(1)}\uFF0C\u8D85\u4E70\u533A\u57DF\uFF08>100\uFF09\uFF0C\u8D8B\u52BF\u504F\u5F3A\u4F46\u6CE8\u610F\u56DE\u8C03` };
33197
+ else if (v < -200) result = { ...result, signal: "bullish", message: `CCI(${p}) = ${v.toFixed(1)}\uFF0C\u6781\u5EA6\u8D85\u5356\uFF08<-200\uFF09\uFF0C\u5F3A\u53CD\u5F39\u4FE1\u53F7` };
33198
+ else if (v < -100) result = { ...result, signal: "bearish", message: `CCI(${p}) = ${v.toFixed(1)}\uFF0C\u8D85\u5356\u533A\u57DF\uFF08<-100\uFF09\uFF0C\u8D8B\u52BF\u504F\u5F31\u4F46\u6CE8\u610F\u53CD\u5F39` };
33199
+ else result = { ...result, signal: "neutral", message: `CCI(${p}) = ${v.toFixed(1)}\uFF0C\u6B63\u5E38\u8303\u56F4\uFF08-100~100\uFF09\uFF0C\u65E0\u660E\u663E\u6781\u7AEF\u4FE1\u53F7` };
33200
+ break;
33201
+ }
33202
+ case "obv": {
33203
+ const obv = calcOBV(candles);
33204
+ const v = obv[obv.length - 1];
33205
+ const prevV = obv[obv.length - 2];
33206
+ const trend = v > obv[obv.length - Math.min(20, obv.length)] ? "\u4E0A\u5347" : "\u4E0B\u964D";
33207
+ result.value = v.toFixed(0);
33208
+ result.signal = trend === "\u4E0A\u5347" ? "bullish" : "bearish";
33209
+ result.message = `OBV \u5F53\u524D ${v.toFixed(0)}\uFF08${v > prevV ? "+" : ""}${(v - prevV).toFixed(0)}\uFF09\uFF0C\u8FD1\u671F\u8D8B\u52BF${trend}\u3002${trend === "\u4E0A\u5347" ? "\u91CF\u4EF7\u914D\u5408\uFF0C\u4E0A\u6DA8\u6709\u8D44\u91D1\u652F\u6491" : "\u6210\u4EA4\u91CF\u4E0D\u914D\u5408\uFF0C\u4E0A\u6DA8\u52A8\u529B\u4E0D\u8DB3"}`;
33210
+ break;
33211
+ }
33212
+ case "vwap": {
33213
+ const vwap = calcVWAP(candles);
33214
+ const v = vwap[vwap.length - 1];
33215
+ result.value = v.toFixed(4);
33216
+ result.signal = latest.c > v ? "bullish" : "bearish";
33217
+ result.message = `VWAP = ${v.toFixed(2)}\u3002\u4EF7\u683C${latest.c > v ? "\u9AD8\u4E8E" : "\u4F4E\u4E8E"}\u6210\u4EA4\u91CF\u52A0\u6743\u5747\u4EF7\uFF0C${latest.c > v ? "\u4E70\u65B9\u4E3B\u5BFC" : "\u5356\u65B9\u4E3B\u5BFC"}`;
33218
+ break;
33219
+ }
33220
+ case "adx": {
33221
+ const { adx, plusDI, minusDI } = calcADX(candles, p);
33222
+ const a = adx[adx.length - 1], pDI = plusDI[plusDI.length - 1], mDI = minusDI[minusDI.length - 1];
33223
+ result.value = { adx: a.toFixed(2), plusDI: pDI.toFixed(2), minusDI: mDI.toFixed(2) };
33224
+ const sig = adxSignal(a, pDI, mDI);
33225
+ result.signal = sig.signal;
33226
+ result.message = sig.message;
33227
+ break;
33228
+ }
33229
+ case "mfi": {
33230
+ const mfi = calcMFI(candles, p);
33231
+ const v = mfi[mfi.length - 1];
33232
+ result.value = v.toFixed(2);
33233
+ if (v > 80) result = { ...result, signal: "bearish", message: `MFI(${p}) = ${v.toFixed(1)}\uFF0C\u8D44\u91D1\u4E25\u91CD\u8D85\u4E70\uFF08>80\uFF09\uFF0C\u53EF\u80FD\u89C1\u9876` };
33234
+ else if (v < 20) result = { ...result, signal: "bullish", message: `MFI(${p}) = ${v.toFixed(1)}\uFF0C\u8D44\u91D1\u4E25\u91CD\u8D85\u5356\uFF08<20\uFF09\uFF0C\u53EF\u80FD\u89C1\u5E95` };
33235
+ else if (v > 60) result = { ...result, signal: "bullish", message: `MFI(${p}) = ${v.toFixed(1)}\uFF0C\u8D44\u91D1\u6301\u7EED\u6D41\u5165` };
33236
+ else if (v < 40) result = { ...result, signal: "bearish", message: `MFI(${p}) = ${v.toFixed(1)}\uFF0C\u8D44\u91D1\u6301\u7EED\u6D41\u51FA` };
33237
+ else result = { ...result, signal: "neutral", message: `MFI(${p}) = ${v.toFixed(1)}\uFF0C\u8D44\u91D1\u9762\u4E2D\u6027` };
33238
+ break;
33239
+ }
33240
+ case "cmf": {
33241
+ const cmf = calcCMF(candles, p);
33242
+ const v = cmf[cmf.length - 1];
33243
+ result.value = v.toFixed(4);
33244
+ if (v > 0.1) result = { ...result, signal: "bullish", message: `CMF(${p}) = ${v.toFixed(3)}\uFF0C\u663E\u8457\u51C0\u6D41\u5165\uFF08>0.1\uFF09\uFF0C\u4E70\u65B9\u4E3B\u5BFC` };
33245
+ else if (v < -0.1) result = { ...result, signal: "bearish", message: `CMF(${p}) = ${v.toFixed(3)}\uFF0C\u663E\u8457\u51C0\u6D41\u51FA\uFF08<-0.1\uFF09\uFF0C\u5356\u65B9\u4E3B\u5BFC` };
33246
+ else if (v > 0) result = { ...result, signal: "bullish", message: `CMF(${p}) = ${v.toFixed(3)}\uFF0C\u8F7B\u5FAE\u51C0\u6D41\u5165` };
33247
+ else result = { ...result, signal: "bearish", message: `CMF(${p}) = ${v.toFixed(3)}\uFF0C\u8F7B\u5FAE\u51C0\u6D41\u51FA` };
33248
+ break;
33249
+ }
33250
+ case "supertrend": {
33251
+ const st = calcSupertrend(candles, p, 3);
33252
+ const trend = st.trend[st.trend.length - 1];
33253
+ const dir = st.direction[st.direction.length - 1];
33254
+ result.value = { trend: trend.toFixed(2), direction: dir };
33255
+ result.signal = dir === 1 ? "bullish" : "bearish";
33256
+ result.message = `Supertrend(${p},3): ${dir === 1 ? "\u{1F7E2} \u591A\u5934" : "\u{1F534} \u7A7A\u5934"}\uFF0C\u6B62\u635F\u7EBF ${trend.toFixed(2)}\u3002${dir === 1 ? "\u8D8B\u52BF\u504F\u591A\uFF0C\u4E0D\u5EFA\u8BAE\u505A\u7A7A" : "\u8D8B\u52BF\u504F\u7A7A\uFF0C\u4E0D\u5EFA\u8BAE\u505A\u591A"}`;
33257
+ break;
33258
+ }
33259
+ case "keltner": {
33260
+ const kc = calcKeltner(candles, p, 2);
33261
+ const up = kc.upper[kc.upper.length - 1], mid = kc.middle[kc.middle.length - 1];
33262
+ const lo = kc.lower[kc.lower.length - 1];
33263
+ result.value = { upper: up.toFixed(2), middle: mid.toFixed(2), lower: lo.toFixed(2) };
33264
+ if (latest.c > up) result = { ...result, signal: "bullish", message: `\u80AF\u7279\u7EB3(${p},2): \u4EF7\u683C\u7A81\u7834\u4E0A\u8F68 ${up.toFixed(2)}\uFF0C\u5F3A\u52BF\u7A81\u7834\u4FE1\u53F7` };
33265
+ else if (latest.c < lo) result = { ...result, signal: "bearish", message: `\u80AF\u7279\u7EB3(${p},2): \u4EF7\u683C\u8DCC\u7834\u4E0B\u8F68 ${lo.toFixed(2)}\uFF0C\u5F31\u52BF\u8DCC\u7834\u4FE1\u53F7` };
33266
+ else result = { ...result, signal: "neutral", message: `\u80AF\u7279\u7EB3(${p},2): \u4EF7\u683C\u5728\u901A\u9053\u5185 [${lo.toFixed(2)}, ${up.toFixed(2)}]\uFF0C\u9707\u8361\u8FD0\u884C` };
33267
+ break;
33268
+ }
33269
+ case "pattern": {
33270
+ const patterns = [];
33271
+ const last = candles[candles.length - 1], prev = candles[candles.length - 2];
33272
+ if (detectDoji(last)) patterns.push("\u5341\u5B57\u661F (Doji) \u2014 \u591A\u7A7A\u5747\u8861\uFF0C\u53D8\u76D8\u524D\u5146");
33273
+ if (detectHammer(last)) patterns.push("\u9524\u5B50\u7EBF (Hammer) \u2014 \u5E95\u90E8\u53CD\u8F6C\u4FE1\u53F7\uFF0C\u770B\u6DA8");
33274
+ const eng = detectEngulfing(prev, last);
33275
+ if (eng === "bullish") patterns.push("\u770B\u6DA8\u541E\u6CA1 (Bullish Engulfing) \u2014 \u5F3A\u5E95\u90E8\u53CD\u8F6C\u4FE1\u53F7");
33276
+ if (eng === "bearish") patterns.push("\u770B\u8DCC\u541E\u6CA1 (Bearish Engulfing) \u2014 \u5F3A\u9876\u90E8\u53CD\u8F6C\u4FE1\u53F7");
33277
+ if (candles.length >= 5) {
33278
+ const last5 = candles.slice(-5);
33279
+ if (last5.every((c) => c.c < c.o) && last.c > last.o) patterns.push("\u6668\u661F (Morning Star) \u2014 \u5E95\u90E8\u4E09\u7EBF\u53CD\u8F6C\uFF0C\u770B\u6DA8");
33280
+ if (last5.every((c) => c.c > c.o) && last.c < last.o) patterns.push("\u9EC4\u660F\u661F (Evening Star) \u2014 \u9876\u90E8\u4E09\u7EBF\u53CD\u8F6C\uFF0C\u770B\u8DCC");
33281
+ }
33282
+ result.value = patterns;
33283
+ result.signal = patterns.length > 0 ? patterns[0].includes("Bullish") || patterns[0].includes("Hammer") || patterns[0].includes("Morning") ? "bullish" : "bearish" : "neutral";
33284
+ result.message = patterns.length > 0 ? patterns.join("\uFF1B") : "\u6700\u8FD1\u4E24\u6839K\u7EBF\u672A\u68C0\u6D4B\u5230\u7ECF\u5178\u53CD\u8F6C\u5F62\u6001";
33285
+ break;
33286
+ }
33287
+ }
33288
+ result._summary = `[${instId} ${b}] ${indicator.toUpperCase()}: ${result.message}`;
33289
+ return toResult(result);
33290
+ } catch (e) {
33291
+ return toError(e);
33292
+ }
33293
+ }
33294
+ );
33295
+ server.tool(
33296
+ "okx_indicator_batch",
33297
+ "CAT:[\u884C\u60C5] | ## \u529F\u80FD\uFF1A\u6279\u91CF\u8BA1\u7B97\u591A\u4E2A\u6280\u672F\u6307\u6807\uFF0C\u4E00\u6B21\u6027\u8FD4\u56DE\u5168\u90E8\u6307\u6807\u503C\u548C\u7EFC\u5408\u4EA4\u6613\u4FE1\u53F7\u603B\u7ED3\n## \u573A\u666F\uFF1AAgent \u9700\u8981\u7EFC\u5408\u5206\u6790\u591A\u4E2A\u6307\u6807\uFF08\u5982\u540C\u65F6\u770BRSI+MACD+\u5E03\u6797\u5E26\uFF09\u65F6\u8C03\u7528\uFF0C\u4E00\u6B21\u62FF\u5230\u6240\u6709\u7ED3\u679C\u548C\u7EFC\u5408\u5224\u65AD\n## \u5173\u952E\u8BCD\uFF1A\u6279\u91CF\u6307\u6807, indicator batch, \u591A\u6307\u6807, \u7EFC\u5408\u4FE1\u53F7, \u6280\u672F\u5206\u6790, \u6307\u6807\u7EC4\u5408\n## \u53C2\u6570\uFF1A\n## - instId: \u4EA7\u54C1ID\n## - indicators: \u6307\u6807\u540D\u5217\u8868\uFF0C\u7528\u9017\u53F7\u5206\u9694\u3002\u5982 RSI,MACD,BB,ATR\n## - bar: K\u7EBF\u7C92\u5EA6\uFF0C\u9ED8\u8BA4 1H\n## \u9274\u6743\uFF1APUBLIC\n## \u98CE\u9669\uFF1AREAD\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~3KB\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u6279\u91CF \u2192 \u5355\u6307\u6807\u6DF1\u5165\u7528 okx_indicator \u2192 agent_quick_trade \u4E0B\u5355",
33298
+ {
33299
+ instId: external_exports.string().describe("\u4EA7\u54C1ID\uFF0C\u5982 BTC-USDT"),
33300
+ indicators: external_exports.string().describe("\u6307\u6807\u540D\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694\u3002\u53EF\u9009: sma,ema,rsi,macd,bb,atr,stoch,wr,cci,obv,vwap,adx,mfi,cmf,supertrend,keltner,pattern"),
33301
+ bar: external_exports.enum(["1m", "5m", "15m", "30m", "1H", "2H", "4H", "6H", "12H", "1D"]).optional().describe("K\u7EBF\u7C92\u5EA6\uFF0C\u9ED8\u8BA41H")
33302
+ },
33303
+ async ({ instId, indicators, bar }) => {
33304
+ try {
33305
+ const b = bar || "1H";
33306
+ const list = indicators.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
33307
+ if (list.length === 0) return toError("indicators \u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A\uFF0CAgent \u8BF7\u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A\u6307\u6807\u540D");
33308
+ if (list.length > 10) return toError("\u6700\u591A\u540C\u65F6\u8BA1\u7B97 10 \u4E2A\u6307\u6807\uFF0CAgent \u8BF7\u51CF\u5C11\u6570\u91CF");
33309
+ const candles = await fetchCandles(instId, b, 300);
33310
+ if (candles.length < 20) {
33311
+ return toResult({
33312
+ instId,
33313
+ bar: b,
33314
+ error: `\u6570\u636E\u4E0D\u8DB3\uFF1A\u4EC5\u83B7\u53D6 ${candles.length} \u6839K\u7EBF`,
33315
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
33316
+ });
33317
+ }
33318
+ const closes = candles.map((c) => c.c);
33319
+ let bullCount = 0, bearCount = 0;
33320
+ const results = [];
33321
+ for (const ind of list) {
33322
+ const p = { sma: 20, ema: 20, rsi: 14, macd: 12, bb: 20, atr: 14, stoch: 14, wr: 14, cci: 20, obv: 0, vwap: 0, adx: 14, mfi: 14, cmf: 20, supertrend: 10, keltner: 20, pattern: 0 }[ind] || 14;
33323
+ let item = { indicator: ind };
33324
+ try {
33325
+ switch (ind) {
33326
+ case "rsi": {
33327
+ const v = calcRSI(closes, p);
33328
+ item.value = v[v.length - 1].toFixed(2);
33329
+ const sig = rsiSignal(v[v.length - 1]);
33330
+ item.signal = sig.signal;
33331
+ item.message = sig.message;
33332
+ break;
33333
+ }
33334
+ case "macd": {
33335
+ const m = calcMACD(closes, 12, 26, 9);
33336
+ const h = m.histogram[m.histogram.length - 1];
33337
+ item.value = `${h.toFixed(4)}`;
33338
+ const prevH = m.histogram[m.histogram.length - 2] || 0;
33339
+ const sig = macdSignal(h, prevH);
33340
+ item.signal = sig.signal;
33341
+ item.message = sig.message;
33342
+ break;
33343
+ }
33344
+ case "bb": {
33345
+ const bb = calcBollinger(closes, p, 2);
33346
+ const pct = bb.pctB[bb.pctB.length - 1];
33347
+ item.value = `pctB:${pct.toFixed(2)}`;
33348
+ if (pct > 1) {
33349
+ item.signal = "bearish";
33350
+ item.message = "\u7A81\u7834\u4E0A\u8F68";
33351
+ } else if (pct < 0) {
33352
+ item.signal = "bullish";
33353
+ item.message = "\u8DCC\u7834\u4E0B\u8F68";
33354
+ } else {
33355
+ item.signal = "neutral";
33356
+ item.message = "\u901A\u9053\u5185";
33357
+ }
33358
+ ;
33359
+ break;
33360
+ }
33361
+ case "atr": {
33362
+ const a = calcATR(candles, p);
33363
+ item.value = a[a.length - 1].toFixed(4);
33364
+ item.signal = "neutral";
33365
+ item.message = `\u6CE2\u52A8\u7387: ${(a[a.length - 1] / candles[candles.length - 1].c * 100).toFixed(2)}%`;
33366
+ break;
33367
+ }
33368
+ case "stoch": {
33369
+ const s = calcStoch(candles, p, 3);
33370
+ item.value = `K:${s.k[s.k.length - 1].toFixed(1)},D:${s.d[s.d.length - 1].toFixed(1)}`;
33371
+ const sig = stochSignal(s.k[s.k.length - 1], s.d[s.d.length - 1]);
33372
+ item.signal = sig.signal;
33373
+ item.message = sig.message;
33374
+ break;
33375
+ }
33376
+ case "wr": {
33377
+ const w = calcWR(candles, p);
33378
+ item.value = w[w.length - 1].toFixed(1);
33379
+ item.signal = w[w.length - 1] > -20 ? "bearish" : w[w.length - 1] < -80 ? "bullish" : "neutral";
33380
+ item.message = `W%R=${item.value}`;
33381
+ break;
33382
+ }
33383
+ case "cci": {
33384
+ const c = calcCCI(candles, p);
33385
+ item.value = c[c.length - 1].toFixed(1);
33386
+ item.signal = Math.abs(c[c.length - 1]) > 100 ? c[c.length - 1] > 0 ? "bearish" : "bullish" : "neutral";
33387
+ item.message = `CCI=${item.value}`;
33388
+ break;
33389
+ }
33390
+ case "obv": {
33391
+ const o = calcOBV(candles);
33392
+ item.value = o[o.length - 1].toFixed(0);
33393
+ item.signal = o[o.length - 1] > o[o.length - 20] ? "bullish" : "bearish";
33394
+ item.message = "\u91CF\u4EF7\u5206\u6790";
33395
+ break;
33396
+ }
33397
+ case "vwap": {
33398
+ const vw = calcVWAP(candles);
33399
+ item.value = vw[vw.length - 1].toFixed(2);
33400
+ item.signal = candles[candles.length - 1].c > vw[vw.length - 1] ? "bullish" : "bearish";
33401
+ item.message = "\u6210\u4EA4\u91CF\u52A0\u6743\u5747\u4EF7";
33402
+ break;
33403
+ }
33404
+ case "adx": {
33405
+ const ax = calcADX(candles, p);
33406
+ const a = ax.adx[ax.adx.length - 1] ?? 0;
33407
+ const pd = ax.plusDI[ax.plusDI.length - 1] ?? 0;
33408
+ const md = ax.minusDI[ax.minusDI.length - 1] ?? 0;
33409
+ item.value = `${a.toFixed(1)}`;
33410
+ const sig = adxSignal(a, pd, md);
33411
+ item.signal = sig.signal;
33412
+ item.message = sig.message;
33413
+ break;
33414
+ }
33415
+ case "mfi": {
33416
+ const mf = calcMFI(candles, p);
33417
+ const v = mf[mf.length - 1] ?? 50;
33418
+ item.value = v.toFixed(1);
33419
+ item.signal = v > 80 ? "bearish" : v < 20 ? "bullish" : "neutral";
33420
+ item.message = `MFI=${item.value}`;
33421
+ break;
33422
+ }
33423
+ case "cmf": {
33424
+ const cm = calcCMF(candles, p);
33425
+ const v = cm[cm.length - 1] ?? 0;
33426
+ item.value = v.toFixed(3);
33427
+ item.signal = v > 0 ? "bullish" : "bearish";
33428
+ item.message = `CMF=${item.value}`;
33429
+ break;
33430
+ }
33431
+ case "supertrend": {
33432
+ const st = calcSupertrend(candles, p, 3);
33433
+ const trend = st.trend[st.trend.length - 1] ?? 0;
33434
+ const dir = st.direction[st.direction.length - 1] ?? 0;
33435
+ item.value = trend.toFixed(2);
33436
+ item.signal = dir === 1 ? "bullish" : "bearish";
33437
+ item.message = `${item.signal === "bullish" ? "\u591A\u5934" : "\u7A7A\u5934"}`;
33438
+ break;
33439
+ }
33440
+ default: {
33441
+ item.value = "N/A";
33442
+ item.signal = "neutral";
33443
+ item.message = `\u6307\u6807 ${ind} \u6682\u4E0D\u652F\u6301\u6279\u91CF\u8BA1\u7B97\uFF0C\u8BF7\u7528 okx_indicator \u5355\u72EC\u67E5\u8BE2`;
33444
+ break;
33445
+ }
33446
+ }
33447
+ } catch {
33448
+ item.value = "error";
33449
+ item.signal = "neutral";
33450
+ item.message = "\u8BA1\u7B97\u5931\u8D25";
33451
+ }
33452
+ if (item.signal === "bullish") bullCount++;
33453
+ else if (item.signal === "bearish") bearCount++;
33454
+ results.push(item);
33455
+ }
33456
+ const consensus = bullCount > bearCount + 2 ? "bullish" : bearCount > bullCount + 2 ? "bearish" : "neutral";
33457
+ const consensusMsg = consensus === "bullish" ? `\u{1F7E2} \u7EFC\u5408\u504F\u591A\uFF08${bullCount}\u4E2A\u770B\u6DA8 vs ${bearCount}\u4E2A\u770B\u8DCC\uFF09\uFF0C\u591A\u4E2A\u6307\u6807\u786E\u8BA4\u591A\u5934\u4FE1\u53F7` : consensus === "bearish" ? `\u{1F534} \u7EFC\u5408\u504F\u7A7A\uFF08${bearCount}\u4E2A\u770B\u8DCC vs ${bullCount}\u4E2A\u770B\u6DA8\uFF09\uFF0C\u591A\u4E2A\u6307\u6807\u786E\u8BA4\u7A7A\u5934\u4FE1\u53F7` : `\u{1F7E1} \u4FE1\u53F7\u5206\u6B67\uFF08${bullCount}\u591A ${bearCount}\u7A7A\uFF09\uFF0C\u65B9\u5411\u4E0D\u660E\u786E\uFF0C\u5EFA\u8BAE\u89C2\u671B`;
33458
+ return toResult({
33459
+ instId,
33460
+ bar: b,
33461
+ indicators: results,
33462
+ consensus: { signal: consensus, bullCount, bearCount, neutralCount: results.length - bullCount - bearCount, message: consensusMsg },
33463
+ _summary: `[${instId} ${b}] \u5171 ${results.length} \u4E2A\u6307\u6807\u3002${consensusMsg}`,
33464
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
33465
+ });
33466
+ } catch (e) {
33467
+ return toError(e);
33468
+ }
33469
+ }
33470
+ );
33471
+ }
33472
+
33473
+ // src/tools/smartmoney.ts
33474
+ function registerSmartMoneyTools(server, auth) {
33475
+ server.tool(
33476
+ "okx_smart_leaderboard",
33477
+ "CAT:[Smart Money] | ## \u529F\u80FD\uFF1A\u4EA4\u6613\u5458\u6392\u884C\u699C\u2014\u2014\u6309\u6536\u76CA\u7387/\u603B\u6536\u76CA\u6392\u5E8F\uFF0C\u542B\u8FD1N\u65E5\u80DC\u7387\u3001\u6700\u5927\u56DE\u64A4\u3001\u8DDF\u5355\u4EBA\u6570\n## \u573A\u666F\uFF1AAgent \u56DE\u7B54\u300C\u54EA\u4E9B\u4EA4\u6613\u5458\u5389\u5BB3\u300D\u300C\u8DDF\u8C01\u8D5A\u94B1\u300D\u3001\u5BFB\u627E\u4F18\u8D28\u4EA4\u6613\u5458\u3001\u5206\u6790\u806A\u660E\u94B1\u6D41\u5411\n## \u5173\u952E\u8BCD\uFF1A\u4EA4\u6613\u5458, \u6392\u884C\u699C, leaderboard, \u806A\u660E\u94B1, smart money, \u8DDF\u5355, \u6536\u76CA\u6392\u540D\n## \u53C2\u6570\uFF1A\n## - instType: \u4EA7\u54C1\u7C7B\u578B\u3002SPOT=\u73B0\u8D27, SWAP=\u6C38\u7EED\u3002\u9ED8\u8BA4SPOT\n## - sortBy: \u6392\u5E8F\u5B57\u6BB5\u3002pnl=\u6536\u76CA\u7387, totalPnl=\u603B\u6536\u76CA, followers=\u8DDF\u5355\u4EBA\u6570\n## - topN: \u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA410\n## \u9274\u6743\uFF1APUBLIC \u2014 \u516C\u5F00\u63A5\u53E3\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~3KB\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u6392\u884C \u2192 okx_smart_trader_detail \u770B\u8BE6\u60C5 \u2192 okx_copy_trader \u8DDF\u5355\uFF08\u9700\u786E\u8BA4\uFF09",
33478
+ {
33479
+ instType: external_exports.enum(["SPOT", "SWAP"]).optional().describe("\u4EA7\u54C1\u7C7B\u578B\uFF0C\u9ED8\u8BA4SPOT"),
33480
+ sortBy: external_exports.enum(["pnl", "totalPnl", "followers"]).optional().describe("\u6392\u5E8F\u3002pnl=\u6536\u76CA\u7387, totalPnl=\u603B\u6536\u76CA, followers=\u8DDF\u5355\u4EBA\u6570"),
33481
+ topN: external_exports.number().int().min(3).max(50).optional().describe("\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA410")
33482
+ },
33483
+ async ({ instType, sortBy, topN }) => {
33484
+ try {
33485
+ const it = instType || "SPOT";
33486
+ const n = topN || 10;
33487
+ const sb = sortBy || "pnl";
33488
+ const raw = await publicApi.getPublicLeadTraders();
33489
+ if (!raw || raw.length === 0) {
33490
+ return toResult({ leaderboard: [], count: 0, message: "\u6682\u65E0\u4EA4\u6613\u5458\u6570\u636E", tsIso: (/* @__PURE__ */ new Date()).toISOString() });
33491
+ }
33492
+ const traders = raw.map((t) => ({
33493
+ uniqueCode: t.uniqueCode,
33494
+ nickName: t.nickName,
33495
+ instType: it,
33496
+ pnl: parseFloat(t.pnl ?? "0"),
33497
+ totalPnl: parseFloat(t.totalPnl ?? "0"),
33498
+ winRate: parseFloat(t.winRate ?? "0"),
33499
+ drawdown: t.maxDrawdown,
33500
+ followers: parseInt(t.followerCount ?? "0"),
33501
+ sharpeRatio: t.sharpeRatio,
33502
+ profitSharingRatio: t.profitSharingRatio,
33503
+ filledPnL: parseFloat(t.filledPnl ?? "0"),
33504
+ beginTs: t.beginTs ? new Date(parseInt(t.beginTs)).toISOString() : void 0
33505
+ })).filter((t) => t.pnl > 0 || t.totalPnl > 0);
33506
+ if (sb === "pnl") traders.sort((a, b) => b.pnl - a.pnl);
33507
+ else if (sb === "totalPnl") traders.sort((a, b) => b.totalPnl - a.totalPnl);
33508
+ else traders.sort((a, b) => b.followers - a.followers);
33509
+ const top = traders.slice(0, n);
33510
+ return toResult({
33511
+ leaderboard: top.map((t, i) => ({ rank: i + 1, ...t, pnl: t.pnl.toFixed(2) + "%", totalPnl: "$" + t.totalPnl.toFixed(2) })),
33512
+ total: traders.length,
33513
+ sortBy: sb,
33514
+ _summary: `\u{1F3C6} ${it} \u4EA4\u6613\u5458\u6392\u884C\u699C Top${n}: ${top.slice(0, 3).map((t) => `${t.nickName}(${t.pnl.toFixed(1)}%)`).join(" | ")}\u3002\u5171 ${traders.length} \u4F4D\u4EA4\u6613\u5458\u3002`,
33515
+ tip: "\u60F3\u770B\u67D0\u4F4D\u4EA4\u6613\u5458\u7684\u8BE6\u7EC6\u6570\u636E\uFF0C\u7528 okx_smart_trader_detail { uniqueCode }",
33516
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
33517
+ });
33518
+ } catch (e) {
33519
+ return toError(e);
33520
+ }
33521
+ }
33522
+ );
33523
+ server.tool(
33524
+ "okx_smart_trader_detail",
33525
+ "CAT:[Smart Money] | ## \u529F\u80FD\uFF1A\u5355\u4EA4\u6613\u5458\u5168\u666F\u5206\u6790\u2014\u2014\u6536\u76CA\u7387+\u80DC\u7387+\u56DE\u64A4+\u5F53\u524D\u6301\u4ED3+\u8FD130\u5929PnL\u66F2\u7EBF\n## \u573A\u666F\uFF1AAgent \u6DF1\u5165\u4E86\u89E3\u67D0\u4F4D\u4EA4\u6613\u5458\uFF08\u4ECE\u6392\u884C\u699C\u70B9\u51FB\u8FDB\u5165\uFF09\u3001\u5224\u65AD\u662F\u5426\u503C\u5F97\u8DDF\u5355\n## \u5173\u952E\u8BCD\uFF1A\u4EA4\u6613\u5458\u8BE6\u60C5, trader detail, \u8DDF\u5355\u5206\u6790, \u6301\u4ED3\u67E5\u770B, \u4E1A\u7EE9\u5206\u6790\n## \u53C2\u6570\uFF1A\n## - uniqueCode: \u4EA4\u6613\u5458\u552F\u4E00\u7801\uFF08\u4ECE\u6392\u884C\u699C\u4E2D\u83B7\u53D6\uFF09\n## - instType: \u4EA7\u54C1\u7C7B\u578B\u3002SPOT=\u73B0\u8D27, SWAP=\u6C38\u7EED\n## \u9274\u6743\uFF1APUBLIC \u2014 \u516C\u5F00\u63A5\u53E3\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~3KB\n## \u5173\u8054\uFF1Aokx_smart_leaderboard \u6392\u884C \u2192 \u672C\u5DE5\u5177\u8BE6\u60C5 \u2192 okx_copy_trader \u8DDF\u5355",
33526
+ {
33527
+ uniqueCode: external_exports.string().describe("\u4EA4\u6613\u5458\u552F\u4E00\u7801\uFF08\u5982 A12345678\uFF09"),
33528
+ instType: external_exports.enum(["SPOT", "SWAP"]).optional().describe("\u4EA7\u54C1\u7C7B\u578B\uFF0C\u9ED8\u8BA4SPOT")
33529
+ },
33530
+ async ({ uniqueCode, instType }) => {
33531
+ try {
33532
+ const it = instType || "SPOT";
33533
+ const [stats, pnl, positions] = await Promise.allSettled([
33534
+ publicApi.getPublicLeadTraderStats(uniqueCode, it, "30"),
33535
+ publicApi.getPublicLeadTraderPnl(uniqueCode, "30"),
33536
+ publicApi.getLeadTraderPositions(uniqueCode, it)
33537
+ ]);
33538
+ const sOk = stats.status === "fulfilled" ? stats.value?.[0] ?? {} : {};
33539
+ const pOk = pnl.status === "fulfilled" ? pnl.value ?? [] : [];
33540
+ const posOk = positions.status === "fulfilled" ? positions.value ?? [] : [];
33541
+ const currentPos = posOk.filter((p) => parseFloat(p.pos || "0") !== 0).map((p) => ({
33542
+ instId: p.instId,
33543
+ posSide: p.posSide,
33544
+ pos: p.pos,
33545
+ avgPx: p.avgOpenPx,
33546
+ markPx: p.markPx,
33547
+ upl: p.upl,
33548
+ uplRatio: p.uplRatio,
33549
+ lever: p.lever
33550
+ }));
33551
+ const pnlCurve = pOk.slice(0, 30).map((p) => ({
33552
+ date: p.ts ? new Date(parseInt(p.ts)).toISOString().slice(0, 10) : void 0,
33553
+ pnl: parseFloat(p.pnl ?? "0").toFixed(2)
33554
+ }));
33555
+ return toResult({
33556
+ uniqueCode,
33557
+ profile: {
33558
+ nickName: sOk.nickName,
33559
+ pnl: parseFloat(sOk.pnl ?? "0").toFixed(2) + "%",
33560
+ totalPnl: "$" + parseFloat(sOk.totalPnl ?? "0").toFixed(2),
33561
+ winRate: parseFloat(sOk.winRate ?? "0").toFixed(1) + "%",
33562
+ maxDrawdown: sOk.maxDrawdown,
33563
+ sharpeRatio: sOk.sharpeRatio,
33564
+ totalEquity: sOk.totalEquity,
33565
+ followerCount: sOk.followerCount,
33566
+ tradesPerDay: sOk.tradesPerDay,
33567
+ avgHoldingHours: sOk.avgHoldingHours
33568
+ },
33569
+ positions: {
33570
+ activeCount: currentPos.length,
33571
+ list: currentPos
33572
+ },
33573
+ pnlTimeline: pnlCurve,
33574
+ _summary: `${sOk.nickName || uniqueCode}: ${sOk.pnl || "?"}% \u6536\u76CA\u7387\uFF0C${sOk.winRate || "?"}% \u80DC\u7387\uFF0C${currentPos.length} \u4E2A\u5F53\u524D\u6301\u4ED3\u3002\u6700\u5927\u56DE\u64A4 ${sOk.maxDrawdown || "?"}\u3002`,
33575
+ tsIso: (/* @__PURE__ */ new Date()).toISOString()
33576
+ });
33577
+ } catch (e) {
33578
+ return toError(e);
33579
+ }
33580
+ }
33581
+ );
33582
+ server.tool(
33583
+ "okx_smart_sentiment",
33584
+ "CAT:[Smart Money] | ## \u529F\u80FD\uFF1A\u5E02\u573A\u60C5\u7EEA\u4EEA\u8868\u76D8\u2014\u2014\u7EFC\u5408\u591A\u7A7A\u6BD4\u3001\u671F\u6743PCR\u3001\u8D44\u91D1\u8D39\u7387\u3001\u6210\u4EA4\u91CF\uFF0C\u7ED9\u51FA\u91CF\u5316\u60C5\u7EEA\u8BC4\u5206\n## \u573A\u666F\uFF1AAgent \u56DE\u7B54\u300C\u73B0\u5728\u5E02\u573A\u60C5\u7EEA\u600E\u4E48\u6837\u300D\u300C\u6563\u6237\u548C\u806A\u660E\u94B1\u5728\u505A\u4EC0\u4E48\u300D\u300C\u8BE5\u8FFD\u591A\u8FD8\u662F\u89C2\u671B\u300D\n## \u5173\u952E\u8BCD\uFF1A\u5E02\u573A\u60C5\u7EEA, sentiment, \u591A\u7A7A\u6BD4, PCR, \u8D44\u91D1\u8D39\u7387, \u6050\u614C\u8D2A\u5A6A, \u60C5\u7EEA\u6307\u6807\n## \u53C2\u6570\uFF1A\n## - instFamily: \u4EA7\u54C1\u65CF\uFF0C\u5982 BTC-USD\u3002\u9ED8\u8BA4 BTC-USD\n## \u9274\u6743\uFF1APUBLIC \u2014 \u516C\u5F00\u63A5\u53E3\n## \u98CE\u9669\uFF1AREAD \u2014 \u53EA\u8BFB\n## \u8FD4\u56DE\u91CF\uFF1A\u5FAE\u5C0F ~2KB\n## \u5173\u8054\uFF1A\u672C\u5DE5\u5177\u770B\u60C5\u7EEA \u2192 agent_market_scan \u770B\u5F02\u52A8 \u2192 agent_quick_trade \u4E0B\u5355",
33585
+ {
33586
+ instFamily: external_exports.string().optional().describe("\u4EA7\u54C1\u65CF\uFF0C\u5982 BTC-USD\u3001ETH-USD\u3002\u9ED8\u8BA4 BTC-USD")
33587
+ },
33588
+ async ({ instFamily }) => {
33589
+ try {
33590
+ const family = instFamily || "BTC-USD";
33591
+ const instId = family.replace("-", "-") + "-SWAP";
33592
+ const ccy = family.split("-")[0];
33593
+ const fetchers = {
33594
+ lsRatio: publicApi.getLongShortRatio(ccy),
33595
+ pcr: publicApi.getOptionPutCallRatio(ccy),
33596
+ fundingRate: publicApi.getFundingRate(instId),
33597
+ topLSRatio: publicApi.getTopTraderLongShortRatio(instId)
33598
+ };
33599
+ const entries = Object.entries(fetchers);
33600
+ const keys = entries.map((e) => e[0]);
33601
+ const results = await Promise.allSettled(entries.map((e) => e[1]));
33602
+ const get = (name) => {
33603
+ const idx = keys.indexOf(name);
33604
+ if (idx < 0) return null;
33605
+ if (results[idx].status === "rejected") return null;
33606
+ return results[idx].value;
33607
+ };
33608
+ const lsArr = get("lsRatio") ?? [];
33609
+ const ls = lsArr[0];
33610
+ const lsRatio = parseFloat(ls?.lsRatio ?? "0");
33611
+ const lsRatioPrev = parseFloat(ls?.lsRatioPrev ?? lsRatio);
33612
+ const pcrArr = get("pcr") ?? [];
33613
+ const pcr = pcrArr[0];
33614
+ const pcrRatio = parseFloat(pcr?.pcrRatio ?? "0");
33615
+ const frArr = get("fundingRate") ?? [];
33616
+ const fr = frArr[0];
33617
+ const fundingRate = parseFloat(fr?.fundingRate ?? "0");
33618
+ const topLSArr = get("topLSRatio") ?? [];
33619
+ const topLS = topLSArr[0];
33620
+ const topLsRatio = parseFloat(topLS?.lsRatio ?? "0");
33621
+ let score = 50;
33622
+ if (lsRatio > 1.5) score += 15;
33623
+ else if (lsRatio > 1.2) score += 8;
33624
+ else if (lsRatio < 0.8) score -= 8;
33625
+ else if (lsRatio < 0.5) score -= 15;
33626
+ if (topLsRatio > 1.2 && lsRatio < 1) score += 10;
33627
+ if (pcrRatio > 1.2) score -= 12;
33628
+ else if (pcrRatio > 0.8) score -= 5;
33629
+ else if (pcrRatio < 0.5) score += 5;
33630
+ else if (pcrRatio < 0.3) score += 10;
33631
+ if (fundingRate > 1e-3) score -= 10;
33632
+ else if (fundingRate > 5e-4) score -= 5;
33633
+ else if (fundingRate < -5e-4) score += 5;
33634
+ score = Math.max(0, Math.min(100, score));
33635
+ const sentiment = score >= 70 ? "\u{1F7E2} \u6781\u5EA6\u770B\u591A" : score >= 55 ? "\u{1F7E2} \u504F\u591A" : score >= 45 ? "\u{1F7E1} \u4E2D\u6027" : score <= 30 ? "\u{1F534} \u6781\u5EA6\u770B\u7A7A" : "\u{1F534} \u504F\u7A7A";
33636
+ const signals = [];
33637
+ if (lsRatio > 1.5) signals.push(`\u591A\u7A7A\u6BD4 ${lsRatio.toFixed(2)} \u6781\u9AD8\uFF0C\u6563\u6237\u9AD8\u5EA6\u770B\u591A\uFF08\u8B66\u60D5\u53CD\u8F6C\uFF09`);
33638
+ else if (lsRatio > 1.2) signals.push(`\u591A\u7A7A\u6BD4 ${lsRatio.toFixed(2)}\uFF0C\u6563\u6237\u504F\u591A`);
33639
+ else if (lsRatio < 0.8) signals.push(`\u591A\u7A7A\u6BD4 ${lsRatio.toFixed(2)}\uFF0C\u6563\u6237\u504F\u7A7A`);
33640
+ if (topLsRatio > 1.2 && lsRatio < 1) signals.push("\u806A\u660E\u94B1\uFF08Top\u4EA4\u6613\u5458\uFF09\u4E0E\u6563\u6237\u65B9\u5411\u5206\u6B67\uFF1A\u806A\u660E\u94B1\u770B\u591A\uFF0C\u6563\u6237\u770B\u7A7A");
33641
+ if (pcrRatio > 1) signals.push(`PCR ${pcrRatio.toFixed(2)}\uFF0C\u671F\u6743\u5E02\u573A\u907F\u9669\u60C5\u7EEA\u91CD\uFF08\u66F4\u591A\u4EBA\u4E70Put\uFF09`);
33642
+ else if (pcrRatio < 0.5) signals.push(`PCR ${pcrRatio.toFixed(2)}\uFF0C\u671F\u6743\u5E02\u573A\u770B\u6DA8\u60C5\u7EEA\u6D53\uFF08\u4E70Call\u4E3A\u4E3B\uFF09`);
33643
+ if (Math.abs(fundingRate) > 1e-3) signals.push(`\u8D44\u91D1\u8D39\u7387 ${(fundingRate * 100).toFixed(3)}%\uFF0C${fundingRate > 0 ? "\u591A\u5934\u62E5\u6324\uFF0C\u6CE8\u610F\u56DE\u8C03" : "\u7A7A\u5934\u62E5\u6324\uFF0C\u6CE8\u610F\u8F67\u7A7A"}`);
33644
+ return toResult({
33645
+ instFamily,
33646
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
33647
+ sentiment: { score, label: sentiment },
33648
+ metrics: {
33649
+ lsRatio: { value: lsRatio.toFixed(2), change: (lsRatio - lsRatioPrev).toFixed(2), label: "\u591A\u7A7A\u6BD4\uFF08\u6563\u6237\uFF09" },
33650
+ topLsRatio: { value: topLsRatio.toFixed(2), label: "\u591A\u7A7A\u6BD4\uFF08Top\u4EA4\u6613\u5458\uFF09" },
33651
+ pcr: { value: pcrRatio.toFixed(2), label: "Put/Call Ratio" },
33652
+ fundingRate: { value: (fundingRate * 100).toFixed(4) + "%", label: "\u8D44\u91D1\u8D39\u7387" }
33653
+ },
33654
+ signals,
33655
+ _summary: `${family} \u5E02\u573A\u60C5\u7EEA: ${sentiment}\uFF08\u8BC4\u5206 ${score}/100\uFF09\u3002${signals.slice(0, 2).join("\u3002")}`,
33656
+ tip: "\u60C5\u7EEA\u8BC4\u5206\u4EC5\u4F5C\u53C2\u8003\uFF0C\u4E0D\u6784\u6210\u6295\u8D44\u5EFA\u8BAE\u3002\u7ED3\u5408\u6280\u672F\u6307\u6807\u548C\u98CE\u9669\u7BA1\u7406\u51B3\u7B56\u3002"
31909
33657
  });
31910
33658
  } catch (e) {
31911
33659
  return toError(e);
@@ -32992,36 +34740,85 @@ function registerAgentHubTools(server) {
32992
34740
 
32993
34741
  // src/index.ts
32994
34742
  async function main() {
32995
- const VERSION = "0.2.44";
34743
+ const VERSION = "0.2.46";
32996
34744
  const server = new McpServer({
32997
34745
  name: "hvip-mcp",
32998
34746
  version: VERSION,
32999
- description: "hvip MCP Server \u2014 304 \u5DE5\u5177\u8986\u76D6 97.7% OKX REST API\uFF0C\u542B\u4EA4\u6613/\u884C\u60C5/\u8D44\u91D1/\u7B56\u7565/\u9884\u6D4B\u5E02\u573A\uFF08\u975E OKX \u5B98\u65B9\u4EA7\u54C1\uFF09\u3002\u4ED3\u5E93: https://github.com/okx-wallet-H/hvip-mcp"
34747
+ description: "hvip MCP Server \u2014 362 \u5DE5\u5177\u8986\u76D6 97.7% OKX REST API\uFF0C\u542B\u4EA4\u6613/\u884C\u60C5/\u8D44\u91D1/\u7B56\u7565/\u9884\u6D4B\u5E02\u573A/\u6280\u672F\u6307\u6807/Smart Money\uFF08\u975E OKX \u5B98\u65B9\u4EA7\u54C1\uFF09\u3002\u4ED3\u5E93: https://github.com/okx-wallet-H/hvip-mcp"
33000
34748
  });
33001
34749
  const auth = getAuth();
33002
- registerMarketTools(server);
33003
- registerPublicTools(server, auth);
33004
- registerStatsTools(server);
33005
- registerSpreadTools(server, auth);
33006
- registerOutcomesTools(server);
33007
- registerAccountTools(server, auth);
33008
- registerTradingTools(server, auth);
33009
- registerAlgoTools(server, auth);
33010
- registerBotTools(server, auth);
33011
- registerCopyTools(server, auth);
33012
- registerSignalTools(server, auth);
33013
- registerFundingTools(server, auth);
33014
- registerSubAccountTools(server, auth);
33015
- registerFinanceTools(server, auth);
33016
- registerAffiliateTools(server, auth);
33017
- registerFiatTools(server, auth);
33018
- registerRfqTools(server, auth);
33019
- registerAgentUtils(server, auth);
33020
- registerXLayerWSTools(server);
33021
- registerWsTools(server);
33022
- registerAgentHubTools(server);
33023
- startAgentHub(parseInt(process.env.WS_AGENT_PORT || "9321"), "0.0.0.0", VERSION);
34750
+ const READ_ONLY = process.env.OKX_READ_ONLY === "true";
34751
+ let skipped = 0;
34752
+ const skipLog = [];
34753
+ function createReadOnlyProxy(s) {
34754
+ const orig = s.tool?.bind?.(s) ?? s.tool.bind(s);
34755
+ s.tool = function(name, ...args) {
34756
+ const risk = classifyRisk(name);
34757
+ if (risk !== "READ") {
34758
+ skipped++;
34759
+ skipLog.push(`${name} (${risk})`);
34760
+ return;
34761
+ }
34762
+ return orig(name, ...args);
34763
+ };
34764
+ return s;
34765
+ }
34766
+ const effectiveServer = READ_ONLY ? createReadOnlyProxy(server) : server;
34767
+ registerMarketTools(effectiveServer);
34768
+ registerPublicTools(effectiveServer, auth);
34769
+ registerStatsTools(effectiveServer);
34770
+ registerSpreadTools(effectiveServer, auth);
34771
+ registerOutcomesTools(effectiveServer);
34772
+ registerAccountTools(effectiveServer, auth);
34773
+ registerTradingTools(effectiveServer, auth);
34774
+ registerAlgoTools(effectiveServer, auth);
34775
+ registerBotTools(effectiveServer, auth);
34776
+ registerCopyTools(effectiveServer, auth);
34777
+ registerSignalTools(effectiveServer, auth);
34778
+ registerFundingTools(effectiveServer, auth);
34779
+ registerSubAccountTools(effectiveServer, auth);
34780
+ registerFinanceTools(effectiveServer, auth);
34781
+ registerAffiliateTools(effectiveServer, auth);
34782
+ registerFiatTools(effectiveServer, auth);
34783
+ registerRfqTools(effectiveServer, auth);
34784
+ registerAgentUtils(effectiveServer, auth);
34785
+ registerIndicatorTools(effectiveServer);
34786
+ registerSmartMoneyTools(effectiveServer, auth);
34787
+ registerXLayerWSTools(effectiveServer);
34788
+ registerWsTools(effectiveServer);
34789
+ registerAgentHubTools(effectiveServer);
34790
+ if (READ_ONLY) {
34791
+ let authStatus = "\u672A\u914D\u7F6E";
34792
+ if (auth) {
34793
+ try {
34794
+ authStatus = "\u5DF2\u914D\u7F6E\uFF08\u53EA\u8BFB\u6A21\u5F0F\uFF0C\u4EC5\u66B4\u9732 READ \u7EA7\u522B\u5DE5\u5177\uFF09";
34795
+ } catch {
34796
+ authStatus = "\u5DF2\u914D\u7F6E";
34797
+ }
34798
+ }
34799
+ process.stderr.write(`[hvip] \u53EA\u8BFB\u6A21\u5F0F\u5DF2\u542F\u7528 | API Key: ${authStatus} | \u5199\u5165\u5DE5\u5177\u5DF2\u8DF3\u8FC7: ${skipped}
34800
+ `);
34801
+ if (skipLog.length > 0 && process.env.NODE_ENV !== "production") {
34802
+ const sample = skipLog.slice(0, 8).join(", ");
34803
+ process.stderr.write(`[hvip] \u8DF3\u8FC7\u793A\u4F8B: ${sample}${skipLog.length > 8 ? ` +${skipLog.length - 8} \u4E2A` : ""}
34804
+ `);
34805
+ }
34806
+ } else if (auth) {
34807
+ try {
34808
+ const cfg = await privateApi.getAccountConfig(auth);
34809
+ const uid = cfg?.[0]?.uid ?? "?";
34810
+ process.stderr.write(`[hvip] API Key \u5DF2\u9A8C\u8BC1 | UID: ${uid} | \u5B8C\u6574\u6A21\u5F0F\uFF08\u5168\u90E8 357 \u5DE5\u5177\uFF09
34811
+ `);
34812
+ } catch {
34813
+ process.stderr.write(`[hvip] API Key \u9A8C\u8BC1\u5931\u8D25 | \u5C06\u4EE5\u672A\u8BA4\u8BC1\u6A21\u5F0F\u542F\u52A8\uFF08\u516C\u5F00\u5DE5\u5177\u53EF\u7528\uFF09
34814
+ `);
34815
+ }
34816
+ } else {
34817
+ process.stderr.write(`[hvip] \u672A\u914D\u7F6E API Key | \u4EC5\u516C\u5F00\u5DE5\u5177\u53EF\u7528
34818
+ `);
34819
+ }
34820
+ startAgentHub(parseInt(process.env.WS_AGENT_PORT || "9321"), "0.2.46.0", VERSION);
33024
34821
  const transport = new StdioServerTransport();
33025
- await server.connect(transport);
34822
+ await effectiveServer.connect(transport);
33026
34823
  }
33027
34824
  main();