@okx_ai/okx-trade-mcp 1.3.2-beta.1 → 1.3.2-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +106 -104
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +9 -9
package/dist/index.js
CHANGED
|
@@ -733,19 +733,19 @@ import * as os3 from "os";
|
|
|
733
733
|
import { execFileSync } from "child_process";
|
|
734
734
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
735
735
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
736
|
-
var
|
|
737
|
-
function
|
|
738
|
-
if (process.env.
|
|
739
|
-
return process.env.
|
|
736
|
+
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
737
|
+
function getPilotBinaryPath() {
|
|
738
|
+
if (process.env.OKX_PILOT_BINARY_PATH) {
|
|
739
|
+
return process.env.OKX_PILOT_BINARY_PATH;
|
|
740
740
|
}
|
|
741
741
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
742
|
-
return join(
|
|
742
|
+
return join(PILOT_BIN_DIR, `okx-pilot${ext}`);
|
|
743
743
|
}
|
|
744
|
-
function
|
|
744
|
+
function execPilotBinary(domain, exclude = [], userAgent) {
|
|
745
745
|
if (!ALLOWED_DOMAIN_RE.test(domain)) {
|
|
746
746
|
return Promise.resolve(null);
|
|
747
747
|
}
|
|
748
|
-
const binPath =
|
|
748
|
+
const binPath = getPilotBinaryPath();
|
|
749
749
|
const args = ["--domain", domain];
|
|
750
750
|
if (exclude.length > 0) {
|
|
751
751
|
args.push("--exclude", exclude.join(","));
|
|
@@ -778,7 +778,7 @@ function execDohBinary(domain, exclude = [], userAgent) {
|
|
|
778
778
|
});
|
|
779
779
|
}
|
|
780
780
|
function getDefaultCachePath() {
|
|
781
|
-
return process.env.
|
|
781
|
+
return process.env.OKX_PILOT_CACHE_PATH || join2(homedir2(), ".okx", "pilot-cache.json");
|
|
782
782
|
}
|
|
783
783
|
function readCache(hostname, cachePath = getDefaultCachePath()) {
|
|
784
784
|
try {
|
|
@@ -832,7 +832,7 @@ function getActiveFailedNodes(nodes) {
|
|
|
832
832
|
const now = Date.now();
|
|
833
833
|
return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
|
|
834
834
|
}
|
|
835
|
-
function
|
|
835
|
+
function resolvePilot(hostname, cachePath) {
|
|
836
836
|
const entry = readCache(hostname, cachePath);
|
|
837
837
|
if (entry) {
|
|
838
838
|
if (entry.mode === "direct") {
|
|
@@ -844,53 +844,53 @@ function resolveDoh(hostname, cachePath) {
|
|
|
844
844
|
}
|
|
845
845
|
return { mode: null, node: null };
|
|
846
846
|
}
|
|
847
|
-
async function
|
|
847
|
+
async function reResolvePilot(hostname, failedIp, userAgent, cachePath) {
|
|
848
848
|
const entry = readCache(hostname, cachePath);
|
|
849
849
|
const active = getActiveFailedNodes(entry?.failedNodes);
|
|
850
850
|
const now = Date.now();
|
|
851
851
|
const alreadyFailed = failedIp && active.some((n) => n.ip === failedIp);
|
|
852
852
|
const failedNodes = failedIp && !alreadyFailed ? [...active, { ip: failedIp, failedAt: now }] : active;
|
|
853
853
|
const excludeIps = failedNodes.map((n) => n.ip);
|
|
854
|
-
const node = await
|
|
854
|
+
const node = await execPilotBinary(hostname, excludeIps, userAgent);
|
|
855
855
|
return classifyAndCache(node, hostname, failedNodes, cachePath);
|
|
856
856
|
}
|
|
857
857
|
function vlog(message) {
|
|
858
858
|
process.stderr.write(`[verbose] ${message}
|
|
859
859
|
`);
|
|
860
860
|
}
|
|
861
|
-
var
|
|
861
|
+
var PilotManager = class {
|
|
862
862
|
opts;
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
863
|
+
// Pilot proxy state (lazy-resolved on first request)
|
|
864
|
+
pilotResolved = false;
|
|
865
|
+
pilotRetried = false;
|
|
866
866
|
directUnverified = false;
|
|
867
867
|
// The first direct connection has not yet been verified
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
868
|
+
pilotNode = null;
|
|
869
|
+
pilotAgent = null;
|
|
870
|
+
pilotBaseUrl = null;
|
|
871
871
|
constructor(opts) {
|
|
872
872
|
this.opts = opts;
|
|
873
873
|
}
|
|
874
874
|
/**
|
|
875
|
-
* Lazily resolve the
|
|
875
|
+
* Lazily resolve the Pilot proxy node on the first request.
|
|
876
876
|
* Uses cache-first strategy via the resolver.
|
|
877
877
|
*/
|
|
878
|
-
|
|
879
|
-
if (this.
|
|
880
|
-
this.
|
|
878
|
+
preparePilot() {
|
|
879
|
+
if (this.pilotResolved || this.opts.hasCustomProxy) return;
|
|
880
|
+
this.pilotResolved = true;
|
|
881
881
|
try {
|
|
882
882
|
const { hostname, protocol } = new URL(this.opts.baseUrl);
|
|
883
|
-
const result =
|
|
883
|
+
const result = resolvePilot(hostname);
|
|
884
884
|
if (!result.mode) {
|
|
885
885
|
this.directUnverified = true;
|
|
886
886
|
if (this.opts.verbose) {
|
|
887
|
-
vlog("
|
|
887
|
+
vlog("Pilot: no cache, trying direct connection first");
|
|
888
888
|
}
|
|
889
889
|
return;
|
|
890
890
|
}
|
|
891
891
|
if (result.mode === "direct") {
|
|
892
892
|
if (this.opts.verbose) {
|
|
893
|
-
vlog("
|
|
893
|
+
vlog("Pilot: mode=direct (overseas or cached), using direct connection");
|
|
894
894
|
}
|
|
895
895
|
return;
|
|
896
896
|
}
|
|
@@ -900,57 +900,57 @@ var DohManager = class {
|
|
|
900
900
|
} catch (err) {
|
|
901
901
|
if (this.opts.verbose) {
|
|
902
902
|
const cause = err instanceof Error ? err.message : String(err);
|
|
903
|
-
vlog(`
|
|
903
|
+
vlog(`Pilot resolution failed, falling back to direct: ${cause}`);
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
}
|
|
907
907
|
/** Get connection parameters for the current request. */
|
|
908
908
|
getConnectionParams() {
|
|
909
|
-
const baseUrl = this.
|
|
909
|
+
const baseUrl = this.pilotNode ? this.pilotBaseUrl : this.opts.baseUrl;
|
|
910
910
|
const result = { baseUrl };
|
|
911
|
-
if (this.
|
|
912
|
-
result.dispatcher = this.
|
|
911
|
+
if (this.pilotAgent) {
|
|
912
|
+
result.dispatcher = this.pilotAgent;
|
|
913
913
|
}
|
|
914
|
-
if (this.
|
|
915
|
-
result.userAgent = this.
|
|
914
|
+
if (this.pilotNode) {
|
|
915
|
+
result.userAgent = this.pilotUserAgent;
|
|
916
916
|
}
|
|
917
917
|
return result;
|
|
918
918
|
}
|
|
919
|
-
/** Whether a
|
|
919
|
+
/** Whether a Pilot proxy node is currently active. */
|
|
920
920
|
get isProxyActive() {
|
|
921
|
-
return this.
|
|
921
|
+
return this.pilotNode !== null;
|
|
922
922
|
}
|
|
923
923
|
/** Whether we have already retried after network failure. */
|
|
924
924
|
get hasRetried() {
|
|
925
|
-
return this.
|
|
925
|
+
return this.pilotRetried;
|
|
926
926
|
}
|
|
927
927
|
/**
|
|
928
928
|
* Handle network failure: re-resolve with --exclude and retry once.
|
|
929
929
|
* Returns true if retry should proceed, false if already retried.
|
|
930
930
|
*/
|
|
931
931
|
async handleNetworkFailure() {
|
|
932
|
-
if (this.
|
|
933
|
-
this.
|
|
934
|
-
const failedIp = this.
|
|
932
|
+
if (this.pilotRetried) return false;
|
|
933
|
+
this.pilotRetried = true;
|
|
934
|
+
const failedIp = this.pilotNode?.ip ?? "";
|
|
935
935
|
const { hostname, protocol } = new URL(this.opts.baseUrl);
|
|
936
|
-
this.
|
|
937
|
-
this.
|
|
938
|
-
this.
|
|
936
|
+
this.pilotNode = null;
|
|
937
|
+
this.pilotAgent = null;
|
|
938
|
+
this.pilotBaseUrl = null;
|
|
939
939
|
if (!failedIp) this.directUnverified = false;
|
|
940
940
|
if (this.opts.verbose) {
|
|
941
|
-
vlog(failedIp ? `
|
|
941
|
+
vlog(failedIp ? `Pilot: proxy node ${failedIp} failed, re-resolving with --exclude` : "Pilot: direct connection failed, calling binary for Pilot resolution");
|
|
942
942
|
}
|
|
943
943
|
try {
|
|
944
|
-
const result = await
|
|
944
|
+
const result = await reResolvePilot(hostname, failedIp, this.pilotUserAgent);
|
|
945
945
|
if (result.mode === "proxy" && result.node) {
|
|
946
946
|
this.applyNode(result.node, protocol);
|
|
947
|
-
this.
|
|
947
|
+
this.pilotRetried = false;
|
|
948
948
|
return true;
|
|
949
949
|
}
|
|
950
950
|
} catch {
|
|
951
951
|
}
|
|
952
952
|
if (this.opts.verbose) {
|
|
953
|
-
vlog("
|
|
953
|
+
vlog("Pilot: re-resolution failed or switched to direct, retrying with direct connection");
|
|
954
954
|
}
|
|
955
955
|
return true;
|
|
956
956
|
}
|
|
@@ -959,7 +959,7 @@ var DohManager = class {
|
|
|
959
959
|
* (Even if the business response is an error, the network path is valid.)
|
|
960
960
|
*/
|
|
961
961
|
cacheDirectIfNeeded() {
|
|
962
|
-
if (!this.directUnverified || this.
|
|
962
|
+
if (!this.directUnverified || this.pilotNode) return;
|
|
963
963
|
this.directUnverified = false;
|
|
964
964
|
const { hostname } = new URL(this.opts.baseUrl);
|
|
965
965
|
writeCache(hostname, {
|
|
@@ -969,25 +969,25 @@ var DohManager = class {
|
|
|
969
969
|
updatedAt: Date.now()
|
|
970
970
|
});
|
|
971
971
|
if (this.opts.verbose) {
|
|
972
|
-
vlog("
|
|
972
|
+
vlog("Pilot: direct connection succeeded, cached mode=direct");
|
|
973
973
|
}
|
|
974
974
|
}
|
|
975
|
-
/** User-Agent for
|
|
976
|
-
get
|
|
975
|
+
/** User-Agent for Pilot proxy requests: OKX/@okx_ai/{packageName}/{version} */
|
|
976
|
+
get pilotUserAgent() {
|
|
977
977
|
return `OKX/@okx_ai/${this.opts.packageUserAgent ?? "unknown"}`;
|
|
978
978
|
}
|
|
979
979
|
/**
|
|
980
|
-
* Apply a
|
|
980
|
+
* Apply a Pilot node: set up the custom Agent + base URL.
|
|
981
981
|
*
|
|
982
982
|
* node.ip may be a real IP or a domain (CNAME like *.aliyunddos1021.com).
|
|
983
983
|
* - Real IP → use directly in lookup callback
|
|
984
984
|
* - Domain → dns.lookup on every connection to get a fresh IP
|
|
985
985
|
*/
|
|
986
986
|
applyNode(node, protocol) {
|
|
987
|
-
this.
|
|
988
|
-
this.
|
|
987
|
+
this.pilotNode = node;
|
|
988
|
+
this.pilotBaseUrl = `${protocol}//${node.host}`;
|
|
989
989
|
const nodeIpIsRealIp = !!isIP(node.ip);
|
|
990
|
-
this.
|
|
990
|
+
this.pilotAgent = new Agent({
|
|
991
991
|
connect: {
|
|
992
992
|
lookup: (_hostname, options, callback) => {
|
|
993
993
|
if (nodeIpIsRealIp) {
|
|
@@ -1011,7 +1011,7 @@ var DohManager = class {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
});
|
|
1013
1013
|
if (this.opts.verbose) {
|
|
1014
|
-
vlog(`
|
|
1014
|
+
vlog(`Pilot proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
|
|
1015
1015
|
}
|
|
1016
1016
|
}
|
|
1017
1017
|
};
|
|
@@ -1233,14 +1233,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1233
1233
|
config;
|
|
1234
1234
|
rateLimiter;
|
|
1235
1235
|
dispatcher;
|
|
1236
|
-
|
|
1236
|
+
pilot;
|
|
1237
1237
|
constructor(config) {
|
|
1238
1238
|
this.config = config;
|
|
1239
1239
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
1240
1240
|
if (config.proxyUrl) {
|
|
1241
1241
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1242
1242
|
}
|
|
1243
|
-
this.
|
|
1243
|
+
this.pilot = new PilotManager({
|
|
1244
1244
|
baseUrl: config.baseUrl,
|
|
1245
1245
|
packageUserAgent: config.userAgent,
|
|
1246
1246
|
verbose: config.verbose,
|
|
@@ -1423,12 +1423,12 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1423
1423
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1424
1424
|
*/
|
|
1425
1425
|
async privatePostBinary(path4, body, opts) {
|
|
1426
|
-
this.
|
|
1426
|
+
this.pilot.preparePilot();
|
|
1427
1427
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1428
1428
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1429
1429
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1430
1430
|
const endpoint = `POST ${path4}`;
|
|
1431
|
-
const conn = this.
|
|
1431
|
+
const conn = this.pilot.getConnectionParams();
|
|
1432
1432
|
this.logRequest("POST", `${conn.baseUrl}${path4}`, "private");
|
|
1433
1433
|
const reqConfig = { method: "POST", path: path4, auth: "private" };
|
|
1434
1434
|
const headers = this.buildHeaders(reqConfig, path4, bodyJson, getNow());
|
|
@@ -1440,13 +1440,15 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1440
1440
|
try {
|
|
1441
1441
|
response = await this.fetchBinary(path4, endpoint, headers, bodyJson, t0);
|
|
1442
1442
|
} catch (error) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1443
|
+
try {
|
|
1444
|
+
await this.pilot.handleNetworkFailure();
|
|
1445
|
+
} catch {
|
|
1446
|
+
}
|
|
1445
1447
|
throw error;
|
|
1446
1448
|
}
|
|
1447
1449
|
const elapsed = Date.now() - t0;
|
|
1448
1450
|
const traceId = extractTraceId(response.headers);
|
|
1449
|
-
this.
|
|
1451
|
+
this.pilot.cacheDirectIfNeeded();
|
|
1450
1452
|
if (!response.ok) {
|
|
1451
1453
|
const text = await response.text();
|
|
1452
1454
|
this.logResponse(response.status, text.length, elapsed, traceId, String(response.status));
|
|
@@ -1472,15 +1474,15 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1472
1474
|
/**
|
|
1473
1475
|
* Send an unauthenticated GET request and return the raw binary response.
|
|
1474
1476
|
* Used for pre-signed download URLs where auth is embedded in the token.
|
|
1475
|
-
* Inherits proxy, timeout,
|
|
1477
|
+
* Inherits proxy, timeout, Pilot, and verbose capabilities from the client.
|
|
1476
1478
|
*/
|
|
1477
1479
|
async publicGetBinary(path4, query, opts) {
|
|
1478
|
-
this.
|
|
1480
|
+
this.pilot.preparePilot();
|
|
1479
1481
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1480
1482
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1481
1483
|
const queryString = buildQueryString(query);
|
|
1482
1484
|
const requestPath = queryString ? `${path4}?${queryString}` : path4;
|
|
1483
|
-
const conn = this.
|
|
1485
|
+
const conn = this.pilot.getConnectionParams();
|
|
1484
1486
|
const url = `${conn.baseUrl}${requestPath}`;
|
|
1485
1487
|
this.logRequest("GET", url, "public");
|
|
1486
1488
|
const headers = new Headers({ Accept: "application/octet-stream" });
|
|
@@ -1496,13 +1498,15 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1496
1498
|
dispatcher: this.dispatcher ?? conn.dispatcher
|
|
1497
1499
|
});
|
|
1498
1500
|
} catch (error) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
+
try {
|
|
1502
|
+
await this.pilot.handleNetworkFailure();
|
|
1503
|
+
} catch {
|
|
1504
|
+
}
|
|
1501
1505
|
throw new NetworkError(`Failed to call OKX endpoint GET ${path4}.`, `GET ${path4}`, error);
|
|
1502
1506
|
}
|
|
1503
1507
|
const elapsed = Date.now() - t0;
|
|
1504
1508
|
const traceId = extractTraceId(response.headers);
|
|
1505
|
-
this.
|
|
1509
|
+
this.pilot.cacheDirectIfNeeded();
|
|
1506
1510
|
if (!response.ok) {
|
|
1507
1511
|
const text = await response.text();
|
|
1508
1512
|
this.logResponse(response.status, text.length, elapsed, traceId, String(response.status));
|
|
@@ -1527,7 +1531,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1527
1531
|
}
|
|
1528
1532
|
/** Execute fetch for binary endpoint, wrapping network errors. */
|
|
1529
1533
|
async fetchBinary(path4, endpoint, headers, bodyJson, t0) {
|
|
1530
|
-
const conn = this.
|
|
1534
|
+
const conn = this.pilot.getConnectionParams();
|
|
1531
1535
|
try {
|
|
1532
1536
|
const fetchOptions = {
|
|
1533
1537
|
method: "POST",
|
|
@@ -1574,16 +1578,16 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1574
1578
|
// JSON request
|
|
1575
1579
|
// ---------------------------------------------------------------------------
|
|
1576
1580
|
/**
|
|
1577
|
-
* Handle network error during a JSON request: refresh
|
|
1581
|
+
* Handle network error during a JSON request: refresh Pilot and maybe retry.
|
|
1578
1582
|
* Always either returns a retry result or throws NetworkError.
|
|
1579
1583
|
*/
|
|
1580
1584
|
async handleRequestNetworkError(error, reqConfig, requestPath, t0) {
|
|
1581
|
-
if (!this.
|
|
1585
|
+
if (!this.pilot.hasRetried) {
|
|
1582
1586
|
if (this.config.verbose) {
|
|
1583
1587
|
const cause = error instanceof Error ? error.message : String(error);
|
|
1584
|
-
vlog2(`Network failure, refreshing
|
|
1588
|
+
vlog2(`Network failure, refreshing Pilot: ${cause}`);
|
|
1585
1589
|
}
|
|
1586
|
-
const shouldRetry = await this.
|
|
1590
|
+
const shouldRetry = await this.pilot.handleNetworkFailure();
|
|
1587
1591
|
if (shouldRetry && (reqConfig.method === "GET" || reqConfig.retryOnNetworkError)) {
|
|
1588
1592
|
return this.request(reqConfig);
|
|
1589
1593
|
}
|
|
@@ -1600,10 +1604,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1600
1604
|
);
|
|
1601
1605
|
}
|
|
1602
1606
|
async request(reqConfig) {
|
|
1603
|
-
this.
|
|
1607
|
+
this.pilot.preparePilot();
|
|
1604
1608
|
const queryString = buildQueryString(reqConfig.query);
|
|
1605
1609
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1606
|
-
const conn = this.
|
|
1610
|
+
const conn = this.pilot.getConnectionParams();
|
|
1607
1611
|
const url = `${conn.baseUrl}${requestPath}`;
|
|
1608
1612
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1609
1613
|
const timestamp = getNow();
|
|
@@ -1632,7 +1636,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1632
1636
|
const rawText = await response.text();
|
|
1633
1637
|
const elapsed = Date.now() - t0;
|
|
1634
1638
|
const traceId = extractTraceId(response.headers);
|
|
1635
|
-
this.
|
|
1639
|
+
this.pilot.cacheDirectIfNeeded();
|
|
1636
1640
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1637
1641
|
}
|
|
1638
1642
|
};
|
|
@@ -2211,6 +2215,10 @@ function registerAccountTools() {
|
|
|
2211
2215
|
showValuation: {
|
|
2212
2216
|
type: "boolean",
|
|
2213
2217
|
description: "Include total asset valuation breakdown by account type (trading/funding/earn). Default false."
|
|
2218
|
+
},
|
|
2219
|
+
valuationCcy: {
|
|
2220
|
+
type: "string",
|
|
2221
|
+
description: "Currency used to denominate the total asset valuation (e.g. USDT, BTC). Default USDT. Only applies when showValuation=true."
|
|
2214
2222
|
}
|
|
2215
2223
|
}
|
|
2216
2224
|
},
|
|
@@ -2218,6 +2226,7 @@ function registerAccountTools() {
|
|
|
2218
2226
|
const args = asRecord(rawArgs);
|
|
2219
2227
|
const ccy = readString(args, "ccy");
|
|
2220
2228
|
const showValuation = readBoolean(args, "showValuation");
|
|
2229
|
+
const valuationCcy = readString(args, "valuationCcy") ?? "USDT";
|
|
2221
2230
|
if (showValuation) {
|
|
2222
2231
|
const balanceResp2 = await context.client.privateGet(
|
|
2223
2232
|
"/api/v5/asset/balances",
|
|
@@ -2225,16 +2234,26 @@ function registerAccountTools() {
|
|
|
2225
2234
|
privateRateLimit("account_get_asset_balance", 6)
|
|
2226
2235
|
);
|
|
2227
2236
|
let valuationData = null;
|
|
2237
|
+
let valuationError;
|
|
2228
2238
|
try {
|
|
2229
2239
|
const valuationResp = await context.client.privateGet(
|
|
2230
2240
|
"/api/v5/asset/asset-valuation",
|
|
2231
|
-
{},
|
|
2241
|
+
{ ccy: valuationCcy },
|
|
2232
2242
|
privateRateLimit("account_get_asset_valuation", 1)
|
|
2233
2243
|
);
|
|
2234
2244
|
valuationData = valuationResp.data;
|
|
2235
|
-
} catch {
|
|
2245
|
+
} catch (err) {
|
|
2246
|
+
valuationError = err instanceof Error ? err.message : String(err);
|
|
2247
|
+
}
|
|
2248
|
+
const valuationResult = {
|
|
2249
|
+
...normalizeResponse(balanceResp2),
|
|
2250
|
+
valuation: valuationData,
|
|
2251
|
+
valuationCcy
|
|
2252
|
+
};
|
|
2253
|
+
if (valuationError !== void 0) {
|
|
2254
|
+
valuationResult["valuationError"] = valuationError;
|
|
2236
2255
|
}
|
|
2237
|
-
return
|
|
2256
|
+
return valuationResult;
|
|
2238
2257
|
}
|
|
2239
2258
|
const balanceResp = await context.client.privateGet(
|
|
2240
2259
|
"/api/v5/asset/balances",
|
|
@@ -6214,9 +6233,9 @@ function registerEventContractTools() {
|
|
|
6214
6233
|
var PATH_LEADERBOARD = "/api/v5/orbit/public/leaderboard";
|
|
6215
6234
|
var PATH_POSITION_CURRENT = "/api/v5/orbit/public/position-current";
|
|
6216
6235
|
var PATH_TRADE_RECORDS = "/api/v5/orbit/public/trade-records";
|
|
6217
|
-
var PATH_OVERVIEW = "/api/v5/journal/
|
|
6218
|
-
var PATH_SIGNAL = "/api/v5/journal/
|
|
6219
|
-
var PATH_SIGNAL_HISTORY = "/api/v5/journal/
|
|
6236
|
+
var PATH_OVERVIEW = "/api/v5/journal/smartmoney/overview";
|
|
6237
|
+
var PATH_SIGNAL = "/api/v5/journal/smartmoney/signal";
|
|
6238
|
+
var PATH_SIGNAL_HISTORY = "/api/v5/journal/smartmoney/signal-history";
|
|
6220
6239
|
var SIGNAL_POOL_FILTER_PROPS = {
|
|
6221
6240
|
sortType: {
|
|
6222
6241
|
type: "string",
|
|
@@ -6286,23 +6305,6 @@ function extractLeaderboardData(data) {
|
|
|
6286
6305
|
}
|
|
6287
6306
|
return [];
|
|
6288
6307
|
}
|
|
6289
|
-
var SMARTMONEY_DEMO_MESSAGE = "Smart Money features are not available in demo/simulated trading mode.";
|
|
6290
|
-
var SMARTMONEY_DEMO_SUGGESTION = "Switch to a live profile to use Smart Money features.";
|
|
6291
|
-
function withSmartmoneyDemoGuard(tool) {
|
|
6292
|
-
const originalHandler = tool.handler;
|
|
6293
|
-
return {
|
|
6294
|
-
...tool,
|
|
6295
|
-
handler: async (args, context) => {
|
|
6296
|
-
if (context.config.demo) {
|
|
6297
|
-
throw new ConfigError(
|
|
6298
|
-
SMARTMONEY_DEMO_MESSAGE,
|
|
6299
|
-
SMARTMONEY_DEMO_SUGGESTION
|
|
6300
|
-
);
|
|
6301
|
-
}
|
|
6302
|
-
return originalHandler(args, context);
|
|
6303
|
-
}
|
|
6304
|
-
};
|
|
6305
|
-
}
|
|
6306
6308
|
function registerSmartmoneyTools() {
|
|
6307
6309
|
const tools = [
|
|
6308
6310
|
/* ---------- 1. Overview ---------- */
|
|
@@ -6373,18 +6375,18 @@ function registerSmartmoneyTools() {
|
|
|
6373
6375
|
{
|
|
6374
6376
|
name: "smartmoney_get_signal",
|
|
6375
6377
|
module: "smartmoney",
|
|
6376
|
-
description: "Single-currency consensus signal: long/short ratio, entry prices, trend, capital flow.
|
|
6378
|
+
description: "Single-currency consensus signal: long/short ratio, entry prices, trend, capital flow. Prefer instId (e.g. BTC-USDT-SWAP); instCcy is accepted but may return empty \u2014 use instId for reliable results. Pass ts=Date.now() for latest data, or dataVersion from a prior call. For multi-currency overview, use smartmoney_get_overview. For timeline, use smartmoney_get_signal_history.",
|
|
6377
6379
|
isWrite: false,
|
|
6378
6380
|
inputSchema: {
|
|
6379
6381
|
type: "object",
|
|
6380
6382
|
properties: {
|
|
6381
6383
|
instId: {
|
|
6382
6384
|
type: "string",
|
|
6383
|
-
description: "e.g. BTC-USDT-SWAP
|
|
6385
|
+
description: "Recommended. e.g. BTC-USDT-SWAP"
|
|
6384
6386
|
},
|
|
6385
6387
|
instCcy: {
|
|
6386
6388
|
type: "string",
|
|
6387
|
-
description: "e.g. BTC, SPOT/SWAP only
|
|
6389
|
+
description: "e.g. BTC, SPOT/SWAP only. May return empty \u2014 prefer instId."
|
|
6388
6390
|
},
|
|
6389
6391
|
dataVersion: {
|
|
6390
6392
|
type: "string",
|
|
@@ -6456,11 +6458,11 @@ function registerSmartmoneyTools() {
|
|
|
6456
6458
|
},
|
|
6457
6459
|
granularity: {
|
|
6458
6460
|
type: "string",
|
|
6459
|
-
description: "1h or 1d"
|
|
6461
|
+
description: "1h or 1d (default 1h)"
|
|
6460
6462
|
},
|
|
6461
6463
|
limit: {
|
|
6462
6464
|
type: "string",
|
|
6463
|
-
description: "Data points 1-500"
|
|
6465
|
+
description: "Data points 1-500 (default 24)"
|
|
6464
6466
|
},
|
|
6465
6467
|
...SIGNAL_POOL_FILTER_PROPS
|
|
6466
6468
|
},
|
|
@@ -6604,7 +6606,7 @@ function registerSmartmoneyTools() {
|
|
|
6604
6606
|
}
|
|
6605
6607
|
}
|
|
6606
6608
|
];
|
|
6607
|
-
return tools
|
|
6609
|
+
return tools;
|
|
6608
6610
|
}
|
|
6609
6611
|
function buildContractTradeTools(cfg) {
|
|
6610
6612
|
const { prefix, module, label, instTypes, instIdExample } = cfg;
|
|
@@ -10383,7 +10385,7 @@ var _require = createRequire(import.meta.url);
|
|
|
10383
10385
|
var pkg = _require("../package.json");
|
|
10384
10386
|
var SERVER_NAME = "okx-trade-mcp";
|
|
10385
10387
|
var SERVER_VERSION = pkg.version;
|
|
10386
|
-
var GIT_HASH = true ? "
|
|
10388
|
+
var GIT_HASH = true ? "e0ee5a96" : "dev";
|
|
10387
10389
|
|
|
10388
10390
|
// src/server.ts
|
|
10389
10391
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|