@yuants/vendor-binance 0.14.3 → 0.14.4

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.
@@ -1,12 +1,14 @@
1
1
  var _a;
2
- import { fetch } from '@yuants/http-services';
2
+ import { fetch, selectHTTPProxyIpRoundRobin } from '@yuants/http-services';
3
3
  import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';
4
- import { encodeHex, formatTime, HmacSHA256, newError, tokenBucket } from '@yuants/utils';
4
+ import { encodeHex, encodePath, formatTime, HmacSHA256, newError, tokenBucket } from '@yuants/utils';
5
5
  const MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');
6
6
  const MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');
7
7
  const terminal = Terminal.fromNodeEnv();
8
8
  const shouldUseHttpProxy = process.env.USE_HTTP_PROXY === 'true';
9
9
  const fetchImpl = shouldUseHttpProxy ? fetch : (_a = globalThis.fetch) !== null && _a !== void 0 ? _a : fetch;
10
+ const MISSING_PUBLIC_IP_LOG_INTERVAL = 3600000;
11
+ const missingPublicIpLogAtByTerminalId = new Map();
10
12
  if (shouldUseHttpProxy) {
11
13
  globalThis.fetch = fetch;
12
14
  }
@@ -30,6 +32,27 @@ export const unifiedOrderAPIBucket = tokenBucket('order/unified/minute', {
30
32
  refillInterval: 60000,
31
33
  refillAmount: 1200,
32
34
  });
35
+ export const buildTokenBucketKey = (baseKey, ip) => encodePath([baseKey, ip]);
36
+ const resolveLocalPublicIp = () => {
37
+ var _a, _b, _c;
38
+ const ip = (_b = (_a = terminal.terminalInfo.tags) === null || _a === void 0 ? void 0 : _a.public_ip) === null || _b === void 0 ? void 0 : _b.trim();
39
+ if (ip)
40
+ return ip;
41
+ const now = Date.now();
42
+ const lastLoggedAt = (_c = missingPublicIpLogAtByTerminalId.get(terminal.terminal_id)) !== null && _c !== void 0 ? _c : 0;
43
+ if (now - lastLoggedAt > MISSING_PUBLIC_IP_LOG_INTERVAL) {
44
+ missingPublicIpLogAtByTerminalId.set(terminal.terminal_id, now);
45
+ console.info(formatTime(Date.now()), 'missing terminal public_ip tag, fallback to public-ip-unknown');
46
+ }
47
+ return 'public-ip-unknown';
48
+ };
49
+ export const createRequestContext = () => {
50
+ if (shouldUseHttpProxy) {
51
+ const ip = selectHTTPProxyIpRoundRobin(terminal);
52
+ return { ip };
53
+ }
54
+ return { ip: resolveLocalPublicIp() };
55
+ };
33
56
  // 每个接口单独进行主动限流控制
34
57
  const mapPathToRetryAfterUntil = {};
35
58
  export const isApiError = (value) => typeof (value === null || value === void 0 ? void 0 : value.code) === 'number' && typeof (value === null || value === void 0 ? void 0 : value.msg) === 'string';
@@ -48,7 +71,11 @@ const appendParams = (url, params) => {
48
71
  url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));
49
72
  }
50
73
  };
51
- const callApi = async (method, endpoint, params, credential) => {
74
+ const callApi = async (method, endpoint, params, credential, requestContext) => {
75
+ var _a;
76
+ if (shouldUseHttpProxy && !requestContext) {
77
+ throw newError('E_PROXY_TARGET_NOT_FOUND', { reason: 'Missing request context' });
78
+ }
52
79
  const url = new URL(endpoint);
53
80
  const normalizedParams = Object.assign({}, params);
54
81
  if (credential) {
@@ -88,10 +115,20 @@ const callApi = async (method, endpoint, params, credential) => {
88
115
  delete mapPathToRetryAfterUntil[endpoint];
89
116
  }
90
117
  MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();
91
- const res = await fetchImpl(url.href, {
92
- method,
93
- headers,
94
- });
118
+ const proxyIp = shouldUseHttpProxy
119
+ ? (_a = requestContext === null || requestContext === void 0 ? void 0 : requestContext.ip) !== null && _a !== void 0 ? _a : selectHTTPProxyIpRoundRobin(terminal)
120
+ : undefined;
121
+ const res = await fetchImpl(url.href, shouldUseHttpProxy
122
+ ? {
123
+ method,
124
+ headers,
125
+ labels: proxyIp ? { ip: proxyIp } : undefined,
126
+ terminal,
127
+ }
128
+ : {
129
+ method,
130
+ headers,
131
+ });
95
132
  const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');
96
133
  const retryAfter = res.headers.get('Retry-After');
97
134
  if (retryAfter) {
@@ -103,8 +140,8 @@ const callApi = async (method, endpoint, params, credential) => {
103
140
  }
104
141
  return res.json();
105
142
  };
106
- export const requestPublic = (method, endpoint, params) => callApi(method, endpoint, params);
107
- export const requestPrivate = (credential, method, endpoint, params) => callApi(method, endpoint, params, credential);
143
+ export const requestPublic = (method, endpoint, params, requestContext) => callApi(method, endpoint, params, undefined, requestContext);
144
+ export const requestPrivate = (credential, method, endpoint, params, requestContext) => callApi(method, endpoint, params, credential, requestContext);
108
145
  export const getDefaultCredential = () => {
109
146
  const access_key = process.env.ACCESS_KEY;
110
147
  const secret_key = process.env.SECRET_KEY;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { fetch } from '@yuants/http-services';\nimport { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, formatTime, HmacSHA256, newError, tokenBucket } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\nconst shouldUseHttpProxy = process.env.USE_HTTP_PROXY === 'true';\nconst fetchImpl = shouldUseHttpProxy ? fetch : globalThis.fetch ?? fetch;\n\nif (shouldUseHttpProxy) {\n globalThis.fetch = fetch;\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\nexport const spotAPIBucket = tokenBucket('api.binance.com', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n\nexport const futureAPIBucket = tokenBucket('fapi.binance.com', {\n capacity: 2400,\n refillInterval: 60_000,\n refillAmount: 2400,\n});\n\nexport const unifiedAPIBucket = tokenBucket('papi.binance.com', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n\nexport const unifiedOrderAPIBucket = tokenBucket('order/unified/minute', {\n capacity: 1200,\n refillInterval: 60_000,\n refillAmount: 1200,\n});\n\n// 每个接口单独进行主动限流控制\nconst mapPathToRetryAfterUntil: Record<string, number> = {};\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n): Promise<T> => {\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(formatTime(Date.now()), 'request', method, url.host, url.pathname);\n } else {\n console.info(formatTime(Date.now()), 'request', method, url.host, url.pathname);\n }\n\n const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n // 主动限流\n throw newError('ACTIVE_RATE_LIMIT', {\n wait_time: `${retryAfterUntil - Date.now()}ms`,\n retryAfterUntil,\n url: `${url.host}${url.pathname}`,\n });\n }\n delete mapPathToRetryAfterUntil[endpoint];\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const res = await fetchImpl(url.href, {\n method,\n headers,\n });\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.host,\n url.pathname,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ path: endpoint, terminal_id: terminal.terminal_id }).set(\n +usedWeight1M,\n );\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(method: HttpMethod, endpoint: string, params?: RequestParams) =>\n callApi<T>(method, endpoint, params);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n) => callApi<T>(method, endpoint, params, credential);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { fetch, selectHTTPProxyIpRoundRobin } from '@yuants/http-services';\nimport { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, encodePath, formatTime, HmacSHA256, newError, tokenBucket } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\nconst shouldUseHttpProxy = process.env.USE_HTTP_PROXY === 'true';\nconst fetchImpl = shouldUseHttpProxy ? fetch : globalThis.fetch ?? fetch;\nconst MISSING_PUBLIC_IP_LOG_INTERVAL = 3_600_000;\nconst missingPublicIpLogAtByTerminalId = new Map<string, number>();\n\nif (shouldUseHttpProxy) {\n globalThis.fetch = fetch;\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface IRequestContext {\n ip: string;\n}\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\nexport const spotAPIBucket = tokenBucket('api.binance.com', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n\nexport const futureAPIBucket = tokenBucket('fapi.binance.com', {\n capacity: 2400,\n refillInterval: 60_000,\n refillAmount: 2400,\n});\n\nexport const unifiedAPIBucket = tokenBucket('papi.binance.com', {\n capacity: 6000,\n refillInterval: 60_000,\n refillAmount: 6000,\n});\n\nexport const unifiedOrderAPIBucket = tokenBucket('order/unified/minute', {\n capacity: 1200,\n refillInterval: 60_000,\n refillAmount: 1200,\n});\n\nexport const buildTokenBucketKey = (baseKey: string, ip: string): string => encodePath([baseKey, ip]);\n\nconst resolveLocalPublicIp = (): string => {\n const ip = terminal.terminalInfo.tags?.public_ip?.trim();\n if (ip) return ip;\n const now = Date.now();\n const lastLoggedAt = missingPublicIpLogAtByTerminalId.get(terminal.terminal_id) ?? 0;\n if (now - lastLoggedAt > MISSING_PUBLIC_IP_LOG_INTERVAL) {\n missingPublicIpLogAtByTerminalId.set(terminal.terminal_id, now);\n console.info(formatTime(Date.now()), 'missing terminal public_ip tag, fallback to public-ip-unknown');\n }\n return 'public-ip-unknown';\n};\n\nexport const createRequestContext = (): IRequestContext => {\n if (shouldUseHttpProxy) {\n const ip = selectHTTPProxyIpRoundRobin(terminal);\n return { ip };\n }\n return { ip: resolveLocalPublicIp() };\n};\n\n// 每个接口单独进行主动限流控制\nconst mapPathToRetryAfterUntil: Record<string, number> = {};\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n requestContext?: IRequestContext,\n): Promise<T> => {\n if (shouldUseHttpProxy && !requestContext) {\n throw newError('E_PROXY_TARGET_NOT_FOUND', { reason: 'Missing request context' });\n }\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(formatTime(Date.now()), 'request', method, url.host, url.pathname);\n } else {\n console.info(formatTime(Date.now()), 'request', method, url.host, url.pathname);\n }\n\n const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n // 主动限流\n throw newError('ACTIVE_RATE_LIMIT', {\n wait_time: `${retryAfterUntil - Date.now()}ms`,\n retryAfterUntil,\n url: `${url.host}${url.pathname}`,\n });\n }\n delete mapPathToRetryAfterUntil[endpoint];\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const proxyIp = shouldUseHttpProxy\n ? requestContext?.ip ?? selectHTTPProxyIpRoundRobin(terminal)\n : undefined;\n const res = await fetchImpl(\n url.href,\n shouldUseHttpProxy\n ? {\n method,\n headers,\n labels: proxyIp ? { ip: proxyIp } : undefined,\n terminal,\n }\n : {\n method,\n headers,\n },\n );\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.host,\n url.pathname,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ path: endpoint, terminal_id: terminal.terminal_id }).set(\n +usedWeight1M,\n );\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n requestContext?: IRequestContext,\n) => callApi<T>(method, endpoint, params, undefined, requestContext);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n requestContext?: IRequestContext,\n) => callApi<T>(method, endpoint, params, credential, requestContext);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  import { scopeError, tokenBucket } from '@yuants/utils';
2
- import { getDefaultCredential, requestPrivate } from './client';
2
+ import { buildTokenBucketKey, createRequestContext, getDefaultCredential, requestPrivate, } from './client';
3
3
  /**
4
4
  * 查询账户信息(USER_DATA)
5
5
  *
@@ -13,8 +13,10 @@ export const getUnifiedAccountInfo = (credential) => {
13
13
  const endpoint = 'https://papi.binance.com/papi/v1/account';
14
14
  const url = new URL(endpoint);
15
15
  const weight = 20;
16
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
17
- return requestPrivate(credential, 'GET', endpoint);
16
+ const requestContext = createRequestContext();
17
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
18
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
19
+ return requestPrivate(credential, 'GET', endpoint, undefined, requestContext);
18
20
  };
19
21
  /**
20
22
  * 获取UM账户信息
@@ -29,8 +31,10 @@ export const getUnifiedUmAccount = (credential) => {
29
31
  const endpoint = 'https://papi.binance.com/papi/v1/um/account';
30
32
  const url = new URL(endpoint);
31
33
  const weight = 5;
32
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
33
- return requestPrivate(credential, 'GET', endpoint);
34
+ const requestContext = createRequestContext();
35
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
36
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
37
+ return requestPrivate(credential, 'GET', endpoint, undefined, requestContext);
34
38
  };
35
39
  /**
36
40
  * 查看当前全部UM挂单(USER_DATA)
@@ -45,16 +49,18 @@ export const getUnifiedUmOpenOrders = (credential, params) => {
45
49
  const endpoint = 'https://papi.binance.com/papi/v1/um/openOrders';
46
50
  const url = new URL(endpoint);
47
51
  const weight = (params === null || params === void 0 ? void 0 : params.symbol) ? 1 : 40;
52
+ const requestContext = createRequestContext();
53
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
48
54
  scopeError('BINANCE_API_RATE_LIMIT', {
49
55
  method: 'GET',
50
56
  endpoint,
51
57
  host: url.host,
52
58
  path: url.pathname,
53
- bucketId: url.host,
59
+ bucketId: bucketKey,
54
60
  weight,
55
61
  hasSymbol: !!(params === null || params === void 0 ? void 0 : params.symbol),
56
- }, () => tokenBucket(url.host).acquireSync(weight));
57
- return requestPrivate(credential, 'GET', endpoint, params);
62
+ }, () => tokenBucket(bucketKey).acquireSync(weight));
63
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
58
64
  };
59
65
  /**
60
66
  * 查询账户余额(USER_DATA)
@@ -69,8 +75,10 @@ export const getUnifiedAccountBalance = (credential, params) => {
69
75
  const endpoint = 'https://papi.binance.com/papi/v1/balance';
70
76
  const url = new URL(endpoint);
71
77
  const weight = 20;
72
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
73
- return requestPrivate(credential, 'GET', endpoint, params);
78
+ const requestContext = createRequestContext();
79
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
80
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
81
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
74
82
  };
75
83
  /**
76
84
  * 账户信息 (USER_DATA)
@@ -83,8 +91,10 @@ export const getSpotAccountInfo = (credential, params) => {
83
91
  const endpoint = 'https://api.binance.com/api/v3/account';
84
92
  const url = new URL(endpoint);
85
93
  const weight = 20;
86
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
87
- return requestPrivate(credential, 'GET', endpoint, params);
94
+ const requestContext = createRequestContext();
95
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
96
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
97
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
88
98
  };
89
99
  /**
90
100
  * Current open orders (USER_DATA)
@@ -97,16 +107,18 @@ export const getSpotOpenOrders = (credential, params) => {
97
107
  const endpoint = 'https://api.binance.com/api/v3/openOrders';
98
108
  const url = new URL(endpoint);
99
109
  const weight = (params === null || params === void 0 ? void 0 : params.symbol) ? 6 : 80;
110
+ const requestContext = createRequestContext();
111
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
100
112
  scopeError('BINANCE_API_RATE_LIMIT', {
101
113
  method: 'GET',
102
114
  endpoint,
103
115
  host: url.host,
104
116
  path: url.pathname,
105
- bucketId: url.host,
117
+ bucketId: bucketKey,
106
118
  weight,
107
119
  hasSymbol: !!(params === null || params === void 0 ? void 0 : params.symbol),
108
- }, () => tokenBucket(url.host).acquireSync(weight));
109
- return requestPrivate(credential, 'GET', endpoint, params);
120
+ }, () => tokenBucket(bucketKey).acquireSync(weight));
121
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
110
122
  };
111
123
  /**
112
124
  * New order (TRADE)
@@ -119,8 +131,10 @@ export const postSpotOrder = (credential, params) => {
119
131
  const endpoint = 'https://api.binance.com/api/v3/order';
120
132
  const url = new URL(endpoint);
121
133
  const weight = 1;
122
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
123
- return requestPrivate(credential, 'POST', endpoint, params);
134
+ const requestContext = createRequestContext();
135
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
136
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
137
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
124
138
  };
125
139
  /**
126
140
  * Cancel order (TRADE)
@@ -133,8 +147,10 @@ export const deleteSpotOrder = (credential, params) => {
133
147
  const endpoint = 'https://api.binance.com/api/v3/order';
134
148
  const url = new URL(endpoint);
135
149
  const weight = 1;
136
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
137
- return requestPrivate(credential, 'DELETE', endpoint, params);
150
+ const requestContext = createRequestContext();
151
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
152
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
153
+ return requestPrivate(credential, 'DELETE', endpoint, params, requestContext);
138
154
  };
139
155
  /**
140
156
  * 用户万向划转(USER_DATA)
@@ -151,8 +167,10 @@ export const postAssetTransfer = (credential, params) => {
151
167
  const endpoint = 'https://api.binance.com/sapi/v1/asset/transfer';
152
168
  const url = new URL(endpoint);
153
169
  const weight = 900;
154
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
155
- return requestPrivate(credential, 'POST', endpoint, params);
170
+ const requestContext = createRequestContext();
171
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
172
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
173
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
156
174
  };
157
175
  /**
158
176
  * 统一账户资金归集(TRADE)
@@ -169,8 +187,10 @@ export const postUnifiedAccountAutoCollection = (credential) => {
169
187
  const endpoint = 'https://papi.binance.com/papi/v1/auto-collection';
170
188
  const url = new URL(endpoint);
171
189
  const weight = 750;
172
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
173
- return requestPrivate(credential, 'POST', endpoint);
190
+ const requestContext = createRequestContext();
191
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
192
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
193
+ return requestPrivate(credential, 'POST', endpoint, undefined, requestContext);
174
194
  };
175
195
  /**
176
196
  * 获取充值地址(支持多网络)(USER_DATA)
@@ -185,8 +205,10 @@ export const getDepositAddress = (credential, params) => {
185
205
  const endpoint = 'https://api.binance.com/sapi/v1/capital/deposit/address';
186
206
  const url = new URL(endpoint);
187
207
  const weight = 10;
188
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
189
- return requestPrivate(credential, 'GET', endpoint, params);
208
+ const requestContext = createRequestContext();
209
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
210
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
211
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
190
212
  };
191
213
  /**
192
214
  * 查询子账户列表(适用主账户)
@@ -199,8 +221,10 @@ export const getSubAccountList = (credential, params) => {
199
221
  const endpoint = 'https://api.binance.com/sapi/v1/sub-account/list';
200
222
  const url = new URL(endpoint);
201
223
  const weight = 1;
202
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
203
- return requestPrivate(credential, 'GET', endpoint, params);
224
+ const requestContext = createRequestContext();
225
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
226
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
227
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
204
228
  };
205
229
  /**
206
230
  * 提币(USER_DATA)
@@ -213,8 +237,10 @@ export const postWithdraw = (credential, params) => {
213
237
  const endpoint = 'https://api.binance.com/sapi/v1/capital/withdraw/apply';
214
238
  const url = new URL(endpoint);
215
239
  const weight = 600;
216
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
217
- return requestPrivate(credential, 'POST', endpoint, params);
240
+ const requestContext = createRequestContext();
241
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
242
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
243
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
218
244
  };
219
245
  /**
220
246
  * 获取提币历史(支持多网络)(USER_DATA)
@@ -232,8 +258,10 @@ export const getWithdrawHistory = (credential, params) => {
232
258
  const endpoint = 'https://api.binance.com/sapi/v1/capital/withdraw/history';
233
259
  const url = new URL(endpoint);
234
260
  const weight = 1;
235
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
236
- return requestPrivate(credential, 'GET', endpoint, params);
261
+ const requestContext = createRequestContext();
262
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
263
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
264
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
237
265
  };
238
266
  /**
239
267
  * 获取充值历史(支持多网络)
@@ -246,8 +274,10 @@ export const getDepositHistory = (credential, params) => {
246
274
  const endpoint = 'https://api.binance.com/sapi/v1/capital/deposit/hisrec';
247
275
  const url = new URL(endpoint);
248
276
  const weight = 1;
249
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
250
- return requestPrivate(credential, 'GET', endpoint, params);
277
+ const requestContext = createRequestContext();
278
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
279
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
280
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
251
281
  };
252
282
  /**
253
283
  * UM下单(TRADE)
@@ -260,15 +290,19 @@ export const postUmOrder = (credential, params) => {
260
290
  const endpoint = 'https://papi.binance.com/papi/v1/um/order';
261
291
  const url = new URL(endpoint);
262
292
  const weight = 1;
263
- scopeError('BINANCE_UNIFIED_ORDER_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/unified/minute').acquireSync(weight));
264
- return requestPrivate(credential, 'POST', endpoint, params);
293
+ const requestContext = createRequestContext();
294
+ const bucketKey = buildTokenBucketKey('order/unified/minute', requestContext.ip);
295
+ scopeError('BINANCE_UNIFIED_ORDER_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
296
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
265
297
  };
266
298
  export const deleteUmOrder = (credential, params) => {
267
299
  const endpoint = 'https://papi.binance.com/papi/v1/um/order';
268
300
  const url = new URL(endpoint);
269
301
  const weight = 1;
270
- scopeError('BINANCE_UNIFIED_ORDER_API_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket('order/unified/minute').acquireSync(weight));
271
- return requestPrivate(credential, 'DELETE', endpoint, params);
302
+ const requestContext = createRequestContext();
303
+ const bucketKey = buildTokenBucketKey('order/unified/minute', requestContext.ip);
304
+ scopeError('BINANCE_UNIFIED_ORDER_API_RATE_LIMIT', { method: 'DELETE', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
305
+ return requestPrivate(credential, 'DELETE', endpoint, params, requestContext);
272
306
  };
273
307
  /**
274
308
  * 获取UM损益资金流水(USER_DATA)
@@ -281,8 +315,10 @@ export const getUMIncome = (credential, params) => {
281
315
  const endpoint = 'https://papi.binance.com/papi/v1/um/income';
282
316
  const url = new URL(endpoint);
283
317
  const weight = 30;
284
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
285
- return requestPrivate(credential, 'GET', endpoint, params);
318
+ const requestContext = createRequestContext();
319
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
320
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
321
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
286
322
  };
287
323
  /**
288
324
  * 获取账户损益资金流水(USER_DATA)
@@ -295,8 +331,10 @@ export const getAccountIncome = (credential, params) => {
295
331
  const endpoint = 'https://fapi.binance.com/fapi/v1/income';
296
332
  const url = new URL(endpoint);
297
333
  const weight = 30;
298
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
299
- return requestPrivate(credential, 'GET', endpoint, params);
334
+ const requestContext = createRequestContext();
335
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
336
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
337
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
300
338
  };
301
339
  /**
302
340
  * Cancel an Existing Order and Send a New Order (TRADE)
@@ -309,8 +347,10 @@ export const postSpotOrderCancelReplace = (credential, params) => {
309
347
  const endpoint = 'https://api.binance.com/api/v3/order/cancelReplace';
310
348
  const url = new URL(endpoint);
311
349
  const weight = 1;
312
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
313
- return requestPrivate(credential, 'POST', endpoint, params);
350
+ const requestContext = createRequestContext();
351
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
352
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
353
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
314
354
  };
315
355
  /**
316
356
  * Modify Order (TRADE)
@@ -323,8 +363,10 @@ export const putUmOrder = (credential, params) => {
323
363
  const endpoint = 'https://papi.binance.com/papi/v1/um/order';
324
364
  const url = new URL(endpoint);
325
365
  const weight = 1;
326
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'PUT', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
327
- return requestPrivate(credential, 'PUT', endpoint, params);
366
+ const requestContext = createRequestContext();
367
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
368
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'PUT', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
369
+ return requestPrivate(credential, 'PUT', endpoint, params, requestContext);
328
370
  };
329
371
  /**
330
372
  * 获取所有全仓杠杆交易对(MARKET_DATA)
@@ -338,8 +380,10 @@ export const getMarginAllPairs = (params) => {
338
380
  const endpoint = 'https://api.binance.com/sapi/v1/margin/allPairs';
339
381
  const url = new URL(endpoint);
340
382
  const weight = 1;
341
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
342
- return requestPrivate(credential, 'GET', endpoint, params);
383
+ const requestContext = createRequestContext();
384
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
385
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
386
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
343
387
  };
344
388
  /**
345
389
  * Get a future hourly interest rate
@@ -351,8 +395,10 @@ export const getMarginNextHourlyInterestRate = (params) => {
351
395
  const endpoint = 'https://api.binance.com/sapi/v1/margin/next-hourly-interest-rate';
352
396
  const url = new URL(endpoint);
353
397
  const weight = 1;
354
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
355
- return requestPrivate(credential, 'GET', endpoint, params);
398
+ const requestContext = createRequestContext();
399
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
400
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
401
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
356
402
  };
357
403
  /**
358
404
  * Query Margin Interest Rate History
@@ -364,8 +410,10 @@ export const getMarginInterestRateHistory = (params) => {
364
410
  const endpoint = 'https://api.binance.com/sapi/v1/margin/interestRateHistory';
365
411
  const url = new URL(endpoint);
366
412
  const weight = 1;
367
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
368
- return requestPrivate(credential, 'GET', endpoint, params);
413
+ const requestContext = createRequestContext();
414
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
415
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
416
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
369
417
  };
370
418
  /**
371
419
  *获取用户持仓
@@ -378,8 +426,10 @@ export const getUserAsset = (credential, params) => {
378
426
  const endpoint = 'https://api.binance.com/sapi/v3/asset/getUserAsset';
379
427
  const url = new URL(endpoint);
380
428
  const weight = 1;
381
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
382
- return requestPrivate(credential, 'POST', endpoint, params);
429
+ const requestContext = createRequestContext();
430
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
431
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
432
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
383
433
  };
384
434
  /**
385
435
  * 查询资金账户
@@ -392,8 +442,10 @@ export const getFundingAsset = (credential, params) => {
392
442
  const endpoint = 'https://api.binance.com/sapi/v1/asset/get-funding-asset';
393
443
  const url = new URL(endpoint);
394
444
  const weight = 1;
395
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
396
- return requestPrivate(credential, 'POST', endpoint, params);
445
+ const requestContext = createRequestContext();
446
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
447
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'POST', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
448
+ return requestPrivate(credential, 'POST', endpoint, params, requestContext);
397
449
  };
398
450
  /**
399
451
  * UM账户成交历史 (USER_DATA)
@@ -406,7 +458,9 @@ export const getUmAccountTradeList = (credential, params) => {
406
458
  const endpoint = 'https://papi.binance.com/papi/v1/um/userTrades';
407
459
  const url = new URL(endpoint);
408
460
  const weight = 5;
409
- scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: url.host, weight }, () => tokenBucket(url.host).acquireSync(weight));
410
- return requestPrivate(credential, 'GET', endpoint, params);
461
+ const requestContext = createRequestContext();
462
+ const bucketKey = buildTokenBucketKey(url.host, requestContext.ip);
463
+ scopeError('BINANCE_API_RATE_LIMIT', { method: 'GET', endpoint, host: url.host, path: url.pathname, bucketId: bucketKey, weight }, () => tokenBucket(bucketKey).acquireSync(weight));
464
+ return requestPrivate(credential, 'GET', endpoint, params, requestContext);
411
465
  };
412
466
  //# sourceMappingURL=private-api.js.map